Ejemplo n.º 1
0
    def _on_prov_refresh_click(self):
        """Populates provider dropdown with fresh list from config.yml"""

        providers = configmanager.read_config()['providers']
        self.provider_combo.clear()
        for provider in providers:
            self.provider_combo.addItem(provider['name'], provider)
Ejemplo n.º 2
0
    def _get_provider_input(self):
        """
        :returns: Returns selected provider from dropdown.
        :rtype: (dict, str, boolean)
        """

        providers = configmanager.read_config()['providers']
        providers_names = [provider['name'] for provider in providers]
        provider_name, ok = QInputDialog.getItem(self.iface.mainWindow(),
                                                 "Pelias Providers",
                                                 "Choose a provider",
                                                 providers_names, 0, False)
        return providers, provider_name, ok
    def __init__(self, parent=None):
        """
        :param parent: Parent window for modality.
        :type parent: QDialog
        """

        QDialog.__init__(self, parent)

        self.setupUi(self)

        # Temp storage for config dict
        self.temp_config = configmanager.read_config()

        self._build_ui()
        self._collapse_boxes()

        self.provider_add.clicked.connect(self._add_provider)
        self.provider_remove.clicked.connect(self._remove_provider)
Ejemplo n.º 4
0
    def show_main_dialog(self):
        """Initializes main Pelias dialog window."""

        # Only populate GUI if it's the first start of the plugin within the QGIS session
        # If not checked, GUI would be rebuilt every time!
        if self.first_start:
            self.first_start = False

            self.dlg = PeliasToolsDialog(
                self.iface,
                self.iface.mainWindow())  # setting parent enables modal view
            self.dlg.buttonBox.accepted.disconnect(self.dlg.accept)
            self.dlg.buttonBox.accepted.connect(self._run_main_dialog)

            providers = configmanager.read_config()['providers']
            for provider in providers:
                self.dlg.provider_combo.addItem(provider['name'], provider)

        self.dlg.show()
Ejemplo n.º 5
0
def get_provider_list():
    """Makes sure to use the last_used provider written in config.ini

    :returns: list of provider objects and provider names
    """

    providers = configmanager.read_config()['providers']
    last_used = CONFIG['provider'].get('last_used', providers[0])
    providers_names = [provider['name'] for provider in providers if provider['key'] not in (None, '') or provider['name'] == 'localhost']
    if len(providers_names) > 0:
        try:
            providers_names.remove(last_used)
            providers_names.insert(0, last_used)
        except:
            pass
    else:
        providers_names = ['No provider configured yet']

    return providers, providers_names
Ejemplo n.º 6
0
    def initAlgorithm(self,
                      configuration,
                      p_str=None,
                      Any=None,
                      *args,
                      **kwargs):

        providers = [
            provider['name']
            for provider in configmanager.read_config()['providers']
        ]

        self.addParameter(
            QgsProcessingParameterEnum(self.IN_PROVIDER, "Provider",
                                       providers))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name=self.IN_POINTS,
                description="Input Point layer",
                types=[QgsProcessing.TypeVectorPoint],
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_ID_FIELD,
                description="Input layer ID Field",
                parentLayerParameterName=self.IN_POINTS))

        self.addParameter(
            QgsProcessingParameterString(
                name=self.IN_COUNTRY,
                description="Restrict search to country",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_LAYERS,
                description="Filter by administrative type",
                optional=True,
                options=LAYERS,
                allowMultiple=True))

        self.addParameter(
            QgsProcessingParameterEnum(name=self.IN_SOURCES,
                                       description="Filter by data source",
                                       optional=True,
                                       options=SOURCES,
                                       allowMultiple=True))

        self.addParameter((QgsProcessingParameterNumber(
            name=self.IN_SIZE,
            description="Limit to number of features",
            defaultValue=5,
            minValue=1,
            maxValue=40)))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                name=self.OUT,
                description="Pelias Reverse Geocoding",
                createByDefault=False))
