# -*- coding: utf-8 -*-
"""
/***************************************************************************
 Tieosoitetyökalu
                                 A QGIS plugin
 Tieosoitetyökalu
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-07-19
        git sha              : $Format:%H$
        copyright            : (C) 2022 Finnish Transport Infrastructure Agency
        email                : paikkatieto@vayla.fi
 ***************************************************************************/
"""

"""
/*

* Copyright 2022 Finnish Transport Infrastructure Agency
*

* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/2020-03/EUPL-1.2%20EN.txt
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and limitations under the Licence.
*/
"""


import logging
import os.path
from pathlib import Path
from typing import Iterable

formatter = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
rootPath = Path(__file__).parent
logPath = Path.joinpath(rootPath, 'logs')
logPath.mkdir(parents=True, exist_ok=True)
logging.basicConfig(filename=Path.joinpath(logPath, 'log.txt'), level=logging.DEBUG, format=formatter)

import json

import requests
from qgis.core import (Qgis, QgsCoordinateReferenceSystem, QgsPointXY,
                       QgsProject, QgsRectangle)
from qgis.gui import QgsMapToolEmitPoint
from qgis.PyQt.QtCore import QCoreApplication, QSettings, QTranslator
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QLineEdit, QMenu, QToolButton
from requests.adapters import HTTPAdapter, Retry

from .CustomExceptions.VkmApiException import VkmApiException
from .CustomExceptions.VkmRequestException import VkmRequestException
from .dialogs.Ajoradat_dialog import Ajoradat_dialog
from .dialogs.DeleteLayer_dialog import DeleteLayer_dialog
from .dialogs.PopUp_dialog import PopUp_dialog
from .dialogs.SearchForm_dialog import SearchForm_dialog
#Import modules
from .dialogs.ShowCoordinates_dialog import ShowCoordinates_dialog
from .LayerHandler import LayerHandler
# Initialize Qt resources from file resources.py
from .resources import *
from .RoadName import RoadName

# Import the code for the dialog



class Osoitetyokalu:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'Osoitetyokalu_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Tieosoitetyökalu')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

        self.my_crs = QgsCoordinateReferenceSystem.fromEpsgId(3067)

        self.LayerHandler = LayerHandler()

        self.RoadName = RoadName()

        QgsProject.instance().layersWillBeRemoved.connect(self.remove_annotations_from_layers)


    def remove_annotations_from_layers(self, layers:Iterable[str]):
        """Removes an annotatiot of a layer about to be deleted.

        Args:
            layers (Iterable[str]): Layer IDs of layers about to be deleted.
        """
        manager = QgsProject.instance().annotationManager()
        for annotation in manager.annotations():
            annotation_layer = annotation.mapLayer()
            if annotation_layer is not None:
                for layer_id in layers:
                    if annotation_layer.id() == layer_id:
                        manager.removeAnnotation(annotation)
                        break


    # noinspection PyMethodMayBeStatic
    @staticmethod
    def tr(message, disambiguation="", n=-1) -> str:
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Osoitetyokalu', message, disambiguation, n)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None,
        add_to_popupMenu=False,
        default_action=False):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        if add_to_popupMenu is True:
            # Adds action to a dropdown menu
            self.popupMenu.addAction(action)

        if default_action is True:
            self.toolButton.setDefaultAction(action)

        self.actions.append(action)

        return action


    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        self.popupMenu = QMenu(self.iface.mainWindow())
        self.toolButton = QToolButton()
        icon_path_1 = ':/plugins/osoitetyokalu/tool_icons/tool_1_v2.png'
        icon_path_2 = ':/plugins/osoitetyokalu/tool_icons/tool_2.png'
        icon_path_3 = ':/plugins/osoitetyokalu/tool_icons/tool_3.png'
        icon_path_4 = ':/plugins/osoitetyokalu/tool_icons/tool_4.png'
        icon_path_5 = ':/plugins/osoitetyokalu/tool_icons/tool_5.png'
        icon_path_6 = ':/plugins/osoitetyokalu/tool_icons/tool_6.png'

        self.add_action(
            icon_path_1,
            text=self.tr(u'1. Tieosoite'),
            callback=self.road_address,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True,
            default_action=False)
#
        self.add_action(
            icon_path_2,
            text=self.tr(u'2. Hakutyökalu'),
            callback=self.popup,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True)
#
        self.add_action(
            icon_path_3,
            text=self.tr(u'3. Tieosa'),
            callback=self.road_part,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True)
#
        self.add_action(
            icon_path_4,
            text=self.tr(u'4. Tieosoite (Alku- ja loppupiste)'),
            callback=self.two_points,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True)
#
        self.add_action(
            icon_path_5,
            text=self.tr(u'5. Kohdistustyökalu'),
            callback=self.search_form,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True)
