WME UT Kadastrs

WME UrSuS Tools: LV Kadastrs

// ==UserScript==
// @name        WME UT Kadastrs
// @namespace   http://ursus.id.lv
// @version     1.0.1
// @description WME UrSuS Tools: LV Kadastrs
// @author      UrSuS
// @match       https://*.waze.com/*editor*
// @license     MIT/BSD/X11
// @icon        data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB3aWR0aD0iNjMuOTk5OTk2bW0iCiAgIGhlaWdodD0iNjQuMDAwMDE1bW0iCiAgIHZpZXdCb3g9IjAgMCA2My45OTk5OTYgNjQuMDAwMDE1IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmc1IgogICBpbmtzY2FwZTp2ZXJzaW9uPSIxLjIgKGRjMmFlZGFmMDMsIDIwMjItMDUtMTUpIgogICBzb2RpcG9kaTpkb2NuYW1lPSJ3bWUga2FkYXN0cnMuc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0ibmFtZWR2aWV3NyIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBpbmtzY2FwZTpkZXNrY29sb3I9IiNkMWQxZDEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9Im1tIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp6b29tPSIxLjAzMzA4MjIiCiAgICAgaW5rc2NhcGU6Y3g9IjY3Ljc1ODQwMSIKICAgICBpbmtzY2FwZTpjeT0iNzU3LjQ0MjEzIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDA1IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2d1aWRlcz0idHJ1ZSI+CiAgICA8c29kaXBvZGk6Z3VpZGUKICAgICAgIHBvc2l0aW9uPSIzMi4wMDAwMDMsMzIuMDAwMDA3IgogICAgICAgb3JpZW50YXRpb249IjEsMCIKICAgICAgIGlkPSJndWlkZTgzMCIKICAgICAgIGlua3NjYXBlOmxvY2tlZD0iZmFsc2UiIC8+CiAgICA8c29kaXBvZGk6Z3VpZGUKICAgICAgIHBvc2l0aW9uPSIzMi4wMDAwMDEsMzIuMDAwMDA5IgogICAgICAgb3JpZW50YXRpb249IjAsLTEiCiAgICAgICBpZD0iZ3VpZGU4MzIiCiAgICAgICBpbmtzY2FwZTpsb2NrZWQ9ImZhbHNlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyIiAvPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC01NC40MzczMywtMTczLjI3MjY1KSI+CiAgICA8cmVjdAogICAgICAgc3R5bGU9ImZpbGw6I2EwMmMyYztzdHJva2Utd2lkdGg6MjYuNDU4MyIKICAgICAgIGlkPSJyZWN0ODE4IgogICAgICAgd2lkdGg9IjY0IgogICAgICAgaGVpZ2h0PSI2NCIKICAgICAgIHg9IjU0LjQzNzMzMiIKICAgICAgIHk9IjE3My4yNzI2NiIgLz4KICAgIDxnCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjE5MzY3MDgsMCwwLDEuMTkzNjcwOCw1Ni42Mzc5MTgsMTc1LjM4OTExKSIKICAgICAgIGlkPSJnODI0Ij4KICAgICAgPHBhdGgKICAgICAgICAgZD0ibSAyNy41LDIuNjc0IGMgLTYuMTgxLDAgLTExLjk0NCwyLjc3NyAtMTUuODMzLDcuNzA4IEMgOC45NTgsMTMuODU0IDcuNSwxOC4xNiA3LjUsMjIuNTM1IHYgMy42OCBDIDcuNSwyNy44MTIgNi44NzUsMjkuMzQgNS44MzMsMzAuNDUxIDUsMzEuMjg1IDMuOTU4LDMxLjkxIDIuODQ3LDMyLjE4OCBjIDAuNDE3LDEuMDQxIDEuMzg5LDIuNjM4IDMuMTI1LDQuMzc1IDEuNDU5LDEuNTI3IDMuMTk1LDIuNzc3IDUuMDcsMy42OCB2IC0wLjA2OSBjIDEuMTgsLTEuODA2IDMuMTI1LC0yLjg0OCA1LjI3NywtMi44NDggMC40MTcsMCAwLjc2NCwwLjA3IDEuMTgxLDAuMTM5IDIuNSwwLjQ4NiA0LjQ0NCwyLjQzMSA0LjkzMSw0Ljg2MSBoIDUuMTM4IGMgNS4zNDgsMCAxMC40MTcsLTIuMjIyIDE0LjA5OCwtNS44MzMgNS42OTQsLTUuNjk0IDcuNDMsLTE0LjIzNiA0LjMwNSwtMjEuNTk3IEMgNDIuODQ3LDcuNDY1IDM1LjYyNSwyLjY3NCAyNy41LDIuNjc0IFoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNhMDJjMmMiCiAgICAgICAgIGlkPSJwYXRoODIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBkPSJNIDI3LjUsMC4xNzQgQyAyMC42MjUsMC4xNzQgMTQuMTY3LDMuMjI5IDkuNzkyLDguNzE1IDYuNjY3LDEyLjY3NCA1LDE3LjUzNSA1LDIyLjYwNCB2IDMuNjExIGMgMCwxLjg3NSAtMS4zMTksMy42MTEgLTMuODg5LDMuNzUgLTAuNjI1LDAgLTEuMTExLDAuNDg2IC0xLjE4LDEuMTExIC0wLjA3LDEuNjY3IDEuNzM2LDQuNzkyIDQuMjM2LDcuMjkyIDEuNzM2LDEuNzM2IDMuNzUsMy4xMjUgNS45MDIsNC4yMzYgLTAuNjk0LDMuODIgMi4yOTIsNy4yOTIgNi4xODEsNy4yOTIgaCAwLjA2OSBjIDIuOTg3LDAgNS40ODcsLTIuMDgzIDYuMTEyLC00LjkzMSBoIDUuMjA4IGMgMC41NTUsMi44NDggMy4wNTUsNC45MzEgNi4xMTEsNC45MzEgMC42OTQsMCAxLjQ1OCwtMC4xMzkgMi4xNTMsLTAuMzQ3IDEuNzM2LC0wLjU1NiAzLjA1NSwtMS44NzUgMy42OCwtMy42MTIgMC41NTYsLTEuNTk3IDAuNDg2LC0zLjE5NCAwLC00LjUxMyAxLjM4OSwtMC45MDMgMi42MzksLTEuODc1IDMuODIsLTMuMDU2IEMgNDcuNjM5LDM0LjIwMSA1MCwyOC41MDcgNTAsMjIuNjA0IDUwLDE2LjYzMiA0Ny42MzksMTEuMDc2IDQzLjQwMyw2Ljg0IDM5LjE2NywyLjQ2NSAzMy40NzIsMC4xNzQgMjcuNSwwLjE3NCBaIG0gMCwyLjUgYyA4LjA1NiwwIDE1LjM0Nyw0Ljg2MSAxOC40NzIsMTIuMjkxIDMuMTI1LDcuNDMxIDEuMzg5LDE1Ljk3MiAtNC4zMDUsMjEuNTk4IC0zLjY4MSwzLjY4IC04Ljc1LDUuODMzIC0xNC4wOTgsNS44MzMgSCAyMi40MzEgQyAyMS45NDQsMzkuODk2IDIwLDM4LjAyMSAxNy41LDM3LjUzNSBjIC0wLjQxNywtMC4wNyAtMC43NjQsLTAuMTM5IC0xLjE4MSwtMC4xMzkgLTIuMDgzLDAgLTQuMDk3LDEuMDQyIC01LjI3NywyLjg0NyB2IDAuMDcgQyA5LjE2NywzOS4zNCA3LjUsMzguMDkgNS45NzIsMzYuNjMyIDQuMjM2LDM0Ljg5NiAzLjI2NCwzMy4yMjkgMi44NDcsMzIuMjU3IDQuMDI4LDMxLjk3OSA1LDMxLjM1NCA1LjgzMywzMC41MjEgNi44NzUsMjkuMzQgNy41LDI3Ljg4MiA3LjUsMjYuMjg1IHYgLTMuNjgxIGMgMCwtNC4zNzUgMS40NTgsLTguNjggNC4xNjcsLTEyLjE1MyBDIDE1LjU1Niw1LjM4MiAyMS4zMTksMi42NzQgMjcuNSwyLjY3NCBaIgogICAgICAgICBpZD0icGF0aDgyMiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIgLz4KICAgIDwvZz4KICAgIDxnCiAgICAgICBhcmlhLWxhYmVsPSJVVCIKICAgICAgIGlkPSJ0ZXh0ODI4IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTozMC42NzMycHg7bGluZS1oZWlnaHQ6MS4yNTtmb250LWZhbWlseTpXYXplOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246V2F6ZTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjAuMTE1MDI0Ij4KICAgICAgPHBhdGgKICAgICAgICAgZD0ibSA4OC4zMTk3NzQsMjA4LjEyNTI2IHEgMCwyLjc5MTI2IC0xLjI1NzYwMiw0LjYwMDk4IC0xLjI1NzYwMSwxLjc3OTA1IC0zLjI4MjAzMiwyLjYzNzkgLTIuMDI0NDMxLDAuODU4ODUgLTQuMzU1NTk1LDAuODU4ODUgLTIuMzAwNDksMCAtNC4zMjQ5MjEsLTAuODU4ODUgLTIuMDI0NDMxLC0wLjg1ODg1IC0zLjI4MjAzMiwtMi42Mzc5IC0xLjI1NzYwMiwtMS43NzkwNCAtMS4yNTc2MDIsLTQuNTcwMyB2IC0xMy4xODk0OCBxIDAsLTAuMjQ1MzggMC4xODQwNCwtMC40Mjk0MiAwLjE4NDAzOSwtMC4yMTQ3MiAwLjQ2MDA5OCwtMC4yMTQ3MiBoIDIuNzkxMjYxIHEgMC4yNzYwNTksMCAwLjQ2MDA5OCwwLjIxNDcyIDAuMjE0NzEyLDAuMTg0MDQgMC4yMTQ3MTIsMC40Mjk0MiB2IDEyLjkxMzQyIHEgMCwyLjE0NzEyIDEuMjU3NjAyLDMuMzc0MDUgMS4yNTc2MDEsMS4yMjY5MyAzLjQ5Njc0NCwxLjIyNjkzIDIuMjY5ODE3LDAgMy41Mjc0MTksLTEuMjI2OTMgMS4yNTc2MDEsLTEuMjI2OTMgMS4yNTc2MDEsLTMuMzc0MDUgdiAtMTIuOTEzNDIgcSAwLC0wLjI0NTM4IDAuMTg0MDM5LC0wLjQyOTQyIDAuMjE0NzEyLC0wLjIxNDcyIDAuNDkwNzcxLC0wLjIxNDcyIGggMi43OTEyNjEgcSAwLjI0NTM4NiwwIDAuNDI5NDI1LDAuMjE0NzIgMC4yMTQ3MTMsMC4xODQwNCAwLjIxNDcxMywwLjQyOTQyIHoiCiAgICAgICAgIGlkPSJwYXRoODc4IiAvPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDkwLjQzNjIxNSwxOTcuNDUwOTkgdiAtMi40NTM4NiBxIDAsLTAuMjc2MDUgMC4xODQwNCwtMC40NjAwOSAwLjE4NDAzOSwtMC4yMTQ3MiAwLjQ2MDA5OCwtMC4yMTQ3MiBoIDE2Ljk5Mjk1NyBxIDAuMjc2MDUsMCAwLjQ2MDA5LDAuMjE0NzIgMC4xODQwNCwwLjE4NDA0IDAuMTg0MDQsMC40NjAwOSB2IDIuNDUzODYgcSAwLDAuMjc2MDYgLTAuMTg0MDQsMC40NjAxIC0wLjE4NDA0LDAuMTg0MDQgLTAuNDYwMDksMC4xODQwNCBoIC02LjM4MDAzIHYgMTcuMDIzNjIgcSAwLDAuMjc2MDYgLTAuMjE0NzEsMC40OTA3NyAtMC4xODQwNCwwLjE4NDA0IC0wLjQ2MDEsMC4xODQwNCBoIC0yLjkxMzk1NCBxIC0wLjI3NjA1OSwwIC0wLjQ2MDA5OCwtMC4xODQwNCAtMC4xODQwNCwtMC4yMTQ3MSAtMC4xODQwNCwtMC40OTA3NyB2IC0xNy4wMjM2MiBoIC02LjM4MDAyNSBxIC0wLjI3NjA1OSwwIC0wLjQ2MDA5OCwtMC4xODQwNCAtMC4xODQwNCwtMC4xODQwNCAtMC4xODQwNCwtMC40NjAxIHoiCiAgICAgICAgIGlkPSJwYXRoODgwIiAvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg==
// @exclude     https://www.waze.com/user/editor*
// @require     https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @require     https://greasyfork.org/scripts/383120-proj4-wazedev/code/proj4-Wazedev.js
// @connect     lvmgeoserver.lvm.lv
// @connect     docs.google.com
// @grant       GM_info
// @grant       GM_xmlhttpRequest
// ==/UserScript==