Ejemplo n.º 7
0
    def processAlgorithm(self, parameters, context, feedback):
        providers = configmanager.read_config()['providers']

        # Init client
        provider = providers[self.parameterAsEnum(parameters, self.IN_PROVIDER,
                                                  context)]
        in_source = self.parameterAsSource(parameters, self.IN_POINTS, context)
        in_id_field_name = self.parameterAsString(parameters, self.IN_ID_FIELD,
                                                  context)
        in_country = self.parameterAsString(parameters, self.IN_COUNTRY,
                                            context)
        in_layers = self.parameterAsEnums(parameters, self.IN_LAYERS, context)
        in_sources = self.parameterAsEnums(parameters, self.IN_SOURCES,
                                           context)
        in_size = self.parameterAsInt(parameters, self.IN_SIZE, context)

        # Get user specified ID field as object
        in_id_field = in_source.fields().field(in_id_field_name)

        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(lambda sleep_for: feedback.reportError(
            "OverQueryLimit: Wait for {} seconds".format(sleep_for)))

        params = dict()

        if in_country:
            params['boundary.country'] = in_country
        if in_layers:
            params['layers'] = convert.comma_list(
                [LAYERS[idx] for idx in in_layers])
        if in_sources:
            params['sources'] = convert.comma_list(
                [SOURCES[idx] for idx in in_sources])
        params['size'] = str(in_size)

        responsehandler = response_handler.ResponseHandler(in_id_field)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context,
                                               responsehandler.get_fields(),
                                               QgsWkbTypes.Point, self.crs_out)

        xformer = transform.transformToWGS(in_source.sourceCrs())
        for num, feat_in in enumerate(in_source.getFeatures()):
            if feedback.isCanceled():
                break
            x_point = xformer.transform(feat_in.geometry().asPoint())
            params['point.lon'] = x_point.x()
            params['point.lat'] = x_point.y()

            in_id_field_value = feat_in[in_id_field_name]
            try:
                response = clnt.request(
                    provider['endpoints'][self.ALGO_NAME_LIST[1]], params)
            except (exceptions.ApiError, exceptions.GenericServerError,
                    exceptions.InvalidKey) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    in_id_field_value, e.__class__.__name__, str(e))
                feedback.reportError(msg)
                logger.log(msg, 2)
                continue

            features_out = responsehandler.generate_out_features(
                response, in_id_field_value)
            for feat_out in features_out:
                sink.addFeature(feat_out)

            feedback.setProgress(int(100.0 / in_source.featureCount() * num))

        return {self.OUT: dest_id}