#
        self.add_action(
            icon_path_6,
            text=self.tr(u'6. Poistotyökalu'),
            callback=self.delete_tool,
            parent=self.iface.mainWindow(),
            add_to_toolbar=False,
            add_to_popupMenu=True)

        self.toolButton.setMenu(self.popupMenu)
        self.toolButton.setDefaultAction(self.actions[0])
        self.toolButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.toolWidget = self.iface.addToolBarWidget(self.toolButton)
        self.actions.append(self.toolWidget)

        # will be set False in run()
        self.first_start = True


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&Tieosoitetyökalu'),
                action)
            self.iface.removeToolBarIcon(action)


    def road_address(self):
        """Retrieves the road address from VKM-api using the coordinates that come from a click on canvas and displays it as a annotation."""
        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[0])
        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
        dlg = ShowCoordinates_dialog()
        dlg.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)

        self.LayerHandler.init_tool1()
        self.iface.mapCanvas().refresh()

        def display_point(pointTool):
            """ Uses pointTool to return XY-coordinates from each click and passes them as search parameters onto GET request to VKM-API.
                Draws an annotation on the canvas with the road address by using the road's coordinates from VKM-API output, that are closest to the clicked coordinates.
                Clicked coordinates and returned road address are also displayed on a dialog.

            Args:
                pointTool (QgsMapToolEmitPoint()): Tool that returns coordinates from clicking on the canvas that it's connected to.
            """

            try:
                #click on canvas returns coordinates
                point_x = str(pointTool.x())
                point_y = str(pointTool.y())
                dlg.CoordLineEdit.setText(f'{point_x}, {point_y}')

                #get new coordinates and address from VKM
                vkm_url='https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'

                road_address, point_x, point_y, _, _, _, _ = self.vkm_request_road_address(vkm_url=vkm_url, point_x=point_x, point_y=point_y)

                dlg.AddrLineEdit.setText(road_address)

                #adding an annotation with road address to the coordinates
                self.LayerHandler.add_annotation('1', road_address, point_x, point_y)

            except AttributeError:
                self.error_popup(self.tr(u'Pistettä ei ole asetettu.'))
            except VkmApiException as e:
                self.error_popup(e)
            except VkmRequestException as e:
                self.error_popup(e)


        #CRS when the ShowCoordinates-tool is opened
        QgsProject.instance().setCrs(self.my_crs)

        #connecting QGIS-canvas to QgsMapToolEmitPoint()
        canvas = self.iface.mapCanvas()
        pointTool = QgsMapToolEmitPoint(canvas)
        pointTool.canvasClicked.connect(display_point)
        canvas.setMapTool(pointTool)

        # show the dialog
        dlg.show()
        # Run the dialog event loop
        result = dlg.exec_()
        # See if OK was pressed
        if result:

            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            pass


    def popup(self):
        """Retrieves all possible address information from VKM-API using the coordinates that come from a click on canvas and displays it on a dialog(Pop-Up window).
        """

        QgsProject.instance().setCrs(self.my_crs)
        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[1])
        if self.first_start == True:
            self.first_start = False
        dlg = PopUp_dialog()
        dlg.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)

        self.LayerHandler.init_tool2()
        self.iface.mapCanvas().refresh()

        def display_popup(pointTool):
            """ Uses pointTool to return XY-coordinates from each click and passes them as search parameters onto GET request to VKM-API.
                Draws a point on the canvas with the road address by using the road's coordinates from VKM-API output, that are closest to the clicked coordinates.
                Clicked coordinates and returned road address are also displayed on a dialog.

            Args:
                pointTool (QgsMapToolEmitPoint()): Tool that returns coordinates from clicking on the canvas that it's connected to.
            """

            lineEdits = dlg.findChildren(QLineEdit)
            for line in lineEdits:
                if line.text():
                    line.clear()

            try:
                point_x = str(pointTool.x())
                point_y = str(pointTool.y())
                #get new coordinates and address from VKM
                vkm_url = 'https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'
                request_url = f'{vkm_url}muunna?x={point_x}&y={point_y}&palautusarvot=1,2,3,4,5,6&vaylan_luonne=0'

                s = requests.Session()
                retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
                s.mount('http://', HTTPAdapter(max_retries=retries))
                response = s.get(request_url, verify=False)
                s.close()

                if response.status_code != 200:
                    raise VkmApiException(request_url)

                vkm_data = json.loads(response.content)
                for vkm_feature in vkm_data['features']:
                    if 'virheet' in vkm_feature['properties']:
                        error_msg = vkm_feature['properties']['virheet']
                        raise VkmRequestException(error_msg)

                    road_address = self.set_popup_text(dlg, vkm_feature)

                    point_x = vkm_feature['properties']['x']
                    point_y = vkm_feature['properties']['y']
                    self.LayerHandler.add_point_feature('2', road_address, point_x, point_y)

            except AttributeError:
                self.error_popup(self.tr(u'Pistettä ei ole asetettu.'))
            except VkmApiException as e:
                self.error_popup(e)
            except VkmRequestException as e:
                self.error_popup(e)


        canvas = self.iface.mapCanvas()
        pointTool = QgsMapToolEmitPoint(canvas)
        pointTool.canvasClicked.connect(display_popup)
        canvas.setMapTool(pointTool)

        dlg.show()
        dlg.exec_()


    def road_part(self):
        """Highlights a clicked road part's roadway(s), draws its ending and starting points and displays their road addresses on an annotation."""

        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[2])

        if self.first_start == True:
            self.first_start = False
        dlg = ShowCoordinates_dialog()
        dlg.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)

        self.LayerHandler.init_tool3()
        self.iface.mapCanvas().refresh()

        def display_road_part(pointTool):
            """ Uses pointTool to return XY-coordinates from each click and passes them as search parameters onto GET request to VKM-API.

                Draws an annotation on the canvas with the road part's starting and ending road addresses, draws their points and hightlights the part's roadways using different color poylines.
                Drawn geometry uses the road part's coordinates and linestring geometry coordinates from VKM-API output.
                Annotation uses the road part's halfway coordiantes which come from a seperate coordinate search.

                Clicked coordinates and returned road address are also displayed on a dialog.

            Args:
                pointTool (QgsMapToolEmitPoint()): Tool that returns coordinates from clicking on the canvas that it's connected to.
            """

            try:
                #click on canvas returns coordinates
                point_x = str(pointTool.x())
                point_y = str(pointTool.y())
                dlg.CoordLineEdit.setText(f'{point_x}, {point_y}')

                #get new coordinates and address from VKM
                vkm_url='https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'

                road_address, point_x, point_y, tie, _, osa, _ = self.vkm_request_road_address(vkm_url=vkm_url, point_x=point_x, point_y=point_y)

                dlg.AddrLineEdit.setText(road_address)

                polyline_dict, road_part_length, starting_point, ending_point, _= self.vkm_request_road_part_geometry(vkm_url, tie, osa)

                for ajorata, coordinates in polyline_dict.items():
                        ending_road_address_split = road_address.split('/')
                        ending_road_address_split[3] = str(road_part_length)
                        ending_road_address_split[1] = ending_point[2]
                        ending_road_address = '/'.join(ending_road_address_split)

                        starting_road_address_split = road_address.split('/')
                        starting_road_address_split[3] = '0'
                        starting_road_address_split[1] = starting_point[2]
                        starting_road_address = '/'.join(starting_road_address_split)

                        roadway = self.tr(u'Alkupiste: {start}\nLoppupiste: {end}\npituus: {length}m').format(
                            start = starting_road_address, end = ending_road_address, length = road_part_length
                            )

                        for linestring in coordinates:
                            xy_points = self.convert_coordinates_to_XY(linestring)
                            if ajorata == '0':
                                self.LayerHandler.add_roadway_feature('3', roadway, xy_points, ajorata)
                            elif ajorata == '1':
                                self.LayerHandler.add_roadway_feature('3', roadway, xy_points, ajorata)
                            elif ajorata == '2':
                                self.LayerHandler.add_roadway_feature('3', roadway, xy_points, ajorata)

                #getting road part's halfway coordinates for annotation
                road_part_halfway = road_part_length // 2

                point_x, point_y = self.vkm_request_coordinates(vkm_url, road=tie, road_part=osa, distance=road_part_halfway)

                self.LayerHandler.add_annotation('3', roadway, point_x, point_y, 5)
                self.zoom_to_feature(point_x, point_y)

                #adding a point to each end of the road part
                self.LayerHandler.add_point_feature('3', starting_road_address, starting_point[0], starting_point[1], 'starting')
                self.LayerHandler.add_point_feature('3', ending_road_address, ending_point[0], ending_point[1], 'ending')

            except AttributeError:
                self.error_popup(self.tr(u'Pistettä ei ole asetettu.'))
            except VkmApiException as e:
                self.error_popup(e)
            except VkmRequestException as e:
                self.error_popup(e)


        #CRS when the ShowCoordinates-tool is opened
        QgsProject.instance().setCrs(self.my_crs)

        canvas = self.iface.mapCanvas()
        pointTool = QgsMapToolEmitPoint(canvas)
        pointTool.canvasClicked.connect(display_road_part)
        canvas.setMapTool(pointTool)

        # show the dialog
        dlg.show()
        # Run the dialog event loop
        result = dlg.exec_()
        # See if OK was pressed
        if result:

            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            pass


    def two_points(self):
        """Highlights every roadway between two clicked points on the same road."""

        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[3])

        if self.first_start == True:
            self.first_start = False
        self.two_points_dlg = ShowCoordinates_dialog()
        self.two_points_dlg.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)

        self.ajoradat_dlg = Ajoradat_dialog()
        self.ajoradat_dlg.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)

        QgsProject.instance().setCrs(self.my_crs)
        self.canvas = self.iface.mapCanvas()

        self.LayerHandler.init_tool4()
        self.iface.mapCanvas().refresh()


        def display_point_A(pointTool_A):
            """ Operates the same way as display_point(), but in addition: clears the roadway dialog lines at the start, creates road address parameters for VKM-API
                request in the display_point_B() and switches canvas connection to pointTool_B that is used by display_point_B().


            Args:
                pointTool (QgsMapToolEmitPoint()): Tool that returns coordinates from clicking on the canvas that it's connected to.
            """

            #variables for display_point_B
            self.tie_A = 0
            self.ajorata_A = 0
            self.osa_A = 0
            self.etaisyys_A = 0
            self.ajoradat_dlg.Ajorata0lineEdit.clear()
            self.ajoradat_dlg.Ajorata1lineEdit.clear()
            self.ajoradat_dlg.Ajorata2lineEdit.clear()

            try:
                #click on canvas returns coordinates
                point_x = str(pointTool_A.x())
                point_y = str(pointTool_A.y())
                self.two_points_dlg.CoordLineEdit.setText(f'{point_x}, {point_y}')

                #get new coordinates and address from VKM
                vkm_url='https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'

                road_address, point_x, point_y, tie_A, ajorata_A, osa_A, etaisyys_A = self.vkm_request_road_address(vkm_url=vkm_url, point_x=point_x, point_y=point_y, display_point='A')

                self.LayerHandler.add_annotation('4', road_address, point_x, point_y)

                self.tie_A = tie_A
                self.ajorata_A = ajorata_A
                self.osa_A = osa_A
                self.etaisyys_A = etaisyys_A

                #connecting canvas to pointTool B
                self.canvas.setMapTool(pointTool_B)

            except AttributeError:
                self.error_popup(self.tr(u'Pistettä ei ole asetettu.'))
            except VkmApiException as e:
                self.error_popup(e)
            except VkmRequestException as e:
                self.error_popup(e)


        def display_point_B(pointTool_B):
            """ Creates the second point by using pointTool_B, uses its own and display_point_A() function's retrieved VKM-API output addresses
                to request the lengths and linestring geometry of roadways between the two points.

                Highlights the possible roadways by drawing polylines using the retrieved linestrings. Roadways are distinguished by color.

                Conntects canvas back to pointTool_A which is used by display_point_A().

            Args:
                pointTool (QgsMapToolEmitPoint()): Tool that returns coordinates from clicking on the canvas that it's connected to.
            """
            try:
                #click on canvas returns coordinates
                point_x = str(pointTool_B.x())
                point_y = str(pointTool_B.y())
                self.two_points_dlg.CoordLineEdit.setText(f'{point_x}, {point_y}')

                #get new coordinates and address from VKM
                vkm_url='https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'


                road_address, point_x_B, point_y_B, tie_B, ajorata_B, osa_B, etaisyys_B = self.vkm_request_road_address(vkm_url=vkm_url, point_x=point_x, point_y=point_y, display_point='B')


                if tie_B != self.tie_A:
                    self.canvas.setMapTool(pointTool_A)
                    self.error_popup(self.tr(u'Alku- ja loppupisteen on oltava samalla tiellä'))
                    return

                else:
                    self.LayerHandler.add_annotation('4', road_address, point_x_B, point_y_B, None, -34, -21)

                    #getting road address and calculating the distance of roadway(s) between points A and B
                    try:
                        polyline_dict, pituus_dict, request_url, kokonaispituus, road_length = self.vkm_request_geometry(vkm_url, self.tie_A, self.osa_A, self.etaisyys_A, tie_B, osa_B, etaisyys_B)
                        self.ajoradat_dlg.AjoradatPituuslineEdit.clear()
                        self.ajoradat_dlg.AjoradatPituuslineEdit.setText(f'{str(kokonaispituus)}m')
                        self.ajoradat_dlg.PituuslineEdit.clear()
                        self.ajoradat_dlg.PituuslineEdit.setText(f'{str(road_length)}m')

                        for ajorata, coordinates in polyline_dict.items():
                            for ajorata_pituus, pituus in pituus_dict.items():
                                if ajorata_pituus == ajorata:
                                    mitattu_pituus = pituus
                                    break

                            roadway = self.tr(u'A {tie_A}/{ajorata_A}/{osa_A}/{etaisyys_A} - B {tie_B}/{ajorata_B}/{osa_B}/{etaisyys_B} pituus: {mitattu_pituus}').format(
                                tie_A = self.tie_A, ajorata_A = self.ajorata_A, osa_A = self.osa_A, etaisyys_A = self.etaisyys_A,
                                tie_B = tie_B, ajorata_B = ajorata_B, osa_B = osa_B, etaisyys_B = etaisyys_B, mitattu_pituus = mitattu_pituus
                            )

                            for linestring in coordinates:
                                xy_points = self.convert_coordinates_to_XY(linestring)
                                if ajorata == '0':
                                    self.LayerHandler.add_roadway_feature('4', roadway, xy_points, ajorata)
                                elif ajorata == '1':
                                    self.LayerHandler.add_roadway_feature('4', roadway, xy_points, ajorata)
                                elif ajorata == '2':
                                    self.LayerHandler.add_roadway_feature('4', roadway, xy_points, ajorata)

                            if ajorata == '0':
                                self.ajoradat_dlg.Ajorata0lineEdit.clear()
                                self.ajoradat_dlg.Ajorata0lineEdit.setText(roadway)
                            elif ajorata == '1':
                                self.ajoradat_dlg.Ajorata1lineEdit.clear()
                                self.ajoradat_dlg.Ajorata1lineEdit.setText(roadway)
                            elif ajorata == '2':
                                self.ajoradat_dlg.Ajorata2lineEdit.clear()
                                self.ajoradat_dlg.Ajorata2lineEdit.setText(roadway)

                        self.ajoradat_dlg.pushButton_Download.clicked.connect(lambda: self.write_roadways_to_csv(request_url, self.ajoradat_dlg))

                        #connecting canvas back to pointTool A
                        self.canvas.setMapTool(pointTool_A)
                        #showing all the roadways address info
                        self.ajoradat_dlg.show()
                        result = self.ajoradat_dlg.exec_()
                        #connecting canvas back to pointTool A

                        if result:
                            self.canvas.setMapTool(pointTool_A)

                    except VkmApiException as e:
                        self.error_popup(e)
                        self.canvas.setMapTool(pointTool_A)
                        return
                    except VkmRequestException as e:
                        self.error_popup(e)
                        self.canvas.setMapTool(pointTool_A)
                        return

            except AttributeError:
                self.error_popup(self.tr(u'Pistettä ei ole asetettu.'))
            except VkmApiException as e:
                self.error_popup(e)
            except VkmRequestException as e:
                self.error_popup(e)


        pointTool_A = QgsMapToolEmitPoint(self.canvas)
        pointTool_A.canvasClicked.connect(display_point_A)
        self.canvas.setMapTool(pointTool_A)

        pointTool_B = QgsMapToolEmitPoint(self.canvas)
        pointTool_B.canvasClicked.connect(display_point_B)

        self.two_points_dlg.show()
        # Run the dialog event loop
        result = self.two_points_dlg.exec_()

        # See if OK was pressed
        if result:

            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            pass


    def search_form(self):
        """ A dialog with a search form for VKM-API requests. Uses the same search parameteres as VKM-API. Either returns and draws a point or line(s)
            depending on the search parameters given.
        """

        self.LayerHandler.init_tool5()
        self.iface.mapCanvas().refresh()

        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[4])

        QgsProject.instance().setCrs(self.my_crs)
        self.vkm_url='https://avoinapi.vaylapilvi.fi/viitekehysmuunnin/'
        self.search_form_dlg = SearchForm_dialog()
        self.search_form_dlg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)
        self.search_form_dlg.show()
        if self.search_form_dlg.exec_():
            self.vkm_request_form_search()


    def delete_tool(self):
        """ A dialog with buttons that either delete one annotation or all of them."""

        # drop-down menu icon = latest tool used
        self.toolButton.setDefaultAction(self.actions[5])

        QgsProject.instance().setCrs(self.my_crs)
        delete_dlg = DeleteLayer_dialog()
        delete_dlg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint)
        delete_dlg.show()

        delete_dlg.pushButton_delete_annotations.clicked.connect(self.LayerHandler.delete_all_annotations)
        delete_dlg.pushButton_delete_annotation.clicked.connect(self.LayerHandler.delete_annotation)
        delete_dlg.pushButton_delete_feature.clicked.connect(self.LayerHandler.remove_feature)
        delete_dlg.pushButton_delete_all_features.clicked.connect(self.LayerHandler.remove_all_features)
        delete_dlg.exec_()