/* globals proj4 */

(function (proj4) {
    'use strict';

    const sScriptName = GM_info.script.name;
    GM_info.script.version;
    let wmeSDK;
    let aGlobalFetchedAddresses = [];
    let aWMEValidAddressVenues = [];
    let aWMEPolygonHighlightFeatures = [];
    let aAddressConnectionLinesFeatures = [];
    let aWMEPolygonHighlightBlueFeatures = [];
    let aMissingFetchedAddresses = [];
    let aHoverFeatures = [];
    let iPopupTimeout;
    let $KadastrsMenuPopupDiv;
    let oAddHouseNumber;
    let aVasarnicas = [];
    function fetchSheetData() {
        const SHEET_ID = "1W230EX3e0ECeF44Ls0GtvI_Iwn9tceidJYT7T4vjJJw";
        const SHEET_NAME = "Sheet1";
        const url = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/gviz/tq?tqx=out:csv&sheet=${SHEET_NAME}`;
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: response => {
                if (response.status === 200) {
                    const csvData = response.responseText;
                    const aRows = csvData.trim().split("\n");
                    const aParsedResult = aRows.slice(1).map(row => {
                        const aCols = row.split(",");
                        return {
                            name: aCols[0].trim().replace(/^"(.*)"$/, "$1"),
                            parent: aCols[1].trim().replace(/^"(.*)"$/, "$1")
                        };
                    });
                    aVasarnicas = aParsedResult;
                }
                else {
                    console.error("Failed to fetch data:", response);
                }
            }
        });
    }
    function createElem(type, attrs = {}, eventListener = {}) {
        const oElement = document.createElement(type);
        for (const [sKey, vValue] of Object.entries(attrs)) {
            if (sKey in oElement) {
                oElement[sKey] = vValue;
            }
            else if (sKey === "classList" && Array.isArray(vValue)) {
                oElement.classList.add(...vValue);
            }
            else {
                oElement.setAttribute(sKey, vValue);
            }
        }
        for (const [sEventType, fnHandler] of Object.entries(eventListener)) {
            if (fnHandler) {
                oElement.addEventListener(sEventType, fnHandler);
            }
        }
        return oElement;
    }
    function getOLMapExtent() {
        let extent = W.map.getExtent();
        if (Array.isArray(extent)) {
            extent = new OpenLayers.Bounds(extent);
            extent.transform("EPSG:4326", "EPSG:3857");
        }
        return extent;
    }
    function addKFetchButton() {
        const divOverlayMain = document.getElementById("overlay-buttons-region");
        if (!divOverlayMain) {
            return;
        }
        const mStyle = {
            position: "absolute",
            top: "0px",
            right: "60px",
            width: "44px",
            zIndex: "1"
        };
        const mainDiv = createElem("div");
        Object.assign(mainDiv.style, mStyle);
        const btnDiv = createElem("div", {
            classList: ["overlay-buttons-container top"]
        });
        const owz = createElem("wz-button", {
            color: "clear-icon",
            classList: ["overlay-button"],
            disabled: "false"
        }, { click: fetchKadastrsData });
        const h6 = createElem("h6", {
            classList: ["w-icon"],
            textContent: "K"
        });
        h6.style["font-family"] = "Waze Boing Medium";
        h6.style["line-height"] = "24px";
        owz.appendChild(h6);
        btnDiv.appendChild(owz);
        mainDiv.appendChild(btnDiv);
        divOverlayMain.appendChild(mainDiv);
    }
    function initScript() {
        if (!unsafeWindow.getWmeSdk) {
            throw new Error("SDK not available");
        }
        wmeSDK = unsafeWindow.getWmeSdk({
            scriptId: "wmeUTKadastrs",
            scriptName: "UrSuS Tools: Kadastrs"
        });
        if (typeof proj4 === "undefined") {
            const oScript = document.createElement("script");
            oScript.type = "text/javascript";
            oScript.src = "https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js";
            document.getElementsByTagName("head")[0].appendChild(oScript);
        }
        oAddHouseNumber = require("Waze/Actions/AddHouseNumber");
        addKFetchButton();
        fetchSheetData();
    }
    void unsafeWindow.SDK_INITIALIZED.then(initScript);
    async function fetchKadastrsData() {
        wazedevtoastr.options = {
            closeButton: false,
            debug: false,
            newestOnTop: false,
            progressBar: false,
            rtl: false,
            positionClass: "toast-bottom-center",
            preventDuplicates: false,
            onclick: null,
            showDuration: 300,
            hideDuration: 1000,
            timeOut: 5000,
            extendedTimeOut: 1000,
            showEasing: "swing",
            hideEasing: "linear",
            showMethod: "fadeIn",
            hideMethod: "fadeOut"
        };
        if (wmeSDK.Map.getZoomLevel() < 17) {
            WazeWrap.Alerts.warning(sScriptName, `You are on ${wmeSDK.Map.getZoomLevel()} Zoom Level, HouseNumbers won't be fetched!`);
        }
        WazeWrap.Alerts.info(sScriptName, "KadastrsAddress Fetching started");
        let sURL = "https://lvmgeoserver.lvm.lv/geoserver/publicwfs/wfs?layer=publicwfs&SERVICE=WFS&REQUEST=GetFeature";
        sURL +=
            "&VERSION=2.0.0&TYPENAMES=publicwfs:arisbuilding&STARTINDEX=0&COUNT=500&SRSNAME=urn:ogc:def:crs:EPSG::3059&BBOX=";
        sURL += getBBox3059();
        sURL += ",urn:ogc:def:crs:EPSG::3059&outputFormat=application/json";
        const sVenueResponseJSON = await makeGetRequest(sURL);
        processFetchedDataNewMode(sVenueResponseJSON);
    }
    async function processFetchedDataNewMode(responseText) {
        const oParser = new OpenLayers.Format.GeoJSON();
        aGlobalFetchedAddresses = oParser.read(responseText);
        WazeWrap.Alerts.info(sScriptName, `${aGlobalFetchedAddresses.length} Kadastrs Address Fetched`);
        let sWazeVenueURL = `https://${window.location.hostname}/row-Descartes/app/Features?bbox=${getBBoxv2()}`;
        sWazeVenueURL += "&language=en-GB&v=2&venueLevel=4&venueFilter=1%2C1%2C0%2C0";
        const sVenueResponseJSON = await makeGetRequest(sWazeVenueURL);
        const oParsedObject = JSON.parse(sVenueResponseJSON);
        let aWazeVenues = oParsedObject.venues.objects;
        aWMEValidAddressVenues = [];
        WazeWrap.Alerts.info(sScriptName, `${aWazeVenues.length} WME Address Fetched`);
        let aWazeHouseNumbers = [];
        if (wmeSDK.Map.getZoomLevel() >= 17) {
            const aSegments = W.model.segments
                .getObjectArray()
                .filter((oObject) => oObject.getAttribute("hasHNs"));
            const oMapExtGeometry = getOLMapExtent().toGeometry();
            const aSegmentsOnScreen = aSegments
                .map((oSegment) => {
                if (oMapExtGeometry.intersects(oSegment.getAttribute("geometry"))) {
                    return oSegment.getID();
                }
            })
                .filter((oItem) => !!oItem);
            const oResults = await W.controller.descartesClient.getHouseNumbers(aSegmentsOnScreen);
            aWazeHouseNumbers = oResults.segmentHouseNumbers.objects;
        }
        aMissingFetchedAddresses = [...aGlobalFetchedAddresses];
        aWazeVenues.map((oVenue) => {
            let { name: sStreetName, city: sCity } = findStreetName(oVenue.streetID);
            let oAddressRegex;
            if (!oVenue.houseNumber && sStreetName === "") {
                if (oVenue.name) {
                    const sConverted = `"${oVenue.name.replace(", ", '", ')}${sCity !== "" ? '"' : ""}`;
                    oAddressRegex = getRegex([sConverted, sCity]);
                }
            }
            else if (!oVenue.houseNumber) {
                if (sStreetName.slice(-4) === "pag.") {
                    const aAddress = sStreetName.split(", ");
                    sStreetName = `"${aAddress[0]}", ${aAddress[1]}`;
                }
                else {
                    sStreetName = `"${sStreetName}"`;
                }
                oAddressRegex = getRegex([sStreetName, sCity]);
            }
            else {
                const sHouseNumber = oVenue.houseNumber.toUpperCase().replace("-", " k-");
                const bIsVasarnica = aVasarnicas.some(mRecord => mRecord.name === sStreetName && mRecord.parent === sCity);
                if (bIsVasarnica) {
                    oAddressRegex = getRegex([`"${sStreetName} ${sHouseNumber}"`, sCity]);
                }
                else {
                    oAddressRegex = getRegex([`${sStreetName} ${sHouseNumber}`, sCity]);
                }
            }
            if (oAddressRegex) {
                excludeKadastrsAddressConsistentWithWME(oAddressRegex, oVenue);
            }
        });
        WazeWrap.Alerts.info(sScriptName, `${aMissingFetchedAddresses.length} Kadastrs Addresses Not Present`);
        const aConvertedAddresses = aGlobalFetchedAddresses
            .map(mRowKadastrsAddress => {
            const mKadastrsAddress = convertKadastrsAddressStringToParts(mRowKadastrsAddress.attributes.std, "");
            if (!mKadastrsAddress) {
                return undefined;
            }
            return {
                cityName: mKadastrsAddress.cityName,
                streetName: mKadastrsAddress.streetName,
                houseNumber: mKadastrsAddress.houseNumber,
                name: mKadastrsAddress.name,
                cityId: "",
                streetID: ""
            };
        })
            .filter(oItem => !!oItem);
        const iFailedAddressIndex = populateIds(aConvertedAddresses);
        if (iFailedAddressIndex) {
            const mMissingAddress = aGlobalFetchedAddresses[iFailedAddressIndex];
            const aLonLat = proj4("EPSG:3059", "EPSG:4326", [mMissingAddress.geometry.x, mMissingAddress.geometry.y]);
            wmeSDK.Map.setMapCenter({
                lonLat: { lon: aLonLat[0], lat: aLonLat[1] },
                zoomLevel: 19
            });
        }
        aConvertedAddresses.forEach(mConvertedAddress => {
            const sHN = mConvertedAddress.houseNumber.includes(" k-")
                ? mConvertedAddress.houseNumber.replace(" k-", "-")
                : mConvertedAddress.houseNumber;
            aWazeVenues = aWazeVenues.filter(mVenue => mVenue.streetID !== mConvertedAddress.streetID || mVenue.houseNumber?.toUpperCase() !== sHN);
        });
        WazeWrap.Alerts.info(sScriptName, `${aWazeVenues.length} WME Venues with incorrect or missing Addresses`);
        aWazeHouseNumbers.map(oWazeHouseNumber => {
            const mSegStreet = W.model.segments.getObjectById(oWazeHouseNumber.attributes.segID).getAddress().attributes
                ?.street?.attributes;
            const { name: sStreetName, city: sCity } = findStreetName(mSegStreet.id);
            const oAddressRegex = getRegex([sStreetName + " " + oWazeHouseNumber.attributes.number.toUpperCase(), sCity]);
            excludeKadastrsAddressConsistentWithWME(oAddressRegex, oWazeHouseNumber);
        });
        addKadastrsLayerWithFeatures(aGlobalFetchedAddresses);
        clearHighlightedAdressFeatures();
        let bIsVenueLayerHidden = false;
        aWazeVenues.forEach(oVenue => {
            const OLVenue = W.map.venueLayer.getOlFeatureByFeatureId(oVenue.id);
            if (!OLVenue) {
                bIsVenueLayerHidden = true;
                return;
            }
            highlightVenue2(OLVenue);
        });
        aWMEValidAddressVenues.forEach(oVenue => {
            const OLVenue = W.map.venueLayer.getOlFeatureByFeatureId(oVenue.id);
            if (!OLVenue) {
                bIsVenueLayerHidden = true;
                return;
            }
            highlightVenue2(OLVenue, true);
        });
        if (bIsVenueLayerHidden) {
            WazeWrap.Alerts.warning(sScriptName, "Please activate Venues or Residential Layer");
        }
        setTimeout(() => W.map.getLayerByUniqueName("venues").redraw(), 0);
    }
    function addKadastrsLayerWithFeatures(aKadastrsFeatures) {
        const aFeaturesForLayer = [];
        const pointLayer = new OpenLayers.Layer.Vector("LVWMEKadastrsPointLayer", {
            rendererOptions: {
                zIndexing: true
            }
        });
        const oExistingLayer = W.map.getLayersByName("LVWMEKadastrsPointLayer")[0];
        const layerContainer = W.selectionManager.getWebMapSelectionMediator().getRootContainerLayer();
        if (oExistingLayer) {
            oExistingLayer.destroyFeatures();
            W.map.removeLayer(oExistingLayer);
            const indexToRemove = W.map.layers.indexOf(oExistingLayer);
            if (indexToRemove !== -1) {
                layerContainer.layers.splice(indexToRemove, 1);
            }
        }
        aKadastrsFeatures.forEach(mFeature => {
            const aCoords = proj4("EPSG:3059", "EPSG:900913", [mFeature.geometry.x, mFeature.geometry.y]);
            mFeature.geometry.x = aCoords[0];
            mFeature.geometry.y = aCoords[1];
            const point = new OpenLayers.Geometry.Point(aCoords[0], aCoords[1]);
            const featureStyle = {
                label: "K",
                pointRadius: 15,
                fillColor: "#00695C",
                fillOpacity: 0.8,
                strokeColor: "#cc6633",
                strokeWidth: 2,
                strokeOpacity: 0.8,
                fontColor: "black",
                labelOutlineColor: "white",
                labelOutlineWidth: 3,
                repositoryObject: {
                    isDeleted: () => false,
                    isNew: () => false,
                    getType: () => null,
                    getID: () => -1
                }
            };
            const feature = new OpenLayers.Feature.Vector(point, null, featureStyle);
            feature.data = mFeature;
            aFeaturesForLayer.push(feature);
        });
        pointLayer.addFeatures(aFeaturesForLayer);
        const selectControl = new OpenLayers.Control.SelectFeature(pointLayer, {
            hover: true,
            onSelect: onFeatureHoverEvent,
            onUnselect: onFeatureUnHoverEvent
        });
        W.map.addControl(selectControl);
        selectControl.activate();
        W.map.addLayer(pointLayer);
        layerContainer.layers.push(pointLayer);
        layerContainer.collectRoots();
    }
    function checkTooltip() {
        window.clearTimeout(iPopupTimeout);
    }
    function onFeatureHoverEvent(e) {
        window.clearTimeout(iPopupTimeout);
        const mKadastrsAddress = convertKadastrsAddressStringToParts(e.data.attributes.std, "waze");
        if (!mKadastrsAddress) {
            return;
        }
        const placeGeom = e.geometry.getCentroid();
        const placePt = new OpenLayers.Geometry.Point(placeGeom.x, placeGeom.y);
        if (!$KadastrsMenuPopupDiv) {
            $KadastrsMenuPopupDiv = createElem("div", {
                id: "kadastrsMenuDiv",
                style: "z-index:9998; visibility:visible; position:absolute; margin: 0px; top: 0px; left: 0px;",
                "data-tippy-root": false
            }, { mouseenter: checkTooltip, mouseleave: hideTooltipAfterDelay });
            Object.assign($KadastrsMenuPopupDiv.style, {
                zIndex: 9998,
                visibility: "visible",
                position: "absolute",
                margin: "0px",
                top: "0px",
                left: "0px"
            });
            W.map.getEl()[0].appendChild($KadastrsMenuPopupDiv);
        }
        const divElemRoot = createElem("div", {
            id: "kadastrsMenuDiv-tooltip",
            classList: ["tippy-box"],
            "data-state": "hidden",
            tabindex: "-1",
            "data-theme": "light-border",
            "data-animation": "fade",
            role: "tooltip",
            "data-placement": "top",
            style: "max-width: 350px; transition-duration:300ms;"
        });
        const divTippyContent = createElem("div", {
            id: "kadastrsMenuDiv-content",
            classList: ["tippy-content"],
            "data-state": "hidden",
            style: "transition-duration: 300ms;"
        });
        const oAddressTextDiv = createElem("div", {
            classList: ["coordinates-wrapper"]
        });
        const oVenuesTextDiv = createElem("div", {
            classList: ["coordinates-wrapper"],
            style: "white-space: pre-wrap;"
        });
        let sVenues = "❌";
        if (e.data.data.Venues) {
            sVenues = e.data.data.Venues.map((oVenue) => oVenue.name).join("\r\n&bull;");
            if (e.data.data.Venues.length > 1) {
                sVenues = "\r\n&bull;" + sVenues + "\r\n";
            }
        }
        oVenuesTextDiv.innerHTML = `Venues: ${sVenues} \n Residential: ${(e.data.data.Residential ?? []).length === 1 ? "✅" : e.data.data.Residential ?? "❌"} HN: ${(e.data.data.HN ?? []).length === 1 ? "✅" : e.data.data.HN ?? "❌"}`;
        divTippyContent.appendChild(oVenuesTextDiv);
        const oInputForm = createElem("div", {
            classList: ["wz-text-input-inner-container"]
        });
        const oInputInput = createElem("wz-text-input", {
            value: e.data.attributes.std
        });
        oInputInput.appendChild(oInputForm);
        oAddressTextDiv.appendChild(oInputInput);
        divTippyContent.appendChild(oAddressTextDiv);
        const mSDKSelection = wmeSDK.Editing.getSelection();
        if (mSDKSelection?.ids.length === 1) {
            const oApplyAddressForm = createElem("div", {
                classList: ["external-providers-control", "form-group"]
            });
            divTippyContent.appendChild(oApplyAddressForm);
            const oApplyAddressFormLabel = createElem("wz-label", {
                "html-for": ""
            });
            oApplyAddressFormLabel.innerText = "Apply Address to selected Venue:";
            oApplyAddressForm.appendChild(oApplyAddressFormLabel);
            const fnFullyApplyToSelectedClick = () => applyAddress(e, "full");
            const oWZButtonFullyApplyToSelectedPlace = createElem("wz-button", {
                color: "secondary",
                size: "sm",
                classList: ["overlay-button"],
                disabled: "false",
                textContent: "Full"
            }, { click: fnFullyApplyToSelectedClick });
            const oWZIconFullyApplyToSelectedPlace = createElem("i", {
                classList: ["w-icon w-icon-location-check-fill"],
                style: "font-size:18px;"
            });
            oWZButtonFullyApplyToSelectedPlace.prepend(oWZIconFullyApplyToSelectedPlace);
            oApplyAddressForm.appendChild(oWZButtonFullyApplyToSelectedPlace);
            const fnApplyKeepingNameToSelectedClick = () => applyAddress(e, "");
            const oWZButtonApplyKeepingNameToSelectedPlace = createElem("wz-button", {
                color: "secondary",
                size: "sm",
                classList: ["overlay-button"],
                disabled: "false",
                textContent: "Keep Name"
            }, { click: fnApplyKeepingNameToSelectedClick });
            const oWZIconApplyKeepingNameToSelectedPlace = createElem("i", {
                classList: ["w-icon w-icon-location-fill"],
                style: "font-size: 18px;"
            });
            oWZButtonApplyKeepingNameToSelectedPlace.prepend(oWZIconApplyKeepingNameToSelectedPlace);
            oApplyAddressForm.appendChild(oWZButtonApplyKeepingNameToSelectedPlace);
            const fnPasteViensetaAddressClick = () => applyAddress(e, "vienseta");
            const oWZButtonApplyAsViensetaToSelectedPlace = createElem("wz-button", {
                color: "secondary",
                size: "sm",
                classList: ["overlay-button"],
                disabled: "false",
                textContent: "Keep Name adding Vienseta"
            }, { click: fnPasteViensetaAddressClick });
            const oWZIconApplyAsViensetaToSelectedPlace = createElem("i", {
                classList: ["w-icon w-icon-home"],
                style: "font-size: 18px;"
            });
            oWZButtonApplyAsViensetaToSelectedPlace.prepend(oWZIconApplyAsViensetaToSelectedPlace);
            oApplyAddressForm.appendChild(oWZButtonApplyAsViensetaToSelectedPlace);
        }
        const oCreateVenueForm = createElem("div", {
            classList: ["external-providers-control", "form-group"]
        });
        divTippyContent.appendChild(oCreateVenueForm);
        const oCreateVenueFormLabel = createElem("wz-label", {
            "html-for": ""
        });
        oCreateVenueFormLabel.innerText = "Create Venue:";
        oCreateVenueForm.appendChild(oCreateVenueFormLabel);
        if (!e.data.data.Residential) {
            const fnCreateResidentialClick = () => createResidential(e);
            const oWZIconCreateResidential = createElem("i", {
                classList: ["w-icon w-icon-navigation-now-fill"],
                style: "font-size:18px;"
            });
            const oWZButtonCreateResidential = createElem("wz-button", {
                color: "secondary",
                size: "sm",
                classList: ["overlay-button"],
                disabled: "false",
                textContent: "Residential"
            }, { click: fnCreateResidentialClick });
            oWZButtonCreateResidential.prepend(oWZIconCreateResidential);
            oCreateVenueForm.appendChild(oWZButtonCreateResidential);
        }
        if (!e.data.data.HN) {
            const fnCreateHNClick = () => createHN(e);
            const oWZButtonCreateHN = createElem("wz-button", {
                color: "secondary",
                size: "sm",
                classList: ["overlay-button"],
                disabled: "false",
                textContent: "HN"
            }, { click: fnCreateHNClick });
            const oWZIconCreateHN = createElem("i", {
                classList: ["w-icon w-icon-home"],
                style: "font-size:18px;"
            });
            oWZButtonCreateHN.prepend(oWZIconCreateHN);
            oCreateVenueForm.appendChild(oWZButtonCreateHN);
        }
        const fnForceCreateClick = () => createVenue(e);
        const oWZButtonCreatePlace = createElem("wz-button", {
            color: "secondary",
            size: "sm",
            classList: ["overlay-button"],
            disabled: "false",
            textContent: `${e.data.data.Venues ? "Force Create Place" : "Place"}`
        }, { click: fnForceCreateClick });
        const oWZIconCreatePlace = createElem("i", {
            classList: ["w-icon w-icon-polygon"],
            style: "font-size:18px;"
        });
        oWZButtonCreatePlace.prepend(oWZIconCreatePlace);
        oCreateVenueForm.appendChild(oWZButtonCreatePlace);
        divElemRoot.appendChild(divTippyContent);
        $KadastrsMenuPopupDiv.replaceChildren(divElemRoot);
        const aLonLat = proj4("EPSG:900913", "EPSG:4326", [placeGeom.x, placeGeom.y]);
        const mPopupPixelPosition = W.map.getPixelFromLonLat({
            lon: aLonLat[0],
            lat: aLonLat[1]
        });
        mPopupPixelPosition.origX = mPopupPixelPosition.x;
        $KadastrsMenuPopupDiv.clientWidth / 2;
        const dataPlacement = "right";
        $KadastrsMenuPopupDiv.style.transform = `translate(${Math.round(mPopupPixelPosition.x + 24)}px, ${Math.round(mPopupPixelPosition.y - 24)}px)`;
        $KadastrsMenuPopupDiv.querySelector("#kadastrsMenuDiv-tooltip")?.setAttribute("data-placement", dataPlacement);
        $KadastrsMenuPopupDiv.querySelector("#kadastrsMenuDiv-tooltip")?.setAttribute("data-state", "visible");
        $KadastrsMenuPopupDiv.querySelector("#kadastrsMenuDiv-content")?.setAttribute("data-state", "visible");
        let aFoundVenues = [];
        if (e.data.data.Venues) {
            aFoundVenues = [...aFoundVenues, ...e.data.data.Venues];
        }
        if (e.data.data.Residential) {
            aFoundVenues = [...aFoundVenues, ...e.data.data.Residential];
        }
        if (e.data.data.HN) {
            aFoundVenues = [...aFoundVenues, ...e.data.data.HN];
        }
        aHoverFeatures = [];
        aFoundVenues.forEach(sFoundVenueKey => {
            highlightVenue(sFoundVenueKey);
        });
        aFoundVenues.forEach(sFoundVenueKey => {
            drawConnectionLines(sFoundVenueKey, placePt);
        });
        aHoverFeatures = [...aAddressConnectionLinesFeatures];
        W.map.getLayerByUniqueName("venues").addFeatures(aHoverFeatures);
        setTimeout(() => W.map.getLayerByUniqueName("venues").redraw(), 0);
    }
    function createGeometry(e) {
        const vertex = 29.4;
        const poly = new OpenLayers.Geometry.LinearRing([
            new OpenLayers.Geometry.Point(e.data.geometry.x - vertex, e.data.geometry.y - vertex / 2),
            new OpenLayers.Geometry.Point(e.data.geometry.x - vertex, e.data.geometry.y + vertex / 2),
            new OpenLayers.Geometry.Point(e.data.geometry.x + vertex, e.data.geometry.y + vertex / 2),
            new OpenLayers.Geometry.Point(e.data.geometry.x + vertex, e.data.geometry.y - vertex / 2),
            new OpenLayers.Geometry.Point(e.data.geometry.x - vertex, e.data.geometry.y - vertex / 2)
        ]);
        poly.rotate(10, poly.getCentroid());
        return new OpenLayers.Geometry.Polygon([poly]);
    }
    function applyAddress(e, sMode) {
        const mAddressBuffer = convertKadastrsAddressStringToParts(e.data.attributes.std, "waze");
        if (!mAddressBuffer) {
            return;
        }
        const selection = wmeSDK.Editing.getSelection();
        if (!selection || selection.objectType !== "venue") {
            return;
        }
        if (selection.ids.length > 0) {
            if (sMode === "vienseta") {
                let oStreet = findStreetId(mAddressBuffer.cityName, mAddressBuffer.name);
                if (!oStreet) {
                    oStreet = findStreetId(`${mAddressBuffer.cityName}, ${mAddressBuffer.pagastsName}`, mAddressBuffer.name);
                }
                if (!oStreet) {
                    const oNewStreet = wmeSDK.DataModel.Streets.addStreet({
                        cityId: mAddressBuffer.cityID,
                        streetName: mAddressBuffer.name
                    });
                    updateVenue(selection.ids[0].toString(), { streetID: oNewStreet.id });
                }
                else {
                    updateVenue(selection.ids[0].toString(), { streetID: oStreet.attributes.id });
                }
            }
            else {
                updateVenue(selection.ids[0].toString(), {
                    houseNumber: mAddressBuffer.houseNumber,
                    streetID: mAddressBuffer.streetID,
                    name: sMode === "full" ? mAddressBuffer.name : undefined
                });
            }
        }
    }
    function updateVenue(sVenueId, mAddressData) {
        if (mAddressData.houseNumber?.includes(" k-")) {
            mAddressData.houseNumber = mAddressData.houseNumber.replace(" k-", "-");
        }
        if (mAddressData.streetID) {
            wmeSDK.DataModel.Venues.updateAddress({
                venueId: sVenueId,
                houseNumber: mAddressData.houseNumber,
                streetId: mAddressData.streetID
            });
        }
        if (mAddressData.name) {
            try {
                wmeSDK.DataModel.Venues.updateVenue({
                    venueId: sVenueId,
                    name: mAddressData.name,
                    lockRank: 2
                });
            }
            catch (oError) {
            }
        }
    }
    function createResidential(e) {
        const WMEAddressParams = convertKadastrsAddressStringToParts(e.data.attributes.std, "waze");
        if (!WMEAddressParams) {
            return;
        }
        const oGeometry = createGeometry(e);
        const movedPoi = oGeometry.getCentroid();
        movedPoi.x += 7;
        movedPoi.y -= 7;
        const sVenueId = wmeSDK.DataModel.Venues.addVenue({
            category: "RESIDENTIAL",
            geometry: W.userscripts.toGeoJSONGeometry(movedPoi)
        }).toString();
        updateVenue(sVenueId, WMEAddressParams);
        wmeSDK.Editing.setSelection({
            selection: {
                ids: [sVenueId],
                objectType: "venue"
            }
        });
    }
    function createHN(e) {
        const WMEAddressParams = convertKadastrsAddressStringToParts(e.data.attributes.std, "waze");
        if (!WMEAddressParams) {
            return;
        }
        const oGeometry = createGeometry(e);
        const aStreetSegments = W.model.segments.getByAttributes({
            primaryStreetID: WMEAddressParams.streetID
        });
        if (aStreetSegments.length > 0) {
            const closestSegment = aStreetSegments.reduce((min, segment) => oGeometry.distanceTo(segment.getOLGeometry(), { details: true }).distance <
                oGeometry.distanceTo(min.getOLGeometry(), { details: true }).distance
                ? segment
                : min);
            const oHNPoint = oGeometry.getCentroid();
            oHNPoint.x += 7;
            oHNPoint.y += 7;
            const fractionPoint = findClosestPoint(oHNPoint, closestSegment.getOLGeometry());
            if (!fractionPoint) {
                return;
            }
            const oFractionPoint = new OpenLayers.Geometry.Point(fractionPoint.x, fractionPoint.y);
            const epsg900913 = new OpenLayers.Projection("EPSG:900913");
            const epsg4326 = new OpenLayers.Projection("EPSG:4326");
            oHNPoint.transform(epsg900913, epsg4326);
            oFractionPoint.transform(epsg900913, epsg4326);
            const oHouseNumber = new Vs({
                forced: false,
                fractionPoint: {
                    coordinates: [oFractionPoint.x, oFractionPoint.y],
                    type: "Point"
                },
                geoJSONGeometry: {
                    coordinates: [oHNPoint.x, oHNPoint.y],
                    type: "Point"
                },
                number: WMEAddressParams.houseNumber,
                segID: closestSegment.getID(),
                id: W.model.segmentHouseNumbers.generateUniqueID()
            });
            W.model.actionManager.add(new oAddHouseNumber(oHouseNumber));
        }
    }
    function createVenue(e) {
        const WMEAddressParams = convertKadastrsAddressStringToParts(e.data.attributes.std, "waze");
        if (!WMEAddressParams) {
            alert(`Street  not found on map. Please check if it actually exist ${e.data.attributes.std}`);
            return;
        }
        const oGeometry = createGeometry(e);
        const sVenueId = wmeSDK.DataModel.Venues.addVenue({
            category: "OTHER",
            geometry: W.userscripts.toGeoJSONGeometry(oGeometry)
        }).toString();
        updateVenue(sVenueId, WMEAddressParams);
        wmeSDK.Editing.setSelection({
            selection: {
                ids: [sVenueId],
                objectType: "venue"
            }
        });
    }
    function drawConnectionLines(foundVenueKey, placePt) {
        const label = "";
        let placeGeomTarget;
        if (foundVenueKey.type === "houseNumber") {
            placeGeomTarget = foundVenueKey.getOLGeometry().getCentroid();
        }
        else {
            placeGeomTarget = W.model.venues.objects[foundVenueKey.id].getOLGeometry().getCentroid();
        }
        const poiPt = new OpenLayers.Geometry.Point(placeGeomTarget.x, placeGeomTarget.y);
        const lsLine = new OpenLayers.Geometry.LineString([placePt, poiPt]);
        const addressConnectionLineFeature = new OpenLayers.Feature.Vector(lsLine, {}, {
            strokeWidth: 3,
            strokeDashstyle: "12 8",
            strokeColor: "#FF0",
            label,
            labelYOffset: 45,
            fontColor: "#FF0",
            fontWeight: "bold",
            labelOutlineColor: "#000",
            labelOutlineWidth: 4,
            fontSize: "18"
        });
        aAddressConnectionLinesFeatures.push(addressConnectionLineFeature);
    }
    function highlightVenue(foundVenue, placePt) {
        if (foundVenue.type === "houseNumber") ;
        else {
            const placeGeomTarget = W.model.venues.objects[foundVenue.id].getOLGeometry().getCentroid();
            new OpenLayers.Geometry.Point(placeGeomTarget.x, placeGeomTarget.y);
            const OLVenue = W.map.venueLayer.getOlFeatureByFeatureId(foundVenue.id);
            if (OLVenue.geometry.getArea() !== 0) {
                OLVenue.geometry.getGeodesicArea(W.map.getProjectionObject());
            }
            if (OLVenue.geometry.getArea() !== 0) {
                const mStyle = {
                    fillColor: "#00695C",
                    fillOpacity: 0.5,
                    strokeWidth: 3,
                    strokeDashstyle: "12 8",
                    strokeColor: "blue",
                    label: "",
                    labelYOffset: 45,
                    fontColor: "#FF0",
                    fontWeight: "bold",
                    labelOutlineColor: "#000",
                    labelOutlineWidth: 4,
                    fontSize: "18"
                };
                OLVenue.style = mStyle;
                aWMEPolygonHighlightBlueFeatures.push(OLVenue);
            }
        }
    }
    function hideTooltipAfterDelay() {
        iPopupTimeout = window.setTimeout(hideCustomPopup, 300);
    }
    function onFeatureUnHoverEvent(e) {
        hideTooltipAfterDelay();
        aAddressConnectionLinesFeatures.forEach(feature => {
            feature.destroy();
            feature = null;
        });
        aAddressConnectionLinesFeatures = [];
        aWMEPolygonHighlightBlueFeatures.forEach(feature => {
            feature.style = null;
        });
        aWMEPolygonHighlightBlueFeatures = [];
        setTimeout(() => W.map.getLayerByUniqueName("venues").redraw(), 0);
    }
    function highlightVenue2(OLVenue, bIsValid) {
        if (OLVenue.geometry.getArea() !== 0) {
            OLVenue.geometry.getGeodesicArea(W.map.getProjectionObject());
        }
        if (OLVenue.geometry.getArea() !== 0) {
            const mStyle = {
                fillColor: "#00695C",
                fillOpacity: 0.5,
                strokeWidth: 3,
                strokeDashstyle: "12 8",
                strokeColor: `${bIsValid ? "green" : "red"}`,
                label: "",
                labelYOffset: 45,
                fontColor: "#FF0",
                fontWeight: "bold",
                labelOutlineColor: "#000",
                labelOutlineWidth: 4,
                fontSize: "18"
            };
            OLVenue.style = mStyle;
            aWMEPolygonHighlightFeatures.push(OLVenue);
        }
    }
    function clearHighlightedAdressFeatures() {
        if (aWMEPolygonHighlightFeatures.length > 0) {
            let layer = aWMEPolygonHighlightFeatures[0].layer;
            aWMEPolygonHighlightFeatures.forEach(feature => {
                feature.style = null;
                layer = feature.layer;
            });
            layer?.redraw();
            aWMEPolygonHighlightFeatures = [];
        }
    }
    function hideCustomPopup() {
        if ($KadastrsMenuPopupDiv) {
            $KadastrsMenuPopupDiv.querySelector("#kadastrsMenuDiv-content")?.setAttribute("data-state", "hidden");
            $KadastrsMenuPopupDiv.querySelector("#kadastrsMenuDiv-tooltip")?.setAttribute("data-state", "hidden");
            $KadastrsMenuPopupDiv.replaceChildren();
        }
    }
    function findStreetId(sCityName, sStreetName, bVienseta) {
        return Object.values(W.model.streets.objects).find((street) => {
            if (!W.model.cities.objects[street.attributes.cityID]) {
                console.log("Error: no City");
            }
            if (street.attributes.name === sStreetName &&
                W.model.cities.objects[street.attributes.cityID].attributes.name.includes(sCityName)) {
                return true;
            }
        });
    }
    function populateIds(addresses) {
        const streetCache = {};
        for (const [index, address] of addresses.entries()) {
            if (!streetCache[address.streetName]) {
                const oStreet = findStreetId(address.cityName, address.streetName);
                if (!oStreet && address.streetName !== "") {
                    const sErrorMsg = "Error: Can't find street: " + address.streetName + " in City: " + address.cityName;
                    console.log(sErrorMsg);
                    alert(sErrorMsg);
                    return index;
                }
                if (oStreet) {
                    streetCache[address.streetName] = oStreet.attributes.id;
                }
            }
            address.streetID = streetCache[address.streetName];
        }
        return undefined;
    }
    function convertKadastrsAddressStringToParts(sAddress, sMode) {
        const aAddress = sAddress.split(", ");
        const bNoCity = aAddress[1].slice(-4) === "pag.";
        const sCityName = bNoCity ? "" : aAddress[1];
        const sPagastsName = bNoCity ? aAddress[1] : aAddress[2];
        bNoCity ? aAddress[2] : aAddress[3];
        bNoCity ? aAddress[3] : aAddress[4];
        const sStreetNameOrHN = aAddress[0];
        let sName = "";
        let sHN = "";
        let bVienseta = false;
        let bVasarnica = false;
        if (bNoCity) {
            sName = `${sStreetNameOrHN.slice(1, -1)}, ${sPagastsName}`;
            const sStreetName = "";
            return {
                cityName: sCityName,
                streetName: sStreetName,
                houseNumber: sHN,
                name: sName
            };
        }
        else {
            if (sStreetNameOrHN.startsWith('"') && sStreetNameOrHN.endsWith('"')) {
                sName = sStreetNameOrHN.slice(1, -1);
                const aVasarnica = sName.split(" ");
                const sLast = aVasarnica.pop();
                const sVasarnicaName = aVasarnica.join(" ");
                const bIsVasarnica = aVasarnicas.some(mRecord => mRecord.name === sVasarnicaName && mRecord.parent === aAddress[1]);
                if (bIsVasarnica) {
                    bVasarnica = true;
                    sHN = aVasarnica.length > 0 ? sLast ?? "" : "";
                    sName = sHN;
                }
                else {
                    bVienseta = true;
                }
            }
            else {
                const aStreetNameAndHN = sStreetNameOrHN.split(" ");
                sName = "";
                sHN = aStreetNameAndHN.pop() ?? "";
                if (sHN.includes("k-")) {
                    sHN = `${aStreetNameAndHN.pop()} ${sHN}`;
                    sName = sHN.replace(" k", "");
                }
                else {
                    sName = sHN;
                }
            }
            const iLastIndex = sStreetNameOrHN.lastIndexOf(" " + sHN);
            const sStreetName = sHN === ""
                ? ""
                : iLastIndex === -1
                    ? sStreetNameOrHN
                    : bVasarnica
                        ? sStreetNameOrHN.substring(1, iLastIndex)
                        : sStreetNameOrHN.substring(0, iLastIndex);
            if (sMode !== "waze") {
                return {
                    cityName: sCityName,
                    streetName: bVienseta ? "" : sStreetName,
                    houseNumber: sHN,
                    name: sName
                };
            }
            else {
                const oStreet = Object.values(W.model.streets.objects).find((street) => {
                    if (street.attributes.name === sStreetName &&
                        W.model.cities.objects[street.attributes.cityID].attributes.name.includes(sCityName)) {
                        return true;
                    }
                });
                let oCity;
                if (!oStreet) {
                    oCity = Object.values(W.model.cities.objects).find((oCity) => {
                        return oCity.attributes.name === sCityName;
                    });
                }
                return {
                    cityID: oStreet ? oStreet.attributes.cityID : oCity ? oCity.attributes.id : undefined,
                    streetID: oStreet ? oStreet.attributes.id : undefined,
                    houseNumber: sHN,
                    name: sName,
                    cityName: sCityName,
                    pagastsName: sPagastsName
                };
            }
        }
    }
    function makeGetRequest(sURL) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: sURL,
                onload: response => resolve(response.responseText),
                onerror: error => reject(error)
            });
        });
    }
    function excludeKadastrsAddressConsistentWithWME(oAddressRegex, oVenueOrHouseNumber) {
        const indexToRemove = aMissingFetchedAddresses.findIndex(mFetchedAddresses => oAddressRegex.test(mFetchedAddresses.attributes.std));
        if (indexToRemove > -1) {
            aMissingFetchedAddresses.splice(indexToRemove, 1);
        }
        const indexToUpdate = aGlobalFetchedAddresses.findIndex(mFetchedAddresses => oAddressRegex.test(mFetchedAddresses.attributes.std));
        if (indexToUpdate > -1) {
            if (oVenueOrHouseNumber.type === "houseNumber") {
                if (!aGlobalFetchedAddresses[indexToUpdate].data.HN) {
                    aGlobalFetchedAddresses[indexToUpdate].data.HN = [];
                }
                aGlobalFetchedAddresses[indexToUpdate].data.HN.push(oVenueOrHouseNumber);
            }
            else {
                aWMEValidAddressVenues.push(oVenueOrHouseNumber);
                if (oVenueOrHouseNumber.categories[0] === "RESIDENCE_HOME") {
                    if (!aGlobalFetchedAddresses[indexToUpdate].data.Residential) {
                        aGlobalFetchedAddresses[indexToUpdate].data.Residential = [];
                    }
                    aGlobalFetchedAddresses[indexToUpdate].data.Residential.push(oVenueOrHouseNumber);
                }
                else {
                    if (!aGlobalFetchedAddresses[indexToUpdate].data.Venues) {
                        aGlobalFetchedAddresses[indexToUpdate].data.Venues = [];
                    }
                    aGlobalFetchedAddresses[indexToUpdate].data.Venues.push(oVenueOrHouseNumber);
                }
            }
        }
    }
    function getRegex(aStrings) {
        const regexPattern = aStrings.map(v => v).join("\\s*,\\s*");
        return new RegExp(regexPattern);
    }
    function findStreetName(iStreetId) {
        if (!W.model.streets.objects[iStreetId]) {
            return {
                name: "",
                city: ""
            };
        }
        const sName = W.model.streets.objects[iStreetId].attributes.name;
        const iCity = W.model.streets.objects[iStreetId].attributes.cityID;
        const sCity = W.model.cities.objects[iCity].attributes.countryID === 123 ? W.model.cities.objects[iCity].attributes.name : "";
        return {
            name: sName,
            city: sCity
        };
    }
    function getBBoxv2() {
        const mapExtent = W.map.getExtent();
        return mapExtent.toString();
    }
    function getBBox3059() {
        const extent = W.map.getExtent();
        if (Array.isArray(extent)) {
            proj4.defs("EPSG:3059", "+proj=tmerc +lat_0=0 +lon_0=24 +k=0.9996 +x_0=500000 +y_0=-6000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
            const aConvertedPoints = [
                ...proj4("EPSG:4326", "EPSG:3059", extent.slice(0, 2)).reverse(),
                ...proj4("EPSG:4326", "EPSG:3059", extent.slice(2, 4)).reverse()
            ];
            const string = aConvertedPoints.join();
            return string;
        }
    }
    class Vs {
        type = "houseNumber";
        attributes;
        _prevID;
        version;
        state;
        constructor(e) {
            this.attributes = e;
        }
        static defaults() {
            return {
                forced: false,
                fractionPoint: null,
                id: "",
                side: null,
                valid: false
            };
        }
        changeID(e) {
            (this._prevID = this.getID()), this.setID(e);
        }
        merge(e) {
            Object.assign(this.attributes, e);
        }
        setID(e) {
            this.attributes.id = e;
        }
        setVersion(e) {
            this.version = e;
        }
        setState(e) {
            this.state = e;
        }
        getAttributes() {
            return this.attributes;
        }
        getAttribute(e) {
            return this.attributes[e];
        }
        getGeometry() {
            return this.getAttribute("geoJSONGeometry");
        }
        getID() {
            return this.attributes.id;
        }
        getUniqueIdentifier() {
            return {
                objectId: this.getID(),
                objectType: this.getType()
            };
        }
        getUniqueID() {
            return `${this.getType()}: ${this.getID()}`;
        }
        getType() {
            return this.type;
        }
        getSide() {
            return this.attributes.side;
        }
        getSegmentId() {
            return this.attributes.segID;
        }
        setSegmentId(e) {
            this.attributes.segID = e;
        }
        getNumber() {
            return this.attributes.number;
        }
        setNumber(e) {
            this.attributes.number = e;
        }
        isForced() {
            return this.attributes.forced;
        }
        isValid() {
            return this.attributes.valid;
        }
        getFractionPoint() {
            return this.attributes.fractionPoint;
        }
        setFractionPoint(e) {
            this.attributes.fractionPoint = e;
        }
        isDeleted() {
            return Boolean(this.state && this.state === "DELETE");
        }
    }
    function findClosestPoint(point, lineString) {
        let closestPoint = null;
        let closestDistance = Infinity;
        for (let i = 0; i < lineString.components.length - 1; i++) {
            const segmentStart = lineString.components[i];
            const segmentEnd = lineString.components[i + 1];
            const segmentVector = {
                x: segmentEnd.x - segmentStart.x,
                y: segmentEnd.y - segmentStart.y
            };
            const pointVector = {
                x: point.x - segmentStart.x,
                y: point.y - segmentStart.y
            };
            const dotProduct = pointVector.x * segmentVector.x + pointVector.y * segmentVector.y;
            const t = dotProduct / (segmentVector.x * segmentVector.x + segmentVector.y * segmentVector.y);
            const closestPointOnSegment = {};
            if (t < 0) {
                closestPointOnSegment.x = segmentStart.x;
                closestPointOnSegment.y = segmentStart.y;
            }
            else if (t > 1) {
                closestPointOnSegment.x = segmentEnd.x;
                closestPointOnSegment.y = segmentEnd.y;
            }
            else {
                closestPointOnSegment.x = segmentStart.x + t * segmentVector.x;
                closestPointOnSegment.y = segmentStart.y + t * segmentVector.y;
            }
            const distance = calculateDistance(closestPointOnSegment, point);
            if (distance < closestDistance) {
                closestDistance = distance;
                closestPoint = closestPointOnSegment;
            }
        }
        return closestPoint;
    }
    function calculateDistance(point1, point2) {
        const dx = point1.x - point2.x;
        const dy = point1.y - point2.y;
        return Math.sqrt(dx * dx + dy * dy);
    }

})(proj4);