Ejemplo n.º 8
0
class PeliasReverseAlgo(QgsProcessingAlgorithm):
    # TODO: create base algorithm class common to all modules

    ALGO_NAME = 'pelias_reverse'
    ALGO_NAME_LIST = ALGO_NAME.split("_")

    IN_PROVIDER = "INPUT_PROVIDER"
    IN_POINTS = "INPUT_POINT_LAYER"
    IN_ID_FIELD = "INPUT_ID_FIELD"
    IN_COUNTRY = 'INPUT_COUNTRY'
    IN_LAYERS = 'INPUT_LAYERS'
    IN_SOURCES = 'INPUT_SOURCES'
    IN_SIZE = 'INPUT_SIZE'
    OUT = 'OUTPUT'

    # Save some important references
    crs_out = QgsCoordinateReferenceSystem(4326)
    providers = configmanager.read_config()['providers']

    # difference = None

    def initAlgorithm(self,
                      configuration,
                      p_str=None,
                      Any=None,
                      *args,
                      **kwargs):

        providers = [provider['name'] for provider in self.providers]

        self.addParameter(
            QgsProcessingParameterEnum(self.IN_PROVIDER, "Provider",
                                       providers))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name=self.IN_POINTS,
                description="Input Point layer",
                types=[QgsProcessing.TypeVectorPoint],
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_ID_FIELD,
                description="Input layer ID Field",
                parentLayerParameterName=self.IN_POINTS))

        self.addParameter(
            QgsProcessingParameterString(
                name=self.IN_COUNTRY,
                description="Restrict search to country",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_LAYERS,
                description="Filter by administrative type",
                optional=True,
                options=LAYERS,
                allowMultiple=True))

        self.addParameter(
            QgsProcessingParameterEnum(name=self.IN_SOURCES,
                                       description="Filter by data source",
                                       optional=True,
                                       options=SOURCES,
                                       allowMultiple=True))

        self.addParameter((QgsProcessingParameterNumber(
            name=self.IN_SIZE,
            description="Limit to number of features",
            defaultValue=5,
            minValue=1,
            maxValue=40)))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                name=self.OUT,
                description="Pelias Reverse Geocoding",
                createByDefault=False))

    def name(self):
        return self.ALGO_NAME

    def shortHelpString(self):
        """Displays the sidebar help in the algorithm window"""

        file = os.path.join(HELP_DIR, 'algorithm_freetext.help')
        with open(file) as helpf:
            msg = helpf.read()

        return msg

    def helpUrl(self):
        """will be connected to the Help button in the Algorithm window"""
        return __help__

    def displayName(self):
        return " ".join(map(lambda x: x.capitalize(), self.ALGO_NAME_LIST))

    def icon(self):
        return QIcon(RESOURCE_PREFIX + 'icon_locate.png')

    def createInstance(self):
        return PeliasReverseAlgo()

    def processAlgorithm(self, parameters, context, feedback):
        # Init client
        provider = self.providers[self.parameterAsEnum(parameters,
                                                       self.IN_PROVIDER,
                                                       context)]
        in_source = self.parameterAsSource(parameters, self.IN_POINTS, context)
        in_id_field_name = self.parameterAsString(parameters, self.IN_ID_FIELD,
                                                  context)
        in_country = self.parameterAsString(parameters, self.IN_COUNTRY,
                                            context)
        in_layers = self.parameterAsEnums(parameters, self.IN_LAYERS, context)
        in_sources = self.parameterAsEnums(parameters, self.IN_SOURCES,
                                           context)
        in_size = self.parameterAsInt(parameters, self.IN_SIZE, context)

        # Get user specified ID field as object
        in_id_field = in_source.fields().field(in_id_field_name)

        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(lambda sleep_for: feedback.reportError(
            "OverQueryLimit: Wait for {} seconds".format(sleep_for)))

        params = dict()

        if in_country:
            params['boundary.country'] = in_country
        if in_layers:
            params['layers'] = convert.comma_list(
                [LAYERS[idx] for idx in in_layers])
        if in_sources:
            params['sources'] = convert.comma_list(
                [SOURCES[idx] for idx in in_sources])
        params['size'] = str(in_size)

        responsehandler = response_handler.ResponseHandler(in_id_field)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context,
                                               responsehandler.get_fields(),
                                               QgsWkbTypes.Point, self.crs_out)

        xformer = transform.transformToWGS(in_source.sourceCrs())
        for num, feat_in in enumerate(in_source.getFeatures()):
            x_point = xformer.transform(feat_in.geometry().asPoint())
            params['point.lon'] = x_point.x()
            params['point.lat'] = x_point.y()

            in_id_field_value = feat_in[in_id_field_name]
            try:
                response = clnt.request(
                    provider['endpoints'][self.ALGO_NAME_LIST[1]], params)
            except (exceptions.ApiError, exceptions.GenericServerError,
                    exceptions.InvalidKey) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    in_id_field_value, e.__class__.__name__, str(e))
                feedback.reportError(msg)
                logger.log(msg, 2)
                continue

            features_out = responsehandler.generate_out_features(
                response, in_id_field_value)
            for feat_out in features_out:
                sink.addFeature(feat_out)

            feedback.setProgress(int(100.0 / in_source.featureCount() * num))

        return {self.OUT: dest_id}
Ejemplo n.º 9
0
    def initAlgorithm(self,
                      configuration,
                      p_str=None,
                      Any=None,
                      *args,
                      **kwargs):

        providers = [
            provider['name']
            for provider in configmanager.read_config()['providers']
        ]

        params_dependent = []
        self.addParameter(
            QgsProcessingParameterEnum(self.IN_PROVIDER, "Provider",
                                       providers))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name=self.IN_POINTS,
                description="Input Point layer",
                types=[QgsProcessing.TypeVector],
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_ID_FIELD,
                description="Input layer ID Field",
                parentLayerParameterName=self.IN_POINTS))

        self.addParameter(
            QgsProcessingParameterField(
                self.IN_TEXT_FIELD,
                "Input address field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String))

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_FOCUS,
                description="Focus point for search",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterExtent(
                name=self.IN_RECTANGLE,
                description="Restrict search to rectangle area",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_CIRCLE,
                description="Restrict search to circular area",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterNumber(name=self.IN_CIRCLE_RADIUS,
                                         description="Radius of circular area",
                                         minValue=1,
                                         maxValue=1000,
                                         optional=True))

        self.addParameter(
            QgsProcessingParameterString(
                name=self.IN_COUNTRY,
                description="Restrict search to country",
                optional=True,
                # toolTip="ISO 3166-1 alpha-3 country codes, e.g. GBR, DEU, USA.."
            ))

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_LAYERS,
                description="Filter by administrative type",
                optional=True,
                options=LAYERS,
                allowMultiple=True))

        self.addParameter(
            QgsProcessingParameterEnum(name=self.IN_SOURCES,
                                       description="Filter by data source",
                                       optional=True,
                                       options=SOURCES,
                                       allowMultiple=True))

        self.addParameter((QgsProcessingParameterNumber(
            name=self.IN_SIZE,
            description="Limit to number of features",
            defaultValue=5,
            minValue=1,
            maxValue=40)))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                name=self.OUT,
                description="Pelias Search Geocoding",
                createByDefault=False))