# ---------------- EXTRA FUNCTIONS ----------------------


    def vkm_request_road_address(self, vkm_url, point_x, point_y, display_point='', palautus_arvot='1,2'):
        """ Returns a road address from VKM-API request.

        Args:
            vkm_url (str): VKM-API URL.
            point_x (str): X coordinate.
            point_y (_type_): Y coordinate.
            display_point (str, optional): Used to distinguish points A and B in two_points(). Defaults to ''.
            palautus_arvot (str, optional): Search parameters for VKM-API that regulate the amount of address information it returns. Defaults to '1,2'.

        Returns:
            road_address (str): Road address that consists of road/roadway/(road)part/distance.
            vkm_error (boolean): Messages to the next function wether to give an error popup or not.
            point_x (float): X coordinate from VKM-API output.
            point_y (float): Y coordinate from VKM-API output.
            tie (str): Road from VKM-API output.
            ajorata (str): Roadway from VKM-API output.
            osa (str): Road part from VKM-API output.
            etaisyys (str): Distance from VKM-API output.
        """

        request_url = f'{vkm_url}muunna?x={point_x}&y={point_y}&palautusarvot={palautus_arvot}&vaylan_luonne=0&sade=50'

        s = requests.Session()
        retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
        s.mount('http://', HTTPAdapter(max_retries=retries))
        response = s.get(request_url, verify=False)
        s.close()

        if response.status_code != 200:
            raise VkmApiException(request_url)

        vkm_data = json.loads(response.content)
        for vkm_feature in vkm_data['features']:
            if 'virheet' in vkm_feature['properties']:
                error_msg = display_point + ' ' + vkm_feature['properties']['virheet']
                raise VkmRequestException(error_msg)

            else:
                try:
                    #getting road coordinates and road address that are nearest to the mouse click
                    point_x = vkm_feature['properties']['x']
                    point_y = vkm_feature['properties']['y']
                    tie = str(vkm_feature['properties']['tie'])
                    ajorata = str(vkm_feature['properties']['ajorata'])
                    osa = str(vkm_feature['properties']['osa'])
                    etaisyys = str(vkm_feature['properties']['etaisyys'])

                    road_address = f'{display_point} {tie}/{ajorata}/{osa}/{etaisyys}'

                except KeyError:
                    #nearest address is a street -> no road address
                    point_x = vkm_feature['properties']['x']
                    point_y = vkm_feature['properties']['y']

                    road_address = self.tr(u'Ei tieosoitetta')


        return road_address, point_x, point_y, tie, ajorata, osa, etaisyys


    def set_popup_text(self, dlg, vkm_feature):
        """Displays address information on PopUp dialog lines.

        Args:
            dlg (QDialog): Dialog to write to.
            vkm_feature (json): One feature of VKM-API output that contains address information.

        Returns:
            (str): Road address that consists of road/roadway/(road)part/distance or a street address.
        """

        if 'katunimi' in  vkm_feature['properties'] and 'katunumero' in vkm_feature['properties']:
            dlg.KatunimilineEdit.setText(str(vkm_feature['properties']['katunimi']))
            dlg.KatunumerolineEdit.setText(str(vkm_feature['properties']['katunumero']))

        if 'tie' in vkm_feature['properties']:
            dlg.TielineEdit.setText(str(vkm_feature['properties']['tie']))
            dlg.AjoratalineEdit.setText(str(vkm_feature['properties']['ajorata']))
            dlg.OsalineEdit.setText(str(vkm_feature['properties']['osa']))
            dlg.EtaisyyslineEdit.setText(str(vkm_feature['properties']['etaisyys']))
            road_name = self.RoadName.get_road_name(vkm_feature['properties']['tie'])
            dlg.MaantiennimilineEdit.setText(road_name)

        if 'ualuenimi' in vkm_feature['properties']:
            dlg.UaluenimilineEdit.setText(str(vkm_feature['properties']['ualuenimi']))

        #getting road coordinates and road address that are nearest to the mouse click
        dlg.XlineEdit.setText(str(vkm_feature['properties']['x']))
        dlg.YlineEdit.setText(str(vkm_feature['properties']['y']))
        dlg.HaLulineEdit.setText(str(vkm_feature['properties']['hallinnollinen_luokka']))
        dlg.KuntanimilineEdit.setText(str(vkm_feature['properties']['kuntanimi']))
        dlg.ElynimilineEdit.setText(str(vkm_feature['properties']['elynimi']))
        dlg.MaakuntanimilineEdit.setText(str(vkm_feature['properties']['maakuntanimi']))
        dlg.Kmtk_idlineEdit.setText(str(vkm_feature['properties']['kmtk_id']))
        dlg.M_arvolineEdit.setText(str(vkm_feature['properties']['m_arvo']))

        if 'tie' in vkm_feature['properties']:
            return '{tie}/{ajorata}/{osa}/{etaisyys}'.format(
                tie = str(vkm_feature['properties']['tie']),
                ajorata = str(vkm_feature['properties']['ajorata']),
                osa = str(vkm_feature['properties']['osa']),
                etaisyys = str(vkm_feature['properties']['etaisyys'])
            )
        if 'katunumero' in vkm_feature['properties']:
            return '{katunimi} {katunumero}, {kuntanimi}'.format(
                katunimi = str(vkm_feature['properties']['katunimi']),
                katunumero = str(vkm_feature['properties']['katunumero']),
                kuntanimi = str(vkm_feature['properties']['kuntanimi'])
            )
        return ""


    def vkm_request_coordinates(self, vkm_url, road, road_part, distance, output_parameters = '1,2'):
        """Returns coordinates from VKM-API request.

        Args:
            vkm_url (str): VKM-API URL.
            road (str): Road for VKM-API request.
            road_part (str): Part for VKM-API request.
            distance (str): Distance for VKM-API request.
            output_parameters (str, optional): Search parameters for VKM-API that regulate the amount of address information it returns. Defaults to '1,2'.

        Raises:
            VkmApiException: VKM-API doesn't respond.
            VkmRequestException: Wrong request parameters.

        Returns:
            point_x (float): X coordinate from VKM-API output.
            point_y (float): Y coordinate from VKM-API output.
        """

        request_url = f'{vkm_url}muunna?tie={road}&osa={road_part}&etaisyys={distance}&palautusarvot={output_parameters}&vaylan_luonne=0'

        s = requests.Session()
        retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
        s.mount('http://', HTTPAdapter(max_retries=retries))
        response = s.get(request_url, verify=False)
        s.close()

        if response.status_code != 200:
            raise VkmApiException(request_url)

        vkm_data = json.loads(response.content)

        for vkm_feature in vkm_data['features']:

            if 'virheet' in vkm_feature['properties']:
                error_message = vkm_feature['properties']['virheet']
                raise VkmRequestException(error_message)

            else:
                #getting road coordinates and road address that are nearest to the mouse click
                point_x = vkm_feature['properties']['x']
                point_y = vkm_feature['properties']['y']

        return point_x, point_y


    def convert_coordinates_to_XY(self, linestring):
        """Converts a linestring to a list of QgsPointXY-type coordinates.

        Args:
            linestring (list): A list of lists of float-type coordinate points.

        Returns:
            xy_points (list): List of linestring coordinates as QgsPointXY.
        """

        return [QgsPointXY(coordinates[0], coordinates[1]) for coordinates in linestring]


    def error_popup(self, error_msg):
        """Gives an error message in Qgis.

        Args:
            road_address (str): Error content.
        """

        self.iface.messageBar().pushMessage(
        f'{error_msg}',
        level=Qgis.Critical, duration=10)


    def vkm_request_geometry(self, vkm_url, tie_A, osa_A, etaisyys_A, tie_B, osa_B, etaisyys_B, palautus_arvot='1,2,5'):
        """Returns line geometry from VKM-API request. Mainly used in two_points()-function.

        Args:
            vkm_url (str): VKM-API URL.
            tie_A (str): Starting road.
            osa_A (_type_): Starting part.
            etaisyys_A (_type_): Starting distance.
            tie_B (_type_): Ending road.
            osa_B (_type_): Ending part.
            etaisyys_B (_type_): Ending distance.
            palautus_arvot (str, optional): Search parameters for VKM-API that regulate the amount of address information it returns. Defaults to '1,2,5'.

        Returns:
            polyline_dict (dict): {roadway : [ [linestring], [linestring],... ], ....}
            pituus_dict (dict): {roadway : roadway length,... }
            request_url (str): VKM URL for usage in write_roadways_to_csv function.
        """

        request_url = f'{vkm_url}muunna?tie={tie_A}&osa={osa_A}&etaisyys={etaisyys_A}&tie_loppu={tie_B}&osa_loppu={osa_B}&etaisyys_loppu={etaisyys_B}&vaylan_luonne=0&valihaku=true&palautusarvot={palautus_arvot}'

        polyline_dict = {}
        pituus_dict = {}

        s = requests.Session()
        retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
        s.mount('http://', HTTPAdapter(max_retries=retries))
        response = s.get(request_url, verify=False)
        s.close()

        if response.status_code != 200:
            raise VkmApiException(request_url)

        vkm_data = json.loads(response.content)

        kokonaispituus = 0
        #length between two points
        road_length = 0
        for vkm_feature in vkm_data['features']:
            if 'virheet' in vkm_feature['properties']:
                error_msg = vkm_feature['properties']['virheet']
                raise VkmRequestException(error_msg)

            else:
                roadway = str(vkm_feature['properties']['ajorata'])
                new_type = str(vkm_feature['geometry']['type'])

                #check if a key already exist and append a linestring to it
                if roadway in polyline_dict:
                    if new_type == 'LineString':
                        polyline_dict[roadway].append(vkm_feature['geometry']['coordinates'])
                    else:
                        for linestring in vkm_feature['geometry']['coordinates']:
                            polyline_dict[roadway].append(linestring)

                elif new_type == 'LineString':
                    polyline_dict[roadway] = [vkm_feature['geometry']['coordinates']]
                else:
                    polyline_dict[roadway] = vkm_feature['geometry']['coordinates']

                #get total legnth of certain roadway lines
                if roadway in pituus_dict:
                    pituus_dict[roadway] = pituus_dict[roadway] + vkm_feature['properties']['mitattu_pituus']
                else:
                    pituus_dict[roadway] = vkm_feature['properties']['mitattu_pituus']

            #get total legnth of all roadway lines
            kokonaispituus = kokonaispituus + vkm_feature['properties']['mitattu_pituus']

            #get road length
            if roadway in ['0', '1']:
                road_length = road_length + vkm_feature['properties']['mitattu_pituus']


        return polyline_dict, pituus_dict, request_url, kokonaispituus, road_length


    def vkm_request_road_part_geometry(self, vkm_url, tie, osa, palautus_arvot='1,2,5'):
        """Returns line geometry of a road part.

        Args:
            vkm_url (str): VKM-API URL.
            tie (str): Road.
            osa (str): (road) part.
            palautus_arvot (str, optional): Search parameters for VKM-API that regulate the amount of address information it returns. Defaults to '1,2,5'.

        Returns:
            polyline_dict (dict): {roadway : [ [linestring], [linestring],... ], ....}
            road_part_length (int): Road part length.
            starting_point (list): Contains XY-coordinates.
            ending_point (list): Contains XY-coordinates.
            request_url (str): VKM URL for usage in write_roadways_to_csv function.
        """

        request_url = f'{vkm_url}muunna?tie={tie}&osa={osa}&osa_loppu={osa}&vaylan_luonne=0&valihaku=true&palautusarvot={palautus_arvot}'

        polyline_dict = {}

        s = requests.Session()
        retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
        s.mount('http://', HTTPAdapter(max_retries=retries))
        response = s.get(request_url, verify=False)
        s.close()

        if response.status_code != 200:
            raise VkmApiException(request_url)

        vkm_data = json.loads(response.content)

        starting_point = []
        #road part length = highest ending distance in any of the roadways of the road part
        road_part_length = 0

        for vkm_feature in vkm_data['features']:

            if 'virheet' in vkm_feature['properties']:
                error_msg = vkm_feature['properties']['virheet']
                raise VkmRequestException(error_msg)

            else:
                if vkm_feature['properties']['etaisyys'] == 0:
                    starting_point.extend((vkm_feature['properties']['x'], vkm_feature['properties']['y'], str(vkm_feature['properties']['ajorata'])))

                if vkm_feature['properties']['etaisyys_loppu'] > road_part_length:
                    road_part_length = vkm_feature['properties']['etaisyys_loppu']
                    x_end = vkm_feature['properties']['x_loppu']
                    y_end = vkm_feature['properties']['y_loppu']
                    roadway_end = str(vkm_feature['properties']['ajorata_loppu'])

                ajorata = str(vkm_feature['properties']['ajorata'])
                new_type = str(vkm_feature['geometry']['type'])

                #check if a key already exist and append a linestring to it
                if ajorata in polyline_dict:
                    if new_type == 'LineString':
                        polyline_dict[ajorata].append(vkm_feature['geometry']['coordinates'])
                    else:
                        for linestring in vkm_feature['geometry']['coordinates']:
                            polyline_dict[ajorata].append(linestring)
                            
                elif new_type == 'LineString':
                    polyline_dict[ajorata] = [vkm_feature['geometry']['coordinates']]
                else:
                    polyline_dict[ajorata] = vkm_feature['geometry']['coordinates']

        ending_point = [x_end, y_end, roadway_end]

        return polyline_dict, road_part_length, starting_point, ending_point, request_url


    def vkm_request_form_search(self):
        """ Checks which lines in SearchForm dialog have content and either returns an error or
            passes the content as VKM-API request parameters to the next function. Runs when Search-button is pressed in the SearchForm dialog.
        """

        params_dict = self.append_form_layout_lines()

        if len(params_dict) == 0:
            self.error_popup(self.tr(u'Täytä vaaditut kentät.'))
            return

        else:
            self.process_center_form_params(params_dict)


    def append_form_layout_lines(self):
        """Appends non-empty SearchForm lines to a dictionary.

        Returns:
            params_dict (dict): Dictionary with VKM-API request parameters.
        """

        lineEdits = self.search_form_dlg.findChildren(QLineEdit)
        return {line.objectName(): line.text() for line in lineEdits if line.text()}


    def process_center_form_params(self, params, output_parameters = '1,2,3,4,5,6'):
        """ Makes a VKM-API request with paramateres given in SearchForm dialog and either draws a point or polyline(s) depending on the ssearch parameters.

            Also centers the canvas on the returned coordinates.

        Args:
            params (dict): Dictionary with VKM-API request parameters.
            palautus_arvot (str, optional): Search parameters for VKM-API that regulate the amount of address information it returns. Defaults to '1,2,3,4,5,6'.
        """

        url = self.vkm_url + 'muunna?'
        url_x = '&x='
        url_y = '&y='
        url_road = '&tie='
        url_roadway = '&ajorata='
        url_part = '&osa='
        url_distance = '&etaisyys='

        url_part_end = '&osa_loppu='
        url_distance_end = '&etaisyys_loppu='

        url_municipality_name = '&kuntanimi='
        url_street_name = '&katunimi='
        url_street_number = '&katunumero='

        url_kmtk_id = '&kmtk_id='
        url_m_value = '&m_arvo='

        #add user input as vkm request parameters
        if 'lineEdit_X' in params:
            url = url + url_x + params['lineEdit_X']
        if 'lineEdit_Y' in params:
            url = url + url_y + params['lineEdit_Y']
        if 'lineEdit_Tie' in params:
            url = url + url_road + params['lineEdit_Tie']
        if 'lineEdit_Ajorata' in params:
            url = url + url_roadway + params['lineEdit_Ajorata']
        if 'lineEdit_Osa' in params:
            url = url + url_part + params['lineEdit_Osa']
        if 'lineEdit_Etaisyys' in params:
            url = url + url_distance + params['lineEdit_Etaisyys']

        if 'lineEdit_Osa_loppu' in params:
            url = url + url_part_end + params['lineEdit_Osa_loppu']
        if 'lineEdit_Etaisyys_loppu' in params:
            url = url + url_distance_end + params['lineEdit_Etaisyys_loppu']

        if 'lineEdit_Kuntanimi' in params:
            url = url + url_municipality_name + params['lineEdit_Kuntanimi']
        if 'lineEdit_Katunimi' in params:
            url = url + url_street_name + params['lineEdit_Katunimi']
        if 'lineEdit_Katunumero' in params:
            url = url + url_street_number + params['lineEdit_Katunumero']

        if 'lineEdit_Kmtk_id' in params:
            url = url + url_kmtk_id + params['lineEdit_Kmtk_id']
        if 'lineEdit_M_arvo' in params:
            url = url + url_m_value + params['lineEdit_M_arvo']

        #valihaku true or false
        valihaku = 'false'
        if 'lineEdit_X' in params and 'lineEdit_Y' in params:
            valihaku = 'false'
        elif 'lineEdit_Katunumero' in params or 'lineEdit_M_arvo' in params:
            valihaku = 'false'
        elif 'lineEdit_Katunimi' in params and 'lineEdit_Kuntanimi' in params:
            valihaku = 'true'
        elif 'lineEdit_Osa_loppu' in params:
            valihaku = 'true'
        elif 'lineEdit_Tie' in params and 'lineEdit_Etaisyys' not in params:
            valihaku = 'true'

        final_url = f'{url}&valihaku={valihaku}&palautusarvot={output_parameters}'

        s = requests.Session()
        retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
        s.mount('http://', HTTPAdapter(max_retries=retries))
        response = s.get(final_url, verify=False)
        s.close()

        if response.status_code != 200:
            self.error_popup(self.tr(u'VKM-API ei vastaa. URL: {final_url}').format(final_url = final_url))
            return

        vkm_data = json.loads(response.content)

        popup_dlg = PopUp_dialog()
        roadways_dlg = Ajoradat_dialog()

        #variables for starting and ending road adresses of a polyline
        x_start = 0
        y_start = 0
        road = 0
        roadway = 0
        part = 0
        distance = 0

        x_end = 0
        y_end = 0
        road_end = 0
        roadway_end = 0
        part_end = 0
        distance_end = 0

        #variables for extracting starting and ending points' params
        distance_int = 0
        part_end_int = 0
        distance_end_int = 0


        #used to find smallest road part in road-only search
        part_min = 999999999
    
        #sum of all the roadways
        overall_length = 0

        #if the line-like search was a road part search
        road_part_length = 0
        #distance between two points
        road_length = 0

        polyline_dict = {}
        length_dict = {}

        for vkm_feature in vkm_data['features']:
            self.search_form_dlg.close()

            if 'virheet' in vkm_feature['properties']:
                error_message = vkm_feature['properties']['virheet']
                self.error_popup(error_message)
                return

            elif vkm_feature['geometry']['type'] == 'Point':
                self.set_popup_text(popup_dlg, vkm_feature)
                point_x = vkm_feature['properties']['x']
                point_y = vkm_feature['properties']['y']
                self.LayerHandler.add_point_feature('5', self.tr(u'Pistemäinen haku'), point_x, point_y)
                self.zoom_to_feature(point_x, point_y)
                popup_dlg.show()
                result = popup_dlg.exec_()
                if result:
                    lineEdits = popup_dlg.findChildren(QLineEdit)
                    for line in lineEdits:
                        if line.text():
                            line.clear()

            else:
                #process and draw a polyline

                #starting params for road address to display and for marking the starting point of the road
                if 'lineEdit_X' in params:
                    x_start = float(params['lineEdit_X'])

                if 'lineEdit_Y' in params:
                    y_start = float(params['lineEdit_Y'])

                if road == 0 and 'lineEdit_Tie' not in params:
                    road = str(vkm_feature['properties']['tie'])
                elif 'lineEdit_Tie' in params:
                    road = params['lineEdit_Tie']

                if roadway == 0 and 'lineEdit_Ajorata' not in params:
                    roadway = str(vkm_feature['properties']['ajorata'])
                elif 'lineEdit_Ajorata' in params:
                    roadway = params['lineEdit_Ajorata']

                if part == 0 and 'lineEdit_Osa' not in params:
                    part = str(vkm_feature['properties']['osa'])
                elif 'lineEdit_Osa' in params:
                    part = params['lineEdit_Osa']

                if distance == 0 and 'lineEdit_Etaisyys' not in params:
                    distance = str(vkm_feature['properties']['etaisyys'])
                elif 'lineEdit_Etaisyys' in params:
                    distance = params['lineEdit_Etaisyys']

            
                #line searches are always on the same road
                road_end = road

                roadway_end = str(vkm_feature['properties']['ajorata'])

                if 'lineEdit_Osa_loppu' in params and 'lineEdit_Etaisyys_loppu' in params:
                    #ending point
                    part_end = params['lineEdit_Osa_loppu']
                    distance_end = params['lineEdit_Etaisyys_loppu']

                    #line search length
                    if vkm_feature['properties']['ajorata'] == 0 or vkm_feature['properties']['ajorata'] == 1:
                        road_length = road_length + vkm_feature['properties']['mitattu_pituus']

                #search was a road part search -> distance_end = highest ending distance of all the features(lines)
                elif 'lineEdit_Osa_loppu' in params and 'lineEdit_Etaisyys_loppu' not in params:
                    part_end = params['lineEdit_Osa_loppu']
                    if vkm_feature['properties']['etaisyys_loppu'] > road_part_length:
                        roadway_end = str(vkm_feature['properties']['ajorata_loppu'])
                        road_part_length = vkm_feature['properties']['etaisyys_loppu']
                        distance_end = str(road_part_length)
                        road_length = distance_end

                    #starting point from a feature where road part's distance = 0
                    if vkm_feature['properties']['etaisyys'] == 0:
                        x_start = vkm_feature['properties']['x']
                        y_start = vkm_feature['properties']['y']
                        distance = 0
                        roadway = str(vkm_feature['properties']['ajorata'])

                #search was a road-only search
                elif 'lineEdit_Tie' in params and 'lineEdit_Osa' not in params and 'lineEdit_Osa_loppu' not in params:
                    #ending point
                    if vkm_feature['properties']['osa_loppu'] > part_end_int:
                        roadway_end = str(vkm_feature['properties']['ajorata_loppu'])
                        part_end_int = vkm_feature['properties']['osa_loppu']
                        part_end = str(part_end_int)
                        distance_end_int = vkm_feature['properties']['etaisyys_loppu']
                        distance_end = str(distance_end_int)

                    elif vkm_feature['properties']['osa_loppu'] == part_end_int:
                        if vkm_feature['properties']['etaisyys_loppu'] > distance_end_int:
                            roadway_end = str(vkm_feature['properties']['ajorata_loppu'])
                            part_end_int = vkm_feature['properties']['osa_loppu']
                            part_end = str(part_end_int)
                            distance_end_int = vkm_feature['properties']['etaisyys_loppu']
                            distance_end = str(distance_end_int)

                    #line search length
                    if vkm_feature['properties']['ajorata'] == 0 or vkm_feature['properties']['ajorata'] == 1:
                        road_length = road_length + vkm_feature['properties']['mitattu_pituus']

                    #starting point
                    if vkm_feature['properties']['osa'] < part_min:
                        part_min = vkm_feature['properties']['osa']
                        part = str(part_min)
                        roadway = str(vkm_feature['properties']['ajorata'])
                        distance_int = vkm_feature['properties']['etaisyys']
                        distance = str(distance_int)
                        x_start = vkm_feature['properties']['x']
                        y_start = vkm_feature['properties']['y']
                    elif vkm_feature['properties']['osa'] == part_min:
                        if vkm_feature['properties']['etaisyys'] < distance_int:
                            part = str(vkm_feature['properties']['osa'])
                            roadway = str(vkm_feature['properties']['ajorata'])
                            distance_int = vkm_feature['properties']['etaisyys']
                            distance = str(distance_int)
                            x_start = vkm_feature['properties']['x']
                            y_start = vkm_feature['properties']['y']
                            

                polyline_roadway = str(vkm_feature['properties']['ajorata'])
                new_type = str(vkm_feature['geometry']['type'])

                #check if a key already exist and append a linestring to it
                if polyline_roadway in polyline_dict:
                    if new_type == 'LineString':
                        polyline_dict[polyline_roadway].append(vkm_feature['geometry']['coordinates'])
                    else:
                        for linestring in vkm_feature['geometry']['coordinates']:
                            polyline_dict[polyline_roadway].append(linestring)
                else:
                    if new_type == 'LineString':
                        polyline_dict[polyline_roadway] = [vkm_feature['geometry']['coordinates']]
                    else:
                        polyline_dict[polyline_roadway] = vkm_feature['geometry']['coordinates']

                #get length of each roadway
                if polyline_roadway in length_dict:
                    length_dict[polyline_roadway] = length_dict[polyline_roadway] + vkm_feature['properties']['mitattu_pituus']
                else:
                    length_dict[polyline_roadway] = vkm_feature['properties']['mitattu_pituus']

                #get roadways' total length
                overall_length = overall_length + vkm_feature['properties']['mitattu_pituus']

        if len(polyline_dict) != 0:
            roadways_dlg.AjoradatPituuslineEdit.clear()
            roadways_dlg.AjoradatPituuslineEdit.setText(f'{str(overall_length)}m')
            roadways_dlg.PituuslineEdit.clear()
            roadways_dlg.PituuslineEdit.setText(f'{str(road_length)}m')

            for polyline_roadway, coordinates in polyline_dict.items():
                for length_dict_roadway, length in length_dict.items():
                    if length_dict_roadway == polyline_roadway:
                        measured_length = str(length)
                        break
                polyline_adress = self.tr(u'Alkupiste {road}/{roadway}/{part}/{distance} - Loppupiste {road_end}/{roadway_end}/{part_end}/{distance_end}, mitattu pituus: {measured_length}').format(
                    road = road, roadway = roadway, part = part, distance = distance,
                    road_end = road_end, roadway_end = roadway_end, part_end = part_end, distance_end = distance_end, 
                    measured_length = measured_length
                )

                for linestring in coordinates:
                    xy_points = self.convert_coordinates_to_XY(linestring)
                    if polyline_roadway == '0':
                        self.LayerHandler.add_roadway_feature('5', polyline_adress, xy_points, polyline_roadway)
                    elif polyline_roadway == '1':
                        self.LayerHandler.add_roadway_feature('5', polyline_adress, xy_points, polyline_roadway)
                    elif polyline_roadway == '2':
                        self.LayerHandler.add_roadway_feature('5', polyline_adress, xy_points, polyline_roadway)

                if polyline_roadway == '0':
                    roadways_dlg.Ajorata0lineEdit.clear()
                    roadways_dlg.Ajorata0lineEdit.setText(polyline_adress)
                elif polyline_roadway == '1':
                    roadways_dlg.Ajorata1lineEdit.clear()
                    roadways_dlg.Ajorata1lineEdit.setText(polyline_adress)
                elif polyline_roadway == '2':
                    roadways_dlg.Ajorata2lineEdit.clear()
                    roadways_dlg.Ajorata2lineEdit.setText(polyline_adress)

            #draw ending and starting points, zoom to starting point
            
            if x_start == 0 and y_start == 0:
                #getting starting coordinates
                x_start, y_start = self.vkm_request_coordinates(self.vkm_url, road, part, distance)
            self.LayerHandler.add_point_feature('5', self.tr(u'Alkupiste'), x_start, y_start, 'starting')
            self.zoom_to_feature(x_start, y_start)
            #getting ending coordinates
            x_end, y_end = self.vkm_request_coordinates(self.vkm_url, road_end, part_end, distance_end)
            self.LayerHandler.add_point_feature('5', self.tr(u'Loppupiste'), x_end, y_end, 'ending')

            roadways_dlg.pushButton_Download.clicked.connect(lambda: self.write_roadways_to_csv(final_url, roadways_dlg))
            roadways_dlg.show()
            result = roadways_dlg.exec_()
            if result:
                roadways_dlg.pushButton.setEnabled(False)
                return

    def zoom_to_feature(self, point_x = None, point_y = None):
        """Zooms and centers to given coordinates.

        Args:
            point_x (float, optional): X coordinate. Defaults to None.
            point_y (float, optional): Y coordiante. Defaults to None.
        """

        rectangle = QgsRectangle(point_x, point_y, point_x, point_y)
        canvas = self.iface.mapCanvas()
        canvas.setExtent(rectangle)
        canvas.zoomScale(16555 / 1)


    def write_roadways_to_csv(self, request_url, dlg):
        # sourcery skip: ensure-file-closed
        """Writes ending and starting address data of every feature in linestring VKM output to a CSV-file.

        Args:
            request_url (str): VKM URL used in two_points() or process_search_form_params functions.
            dlg (QDialog): Dialog.

        Raises:
            VkmApiException: VKM API doesn't respond.
            VkmRequestException: Wrong request parameters.
        """

        try:
            s = requests.Session()
            retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ])
            s.mount('http://', HTTPAdapter(max_retries=retries))
            response = s.get(request_url, verify=False)
            s.close()

            if response.status_code != 200:
                raise VkmApiException(request_url)

            vkm_data = json.loads(response.content)

            user_path = dlg.get_file_path()

            roadways_file = open(str(user_path), 'w')

            header_row = 'tie,ajr,aosa,aet,losa,let,pituus\n'
            roadways_file.write(header_row)
            for vkm_feature in vkm_data['features']:

                if 'virheet' in vkm_feature['properties']:
                    error_msg = vkm_feature['properties']['virheet']
                    raise VkmRequestException(error_msg)

                else:
                    road = str(vkm_feature['properties']['tie'])
                    roadway = str(vkm_feature['properties']['ajorata'])
                    part = str(vkm_feature['properties']['osa'])
                    distance = str(vkm_feature['properties']['etaisyys'])

                    part_end = str(vkm_feature['properties']['osa_loppu'])
                    distance_end = str(vkm_feature['properties']['etaisyys_loppu'])

                    length = str(vkm_feature['properties']['mitattu_pituus'])

                    feature_row = f'{road},{roadway},{part},{distance},{part_end},{distance_end},{length}\n'
                    roadways_file.write(feature_row)

            roadways_file.close()

            message = self.tr(u'Tiedosto tallennettu polkuun: {user_path}').format(user_path = str(user_path))
            self.iface.messageBar().pushMessage(self.tr(u'Lataus onnistui'), message, level=Qgis.Success, duration=7)

        except VkmApiException as e:
            self.error_popup(e)
        except VkmRequestException as e:
            self.error_popup(e)
        except OSError as e:
            self.error_popup(self.tr(u'Virhe tiedostoa ladattaessa. Yritä uudelleen.'))
            logging.info(e)

