"""
/***************************************************************************
 TraverseDialog
                                 A QGIS plugin
 Topaze
                             -------------------
        begin                : 2022-09-29
        git sha              : $Format:%H$
        copyright            : (C) 2022 by Jean-Marie ARSAC
        email                : jmarsac@arsac.wf
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import datetime
import json
import os
from pathlib import Path

import numpy as np
from PyQt5 import QtCore, QtWidgets, uic
from qgis.core import Qgis, QgsProject
from qgis.PyQt.QtCore import QDir, QUrl

from topaze.calc.antenna_traverse import AntennaTraverse
from topaze.calc.closed_traverse import ClosedTraverse
from topaze.calc.framed_traverse import FramedTraverse
from topaze.dlg_util import DlgUtil
from topaze.file_utils import FileUtils
from topaze.ptopo import Ptopo, PtopoConst
from topaze.toolbelt.i18n import tr
from topaze.toolbelt.log_handler import PlgLogger
from topaze.topaze_config import TopazeConfig
from topaze.topaze_utils import TopazeUtils

FORM_CLASS, _ = uic.loadUiType(
    os.path.join(os.path.dirname(__file__), "traverse_dialog.ui")
)


class TraverseDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(TraverseDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.setWindowTitle(tr("Compute traverse"))
        self.listWidget_available.currentTextChanged.connect(
            self.display_available_coordinates
        )
        self.listWidget_useful.currentTextChanged.connect(
            self.display_useful_coordinates
        )
        self.pushButton_use.clicked.connect(self.use_it)
        self.pushButton_unuse.clicked.connect(self.unuse_it)
        self.pushButton_use_all.clicked.connect(self.use_all)
        self.pushButton_unuse_all.clicked.connect(self.unuse_all)
        self.pushButton_compute.clicked.connect(self.on_pushbutton_compute_clicked)
        self.pushButton_quit.clicked.connect(self.on_pushbutton_quit_clicked)
        self.pushButton_save.clicked.connect(self.on_pushbutton_save_clicked)
        self.checkBox_compute_z.stateChanged.connect(
            self.on_checkBox_compute_z_stateChanged
        )
        self.radioButton_antenna.toggled.connect(self.on_radioButton_antenna_toggled)
        self.radioButton_framed.toggled.connect(self.on_radioButton_framed_toggled)
        self.radioButton_closed.toggled.connect(self.on_radioButton_closed_toggled)
        self.lineEdit_linear_alteration.textChanged.connect(
            self.on_lineEdit_linear_alteration_changed
        )
        self.checkBox_apparent_level.stateChanged.connect(
            self.on_checkBox_apparent_level_changed
        )

        self._obs_array = None
        self.iface = None
        self.radioButton_antenna.setChecked(True)
        self.checkBox_compute_z.setChecked(True)
        self.lineEdit_linear_alteration.setText("1.0")  # k_alteration_lineaire
        self.checkBox_apparent_level.setChecked(True)  # niveau apparent
        self._ptopo_array = None
        self._ptopo_layer = None

    def set_iface(self, iface):
        self.iface = iface

    def showDialog(self, obs_array, ptopo_array=None, ptopo_layer=None):
        self._obs_array = obs_array
        self._ptopo_array = ptopo_array
        self._ptopo_layer = ptopo_layer
        self.load_stations()
        self.show()

    def on_checkBox_compute_z_stateChanged(self, state):
        if state == QtCore.Qt.Checked:
            self.compute_z = True
        else:
            self.compute_z = False

    def on_checkBox_apparent_level_changed(self, state):
        if state == QtCore.Qt.Checked:
            self.apparent_level = True
        else:
            self.apparent_level = False

    def on_lineEdit_linear_alteration_changed(self, text):
        try:
            self.k_alteration_lineaire = float(text)
        except ValueError:
            self.k_alteration_lineaire = 1.0

    def on_radioButton_antenna_toggled(self, checked):
        if checked:
            self.path_type = "antenna"

    def on_radioButton_framed_toggled(self, checked):
        if checked:
            self.path_type = "framed"

    def on_radioButton_closed_toggled(self, checked):
        if checked:
            self.path_type = "closed"

    def on_pushbutton_compute_clicked(self):
        to_compute = []
        references = []
        try:
            if self.listWidget_useful.count() < 2:
                raise RuntimeError(
                    tr("At least 2 stations must be selected to compute a traverse")
                )
            for index in range(self.listWidget_useful.count()):
                item = self.listWidget_useful.item(index)
                to_compute.append(item.text())

            self.clear_coordinates_fields()
            target_path = QgsProject.instance().homePath() + "/rapport"
            if self.path_type == "antenna":
                json_fullfilepath = self.save_data_to_compute_antenna_traverse(
                    "antenna_path.json", self._obs_array, to_compute, references
                )
                self._results = AntennaTraverse.compute_antenna_traverse_from_json(
                    Path(json_fullfilepath), Path(target_path)
                )
                msg1 = tr("Antenna traverse calculation of ") + str(to_compute)
                msg2 = tr(' report in folder <a href="{}">{}</a>').format(
                    QUrl.fromLocalFile(target_path).toString(),
                    QDir.toNativeSeparators(target_path),
                )
                PlgLogger.log(msg1 + msg2, log_level=3, push=True)
            elif self.path_type == "framed":
                json_fullfilepath = self.save_data_to_compute_framed_traverse(
                    "framed_path.json", self._obs_array, to_compute, references
                )
                self._results = FramedTraverse.compute_framed_traverse_from_json(
                    Path(json_fullfilepath), Path(target_path)
                )
                msg1 = tr("Framed traverse calculation of ") + str(to_compute)
                msg2 = tr(' report in folder <a href="{}">{}</a>').format(
                    QUrl.fromLocalFile(target_path).toString(),
                    QDir.toNativeSeparators(target_path),
                )
                self.iface.messageBar().pushMessage(msg1, msg2, Qgis.Success, 10)
            elif self.path_type == "closed":
                json_fullfilepath = self.save_data_to_compute_antenna_traverse(
                    "closed_path.json", self._obs_array, to_compute, references
                )
                self._results = ClosedTraverse.compute_closed_traverse_from_json(
                    Path(json_fullfilepath), Path(target_path)
                )
                msg1 = tr("Closed traverse calculation of ") + str(to_compute)
                msg2 = tr(' report in folder <a href="{}">{}</a>').format(
                    QUrl.fromLocalFile(target_path).toString(),
                    QDir.toNativeSeparators(target_path),
                )
                PlgLogger.log(msg1 + msg2, log_level=3, push=True)
        except Exception as e:
            PlgLogger.log(str(e), log_level=2, push=True)

    def on_pushbutton_quit_clicked(self):
        if FileUtils.exists_temp_file("antenna_path.json"):
            FileUtils.remove_temp_file("antenna_path.json")
        if FileUtils.exists_temp_file("framed_path.json"):
            FileUtils.remove_temp_file("framed_path.json")
        if FileUtils.exists_temp_file("closed_path.json"):
            FileUtils.remove_temp_file("closed_path.json")
        self.hide()

    def on_pushbutton_save_clicked(self):
        for station_id in self._to_compute_array[1:]:
            if station_id in self._results["stations"]:
                pt = Ptopo(
                    matricule=station_id,
                    type="R",
                    x=float(self._results["stations"][station_id].x),
                    y=float(self._results["stations"][station_id].y),
                    z=float(self._results["stations"][station_id].z),
                )
                if TopazeUtils.ptopo_exists_in_array(pt.matricule, self._obs_array):
                    TopazeUtils.update_xyz_in_array(
                        self._obs_array, pt.matricule, pt.x, pt.y, pt.z
                    )
                else:
                    self._obs_array.append(pt)
                fid, created = TopazeUtils.upsert_ptopo_in_layer(pt, True, self.iface)
                if fid:
                    msg1 = tr("Antenna path")
                    if created:
                        msg2 = tr("PTOPO #{} ({}) created.").format(
                            str(fid), station_id
                        )
                    else:
                        msg2 = tr("PTOPO #{} ({}) updated.").format(
                            str(fid), station_id
                        )
                    self.iface.messageBar().pushMessage(msg1, msg2, Qgis.Success, 10)

    def load_stations(self):
        if self._obs_array:
            self._station_matricules = (
                TopazeUtils.find_all_station_matricules_in_obs_array(self._obs_array)
            )
            self.listWidget_available.clear()
            self.listWidget_useful.clear()
            DlgUtil.load_combobox_list_array(
                self.listWidget_available, self._station_matricules, False
            )

    def use_it(self):
        selectedItems = self.listWidget_available.selectedItems()
        if not selectedItems:
            return

        for item in selectedItems:
            self.listWidget_available.takeItem(self.listWidget_available.row(item))
            self.listWidget_useful.addItem(item)

        self.clear_coordinates_fields()

    def unuse_it(self):
        selectedItems = self.listWidget_useful.selectedItems()
        if not selectedItems:
            return

        for item in selectedItems:
            self.listWidget_useful.takeItem(self.listWidget_useful.row(item))
            self.listWidget_available.addItem(item)

        self.clear_coordinates_fields()

    def use_all(self):
        items = self.listWidget_available.findItems("", QtCore.Qt.MatchContains)
        if not items:
            return

        for item in items:
            self.listWidget_available.takeItem(self.listWidget_available.row(item))
            self.listWidget_useful.addItem(item)

        self.clear_coordinates_fields()

    def unuse_all(self):
        items = self.listWidget_useful.findItems("", QtCore.Qt.MatchContains)
        if not items:
            return

        for item in items:
            self.listWidget_useful.takeItem(self.listWidget_useful.row(item))
            self.listWidget_available.addItem(item)
        self.clear_coordinates_fields()

    def display_coordinates(self, matricule):
        try:
            # print(matricule)
            self.clear_coordinates_fields()
            pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                matricule, self._obs_array
            )
            if pt is None:
                pt = TopazeUtils.get_ptopo_by_matricule(
                    matricule, self._ptopo_array, self._ptopo_layer
                )
            if pt:
                self.lineEdit_x_raw.setText(str(pt.x))
                self.lineEdit_y_raw.setText(str(pt.y))
                self.lineEdit_z_raw.setText(str(pt.z))
        except Exception as e:
            print(str(e))

    def display_available_coordinates(self):
        try:
            matricule = self.listWidget_available.currentItem().text()
            self.display_coordinates(matricule)
        except Exception as e:
            print(str(e))

    def display_useful_coordinates(self):
        try:
            matricule = self.listWidget_useful.currentItem().text()
            self.display_coordinates(matricule)
        except Exception as e:
            print(str(e))

    def save_data_to_compute_antenna_traverse(
        self, tmp_filename, obs_array, to_compute_array, reference_array
    ):
        self._to_compute_array = to_compute_array
        config = TopazeConfig.config_dict()
        data_dict = {
            "meta": {
                "project_name": "Antenna traverse calculation of "
                + self.lineEdit_path_name.text(),
                "observer": os.getlogin(),
                "instrument": "TS30",
                "date": str(datetime.date.today()),
                "angle_unit": "gon",
                "distance_unit": "m",
            },
            "config": {
                "distance_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "distance_back_forward_tolerance"
                    ]
                ),
                "direction_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "direction_back_forward_tolerance"
                    ]
                ),
                "vertical_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "vertical_back_forward_tolerance"
                    ]
                ),
                "angular_closure_tolerance": float(
                    config["common"]["traverse_tolerances"]["angular_closure_tolerance"]
                ),
                "planimetric_closure_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "planimetric_closure_tolerance"
                    ]
                ),
                "compute_z": self.compute_z,
                "linear_scale_factor": self.k_alteration_lineaire,
            },
            "calcul": to_compute_array[1:],
            "stations_sequence": to_compute_array,
            "corrections": {"k_alteration_lineaire": 1.0, "niveau_apparent": True},
            "stations": [],
            "obs": [],
            "stdev": {
                "ah_mgon": 1.0,  # écart-type dir. horizontale en mgon
                "av_mgon": 1.5,  # écart-type zénithal en mgon
                "di_mm": 2.0,  # composante fixe (mm)
                "di_ppm": 2.0,  # composante proportionnelle (ppm)
            },
        }

        stations = TopazeUtils.find_all_stations_in_obs_array(obs_array)
        for station in stations:
            if station.matricule in list(set(to_compute_array + reference_array)):
                dico = {"matricule": station.matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    station.matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        station.matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                data_dict["stations"].append(dico)

            reference_matricules = (
                TopazeUtils.find_all_reference_matricules_in_obs_array(
                    obs_array, station.matricule
                )
            )
            for matricule in reference_matricules:
                dico = {"matricule": matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                if dico not in data_dict["stations"]:
                    data_dict["stations"].append(dico)
            if station.matricule in to_compute_array:
                references = TopazeUtils.find_all_references_in_obs_array(
                    obs_array, station.matricule
                )
            else:
                references = []
            for ref in references:
                if ref.matricule in reference_matricules:
                    dico = {"origine": station.matricule, "hi": station.hi}
                    if station.matricule and ref.matricule in to_compute_array:
                        sight_type = "traverse"
                    else:
                        sight_type = "reference"
                    if ref.av is None or ref.av < 200.0:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": station.v0,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CG",
                            }
                        )
                    else:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": None,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CD",
                            }
                        )
                    data_dict["obs"].append(dico)
        data_s = json.dumps(data_dict, indent=4)
        fullfilepath = FileUtils.create_temp_file(tmp_filename, data_s)
        return fullfilepath

    def save_data_to_compute_framed_traverse(
        self, tmp_filename, obs_array, to_compute_array, reference_array
    ):
        self._to_compute_array = to_compute_array
        config = TopazeConfig.config_dict()
        data_dict = {
            "meta": {
                "project_name": "Framed path " + self.lineEdit_path_name.text(),
                "observer": os.getlogin(),
                "instrument": "TS30",
                "date": str(datetime.date.today()),
                "angle_unit": "gon",
                "distance_unit": "m",
            },
            "config": {
                "distance_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "distance_back_forward_tolerance"
                    ]
                ),
                "direction_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "direction_back_forward_tolerance"
                    ]
                ),
                "vertical_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "vertical_back_forward_tolerance"
                    ]
                ),
                "angular_closure_tolerance": float(
                    config["common"]["traverse_tolerances"]["angular_closure_tolerance"]
                ),
                "planimetric_closure_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "planimetric_closure_tolerance"
                    ]
                ),
                "compute_z": self.compute_z,
                "linear_scale_factor": self.k_alteration_lineaire,
            },
            "calcul": to_compute_array[1 : len(to_compute_array) - 1],
            "stations_sequence": to_compute_array,
            "corrections": {"k_alteration_lineaire": 1.0, "niveau_apparent": True},
            "stations": [],
            "obs": [],
            "stdev": {
                "ah_mgon": 1.0,  # écart-type dir. horizontale en mgon
                "av_mgon": 1.5,  # écart-type zénithal en mgon
                "di_mm": 2.0,  # composante fixe (mm)
                "di_ppm": 2.0,  # composante proportionnelle (ppm)
            },
        }

        stations = TopazeUtils.find_all_stations_in_obs_array(obs_array)
        for station in stations:
            if station.matricule in list(set(to_compute_array + reference_array)):
                dico = {"matricule": station.matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    station.matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        station.matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                data_dict["stations"].append(dico)

            reference_matricules = (
                TopazeUtils.find_all_reference_matricules_in_obs_array(
                    obs_array, station.matricule
                )
            )
            for matricule in reference_matricules:
                dico = {"matricule": matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                if dico not in data_dict["stations"]:
                    data_dict["stations"].append(dico)
            if station.matricule in to_compute_array:
                references = TopazeUtils.find_all_references_in_obs_array(
                    obs_array, station.matricule
                )
            else:
                references = []
            for ref in references:
                if ref.matricule in reference_matricules:
                    dico = {"origine": station.matricule, "hi": station.hi}
                    if station.matricule and ref.matricule in to_compute_array:
                        sight_type = "traverse"
                    else:
                        sight_type = "reference"
                    if ref.av is None or ref.av < 200.0:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": None,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CG",
                            }
                        )
                    else:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": None,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CD",
                            }
                        )
                    data_dict["obs"].append(dico)
        data_s = json.dumps(data_dict, indent=4)
        fullfilepath = FileUtils.create_temp_file(tmp_filename, data_s)
        return fullfilepath

    def save_data_to_compute_closed_traverse(
        self, tmp_filename, obs_array, to_compute_array, reference_array
    ):
        self._to_compute_array = to_compute_array
        config = TopazeConfig.config_dict()
        data_dict = {
            "meta": {
                "project_name": "Closed path " + self.lineEdit_path_name.text(),
                "observer": os.getlogin(),
                "instrument": "TS30",
                "date": str(datetime.date.today()),
                "angle_unit": "gon",
                "distance_unit": "m",
            },
            "config": {
                "distance_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "distance_back_forward_tolerance"
                    ]
                ),
                "direction_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "direction_back_forward_tolerance"
                    ]
                ),
                "vertical_back_forward_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "vertical_back_forward_tolerance"
                    ]
                ),
                "angular_closure_tolerance": float(
                    config["common"]["traverse_tolerances"]["angular_closure_tolerance"]
                ),
                "planimetric_closure_tolerance": float(
                    config["common"]["traverse_tolerances"][
                        "planimetric_closure_tolerance"
                    ]
                ),
                "compute_z": self.compute_z,
                "linear_scale_factor": self.k_alteration_lineaire,
            },
            "calcul": to_compute_array[1 : len(to_compute_array) - 1],
            "stations_sequence": to_compute_array,
            "corrections": {"k_alteration_lineaire": 1.0, "niveau_apparent": True},
            "stations": [],
            "obs": [],
            "stdev": {
                "ah_mgon": 1.0,  # écart-type dir. horizontale en mgon
                "av_mgon": 1.5,  # écart-type zénithal en mgon
                "di_mm": 2.0,  # composante fixe (mm)
                "di_ppm": 2.0,  # composante proportionnelle (ppm)
            },
        }

        stations = TopazeUtils.find_all_stations_in_obs_array(obs_array)
        for station in stations:
            if station.matricule in list(set(to_compute_array + reference_array)):
                dico = {"matricule": station.matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    station.matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        station.matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                data_dict["stations"].append(dico)

            reference_matricules = (
                TopazeUtils.find_all_reference_matricules_in_obs_array(
                    obs_array, station.matricule
                )
            )
            for matricule in reference_matricules:
                dico = {"matricule": matricule}
                pt = TopazeUtils.get_ptopo_by_matricule_in_obs_array(
                    matricule, obs_array
                )
                if pt is None:
                    pt = TopazeUtils.get_ptopo_by_matricule(
                        matricule, self._ptopo_array, self._ptopo_layer
                    )
                if pt:
                    if not pt.z or pt.z <= PtopoConst.UNKNOWN_Z:
                        dico.update({"x": pt.x, "y": pt.y, "z": PtopoConst.NO_Z})
                    else:
                        dico.update({"x": pt.x, "y": pt.y, "z": pt.z})
                else:
                    dico.update(
                        {
                            "x": PtopoConst.NO_X,
                            "y": PtopoConst.NO_Y,
                            "z": PtopoConst.NO_Z,
                        }
                    )
                if dico not in data_dict["stations"]:
                    data_dict["stations"].append(dico)
            if station.matricule in to_compute_array:
                references = TopazeUtils.find_all_references_in_obs_array(
                    obs_array, station.matricule
                )
            else:
                references = []
            for ref in references:
                if ref.matricule in reference_matricules:
                    dico = {"origine": station.matricule, "hi": station.hi}
                    if station.matricule and ref.matricule in to_compute_array:
                        sight_type = "traverse"
                    else:
                        sight_type = "reference"
                    if ref.av is None or ref.av < 200.0:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": None,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CG",
                            }
                        )
                    else:
                        dico.update(
                            {
                                "origine": station.matricule,
                                "type": sight_type,
                                "occurrence": 1,
                                "hi": station.hi,
                                "cible": ref.matricule,
                                "v0": None,
                                "ah": ref.ah,
                                "av": ref.av,
                                "di": ref.di,
                                "hp": ref.hp,
                                "face": "CD",
                            }
                        )
                    data_dict["obs"].append(dico)
        data_s = json.dumps(data_dict, indent=4)
        fullfilepath = FileUtils.create_temp_file(tmp_filename, data_s)
        return fullfilepath

    def clear_coordinates_fields(self):
        self.lineEdit_x_raw.setText("")
        self.lineEdit_y_raw.setText("")
        self.lineEdit_z_raw.setText("")