Ejemplo n.º 10
0
    def processAlgorithm(self, parameters, context, feedback):
        providers = configmanager.read_config()['providers']
        # Init client
        provider = providers[self.parameterAsEnum(parameters, self.IN_PROVIDER,
                                                  context)]
        in_source = self.parameterAsSource(parameters, self.IN_POINTS, context)
        in_id_field_name = self.parameterAsString(parameters, self.IN_ID_FIELD,
                                                  context)
        in_text_field_name = self.parameterAsString(parameters,
                                                    self.IN_TEXT_FIELD,
                                                    context)
        in_focus = self.parameterAsPoint(parameters, self.IN_FOCUS, context,
                                         self.crs_out)
        in_rectangle = self.parameterAsExtent(parameters, self.IN_RECTANGLE,
                                              context, self.crs_out)
        in_circle = self.parameterAsPoint(parameters, self.IN_CIRCLE, context,
                                          self.crs_out)
        in_circle_radius = self.parameterAsInt(parameters,
                                               self.IN_CIRCLE_RADIUS, context)
        in_country = self.parameterAsString(parameters, self.IN_COUNTRY,
                                            context)
        in_layers = self.parameterAsEnums(parameters, self.IN_LAYERS, context)
        in_sources = self.parameterAsEnums(parameters, self.IN_SOURCES,
                                           context)
        in_size = self.parameterAsInt(parameters, self.IN_SIZE, context)

        # Get user specified ID field as object
        in_id_field = in_source.fields().field(in_id_field_name)

        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(lambda sleep_for: feedback.reportError(
            "OverQueryLimit: Wait for {} seconds".format(sleep_for)))

        params = dict()

        if in_focus.x() and in_focus.y():
            params['focus.lon'], params['focus.lat'] = [
                convert.format_float(coord) for coord in in_focus
            ]
        if not in_rectangle.isNull():
            (params['boundary.rect.min_lon'], params['boundary.rect.min_lat'],
             params['boundary.rect.max_lon'],
             params['boundary.rect.max_lat']) = [
                 convert.format_float(coord)
                 for coord in in_rectangle.toRectF().getCoords()
             ]
        if in_circle.x() and in_circle.y():
            (params['boundary.circle.lon'], params['boundary.circle.lat']) = [
                convert.format_float(coord) for coord in in_circle
            ]
            params['boundary.circle.radius'] = str(in_circle_radius)
        if in_country:
            params['boundary.country'] = in_country
        if in_layers:
            params['layers'] = convert.comma_list(
                [LAYERS[idx] for idx in in_layers])
        if in_sources:
            params['sources'] = convert.comma_list(
                [SOURCES[idx] for idx in in_sources])
        params['size'] = str(in_size)

        responsehandler = response_handler.ResponseHandler(in_id_field)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context,
                                               responsehandler.get_fields(),
                                               QgsWkbTypes.Point, self.crs_out)

        for num, feat_in in enumerate(in_source.getFeatures()):
            if feedback.isCanceled():
                break

            params_feat = dict()
            if in_text_field_name and feat_in[in_text_field_name]:
                params_feat['text'] = feat_in[in_text_field_name]
            in_id_field_value = feat_in[in_id_field_name]

            try:
                response = clnt.request(
                    provider['endpoints'][self.ALGO_NAME_LIST[1]], {
                        **params,
                        **params_feat
                    })
            except (exceptions.ApiError, exceptions.GenericServerError,
                    exceptions.InvalidKey) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    in_id_field_value, e.__class__.__name__, str(e))
                feedback.reportError(msg)
                logger.log(msg, 2)
                continue

            features_out = responsehandler.generate_out_features(
                response, in_id_field_value)
            for feat_out in features_out:
                sink.addFeature(feat_out)

            feedback.setProgress(int(100.0 / in_source.featureCount() * num))

        return {self.OUT: dest_id}
Ejemplo n.º 11
0
    def initAlgorithm(self, configuration, p_str=None, Any=None, *args, **kwargs):

        providers = [provider['name'] for provider in configmanager.read_config()['providers']]

        self.addParameter(
            QgsProcessingParameterEnum(
                self.IN_PROVIDER,
                "Provider",
                providers
            )
        )

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name=self.IN_POINTS,
                description="Input Point layer",
                types=[QgsProcessing.TypeVector],
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_ID_FIELD,
                description="Input layer ID Field",
                parentLayerParameterName=self.IN_POINTS
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                self.IN_ADDR_FIELD,
                "Address field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_NEIGH_FIELD,
                description="Neighbourhood field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_BOROUGH_FIELD,
                description="Borough field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_LOCALITY_FIELD,
                description="Locality field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_COUNTY_FIELD,
                description="County field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional= True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_REGION_FIELD,
                description="Region field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_POSTAL_FIELD,
                description="Postal code field",
                parentLayerParameterName=self.IN_POINTS,
                optional=True
            )
        )

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_COUNTRY_FIELD,
                description="Country field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True
            )
        )

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_FOCUS,
                description="Focus point for search",
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterExtent(
                name=self.IN_RECTANGLE,
                description="Restrict search to rectangle area",
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_CIRCLE,
                description="Restrict search to circular area",
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                name=self.IN_CIRCLE_RADIUS,
                description="Radius of circular area",
                minValue= 1,
                maxValue= 1000,
                optional= True
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                name=self.IN_COUNTRY,
                description="Restrict search to country",
                optional=True,
            )
        )

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_LAYERS,
                description="Filter by administrative type",
                optional=True,
                options=LAYERS,
                allowMultiple=True
            )
        )

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_SOURCES,
                description="Filter by data source",
                optional=True,
                options=SOURCES,
                allowMultiple=True
            )
        )

        self.addParameter((
            QgsProcessingParameterNumber(
                name=self.IN_SIZE,
                description="Limit to number of features",
                defaultValue=5,
                minValue=1,
                maxValue=40
            )
        ))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                name=self.OUT,
                description="Pelias Structured Geocoding",
                createByDefault=False
            )
        )
Ejemplo n.º 12
0
class PeliasStrucSearchAlgo(QgsProcessingAlgorithm):
    # TODO: create base algorithm class common to all modules

    ALGO_NAME = 'pelias_search_structured'
    ALGO_NAME_LIST = ALGO_NAME.split("_")

    IN_PROVIDER = "INPUT_PROVIDER"
    IN_POINTS = "INPUT_POINT_LAYER"
    IN_ID_FIELD = "INPUT_ID_FIELD"
    IN_ADDR_FIELD = "INPUT_ADDR_FIELD"
    IN_NEIGH_FIELD = "INPUT_NEIGHB_FIELD"
    IN_BOROUGH_FIELD = "INPUT_BOROUGH_FIELD"
    IN_LOCALITY_FIELD = "INPUT_LOCALITY_FIELD"
    IN_COUNTY_FIELD = "INPUT_COUNTY_FIELD"
    IN_REGION_FIELD = "INPUT_REGION_FIELD"
    IN_POSTAL_FIELD = "INPUT_POSTAL_FIELD"
    IN_COUNTRY_FIELD = "INPUT_COUNTRY_FIELD"
    IN_FOCUS = 'INPUT_FOCUS'
    IN_RECTANGLE = 'INPUT_RECTANGLE'
    IN_CIRCLE = 'INPUT_CIRCLE'
    IN_CIRCLE_RADIUS = 'INPUT_CIRCLE_RADIUS'
    IN_COUNTRY = 'INPUT_COUNTRY'
    IN_LAYERS = 'INPUT_LAYERS'
    IN_SOURCES = 'INPUT_SOURCES'
    IN_SIZE = 'INPUT_SIZE'
    OUT = 'OUTPUT'

    # Save some important references
    crs_out = QgsCoordinateReferenceSystem(4326)
    providers = configmanager.read_config()['providers']

    def initAlgorithm(self,
                      configuration,
                      p_str=None,
                      Any=None,
                      *args,
                      **kwargs):

        providers = [provider['name'] for provider in self.providers]

        self.addParameter(
            QgsProcessingParameterEnum(self.IN_PROVIDER, "Provider",
                                       providers))

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                name=self.IN_POINTS,
                description="Input Point layer",
                types=[QgsProcessing.TypeVector],
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_ID_FIELD,
                description="Input layer ID Field",
                parentLayerParameterName=self.IN_POINTS))

        self.addParameter(
            QgsProcessingParameterField(
                self.IN_ADDR_FIELD,
                "Address field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_NEIGH_FIELD,
                description="Neighbourhood field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_BOROUGH_FIELD,
                description="Borough field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_LOCALITY_FIELD,
                description="Locality field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_COUNTY_FIELD,
                description="County field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_REGION_FIELD,
                description="Region field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_POSTAL_FIELD,
                description="Postal code field",
                parentLayerParameterName=self.IN_POINTS,
                optional=True))

        self.addParameter(
            QgsProcessingParameterField(
                name=self.IN_COUNTRY_FIELD,
                description="Country field",
                parentLayerParameterName=self.IN_POINTS,
                type=QgsProcessingParameterField.String,
                optional=True))

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_FOCUS,
                description="Focus point for search",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterExtent(
                name=self.IN_RECTANGLE,
                description="Restrict search to rectangle area",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterPoint(
                name=self.IN_CIRCLE,
                description="Restrict search to circular area",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterNumber(name=self.IN_CIRCLE_RADIUS,
                                         description="Radius of circular area",
                                         minValue=1,
                                         maxValue=1000,
                                         optional=True))

        self.addParameter(
            QgsProcessingParameterString(
                name=self.IN_COUNTRY,
                description="Restrict search to country",
                optional=True,
            ))

        self.addParameter(
            QgsProcessingParameterEnum(
                name=self.IN_LAYERS,
                description="Filter by administrative type",
                optional=True,
                options=LAYERS,
                allowMultiple=True))

        self.addParameter(
            QgsProcessingParameterEnum(name=self.IN_SOURCES,
                                       description="Filter by data source",
                                       optional=True,
                                       options=SOURCES,
                                       allowMultiple=True))

        self.addParameter((QgsProcessingParameterNumber(
            name=self.IN_SIZE,
            description="Limit to number of features",
            defaultValue=5,
            minValue=1,
            maxValue=40)))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                name=self.OUT,
                description="Pelias Structured Geocoding",
                createByDefault=False))

    def name(self):
        return self.ALGO_NAME

    def shortHelpString(self):
        """Displays the sidebar help in the algorithm window"""

        file = os.path.join(HELP_DIR, 'algorithm_freetext.help')
        with open(file) as helpf:
            msg = helpf.read()

        return msg

    def helpUrl(self):
        """will be connected to the Help button in the Algorithm window"""
        return __help__

    def displayName(self):
        return " ".join(map(lambda x: x.capitalize(), self.ALGO_NAME_LIST))

    def icon(self):
        return QIcon(RESOURCE_PREFIX + 'icon_locate.png')

    def createInstance(self):
        return PeliasStrucSearchAlgo()

    def processAlgorithm(self, parameters, context, feedback):
        # Init client
        provider = self.providers[self.parameterAsEnum(parameters,
                                                       self.IN_PROVIDER,
                                                       context)]
        in_source = self.parameterAsSource(parameters, self.IN_POINTS, context)
        in_id_field_name = self.parameterAsString(parameters, self.IN_ID_FIELD,
                                                  context)
        in_add_name = self.parameterAsString(parameters, self.IN_ADDR_FIELD,
                                             context)
        in_neigh_name = self.parameterAsString(parameters, self.IN_NEIGH_FIELD,
                                               context)
        in_borough_name = self.parameterAsString(parameters,
                                                 self.IN_BOROUGH_FIELD,
                                                 context)
        in_locality_name = self.parameterAsString(parameters,
                                                  self.IN_LOCALITY_FIELD,
                                                  context)
        in_county_name = self.parameterAsString(parameters,
                                                self.IN_COUNTY_FIELD, context)
        in_region_name = self.parameterAsString(parameters,
                                                self.IN_REGION_FIELD, context)
        in_postal_name = self.parameterAsString(parameters,
                                                self.IN_POSTAL_FIELD, context)
        in_country_name = self.parameterAsString(parameters,
                                                 self.IN_COUNTRY_FIELD,
                                                 context)
        in_focus = self.parameterAsPoint(parameters, self.IN_FOCUS, context,
                                         self.crs_out)
        in_rectangle = self.parameterAsExtent(parameters, self.IN_RECTANGLE,
                                              context, self.crs_out)
        in_circle = self.parameterAsPoint(parameters, self.IN_CIRCLE, context,
                                          self.crs_out)
        in_circle_radius = self.parameterAsInt(parameters,
                                               self.IN_CIRCLE_RADIUS, context)
        in_country = self.parameterAsString(parameters, self.IN_COUNTRY,
                                            context)
        in_layers = self.parameterAsEnums(parameters, self.IN_LAYERS, context)
        in_sources = self.parameterAsEnums(parameters, self.IN_SOURCES,
                                           context)
        in_size = self.parameterAsInt(parameters, self.IN_SIZE, context)

        # Get user specified ID field as object
        in_id_field = in_source.fields().field(in_id_field_name)

        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(lambda sleep_for: feedback.reportError(
            "OverQueryLimit: Wait for {} seconds".format(sleep_for)))

        params = dict()

        if in_focus.x() and in_focus.y():
            params['focus.lon'], params['focus.lat'] = [
                convert.format_float(coord) for coord in in_focus
            ]
        if not in_rectangle.isNull():
            (params['boundary.rect.min_lon'], params['boundary.rect.min_lat'],
             params['boundary.rect.max_lon'],
             params['boundary.rect.max_lat']) = [
                 convert.format_float(coord)
                 for coord in in_rectangle.toRectF().getCoords()
             ]
        if in_circle.x() and in_circle.y():
            (params['boundary.circle.lon'], params['boundary.circle.lat']) = [
                convert.format_float(coord) for coord in in_circle
            ]
            params['boundary.circle.radius'] = str(in_circle_radius)
        if in_country:
            params['boundary.country'] = in_country
        if in_layers:
            params['layers'] = convert.comma_list(
                [LAYERS[idx] for idx in in_layers])
        if in_sources:
            params['sources'] = convert.comma_list(
                [SOURCES[idx] for idx in in_sources])
        params['size'] = str(in_size)

        responsehandler = response_handler.ResponseHandler(in_id_field)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context,
                                               responsehandler.get_fields(),
                                               QgsWkbTypes.Point, self.crs_out)

        for num, feat_in in enumerate(in_source.getFeatures()):
            params_feat = dict()
            if in_add_name and feat_in[in_add_name]:
                params_feat['address'] = feat_in[in_add_name]
            if in_neigh_name and feat_in[in_neigh_name]:
                params_feat['neighbourhood'] = feat_in[in_neigh_name]
            if in_borough_name and feat_in[in_borough_name]:
                params_feat['borough'] = feat_in[in_borough_name]
            if in_locality_name and feat_in[in_locality_name]:
                params_feat['locality'] = feat_in[in_locality_name]
            if in_county_name and feat_in[in_county_name]:
                params_feat['county'] = feat_in[in_county_name]
            if in_region_name and feat_in[in_region_name]:
                params_feat['region'] = feat_in[in_region_name]
            if in_postal_name and feat_in[in_postal_name]:
                params_feat['postalcode'] = feat_in[in_postal_name]
            if in_country_name and feat_in[in_country_name]:
                params_feat['country'] = feat_in[in_country_name]

            in_id_field_value = feat_in[in_id_field_name]

            try:
                response = clnt.request(
                    provider['endpoints'][self.ALGO_NAME_LIST[2]], {
                        **params,
                        **params_feat
                    })
            except (exceptions.ApiError, exceptions.GenericServerError,
                    exceptions.InvalidKey) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    in_id_field_value, e.__class__.__name__, str(e))
                feedback.reportError(msg)
                logger.log(msg, 2)
                continue

            features_out = responsehandler.generate_out_features(
                response, in_id_field_value)
            for feat_out in features_out:
                sink.addFeature(feat_out)

            feedback.setProgress(int(100.0 / in_source.featureCount() * num))

        return {self.OUT: dest_id}