Feature Your Map beta

get poi data from osm

目前為 2024-04-06 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Feature Your Map beta
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  get poi data from osm
// @author       KaKa
// @match        https://map-making.app/maps/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_setClipboard
// @license      MIT
// @icon         https://www.google.com/s2/favicons?domain=geoguessr.com
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// ==/UserScript==

(function() {
    'use strict';
    let mapFeatures={'way':['motorway','trunk','primary','secondary','tertiary','unclassified','footway','path','pedestrain','river','bridge'],
                     'node':[ 'bus stop', 'level crossing','milestone', 'crosswalk','traffic light','postbox', 'hydrant','utility pole', 'lamppost']}
    const API_URL = "https://overpass-api.de/api/interpreter";
    const checkboxButtonStyle = `
.checkbox-button {
    display: inline-block;
    cursor: pointer;
    background-color: #007bff; /* 初始状态下按钮为蓝色 */
    color: #fff;
    padding: 5px 10px;
    border-radius: 5px;
    margin-right: 10px;
}

.checkbox-button:hover {
    background-color: #4CAF50; /* 鼠标悬停时按钮变成绿色 */
    border-color: #4CAF50; /* 边框颜色也相应修改 */
}

.checkbox-button input[type="checkbox"] {
    display: none;
}

.checkbox-button input[type="checkbox"]:checked + span {
    background-color: #4CAF50; /* 选中后按钮变成绿色 */
    color: #fff;
    font-weight: bold;
    border-color: #4CAF50; /* 选中后边框颜色也为绿色 */
}

.category-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.category-row {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
    margin-right: 30px;
    margin-left: 30px;
}

.flex-fill {
    flex: 1;
}
`;
    async function fetchData(query, mode,feature,advanced) {

        const requestBody = getRequestBody(feature,mode,advanced,query)
        const response = await fetch(API_URL, {
            method: "POST",
            body: "data=" + encodeURIComponent(requestBody),
        });

        if (!response.ok) {
            throw new Error("Network response was not ok");
        }

        return response.json();
    }

    async function getData(query, mode,features,advanced) {
        try {
            const js = {
                "name": "",
                "customCoordinates": [],
                "extra": {
                    "tags": {},
                    "infoCoordinates": []
                }
            };
            let elements = [];
            if (advanced==='Intersection'){
                const response=await fetchData(query, mode, features,advanced)
                if (response.remark && response.remark.includes("runtime error")) {
                    alert("RAM runned out or query timed out. Please try narrowing your search scope.");

                } else if (response.elements && response.elements.length > 0) {
                    elements.push(...response.elements);
                }
                writeData(elements, features, js,advanced);
            }

            else if(advanced==='Around'){
                const response=await fetchData(query, mode, features,advanced)
                if (response.remark && response.remark.includes("runtime error")) {
                    alert("RAM runned out or query timed out. Please try narrowing your search scope.");

                } else if (response.elements && response.elements.length > 0) {
                    elements.push(...response.elements);
                }
                writeData(elements, features, js,advanced)}
            else{
            for (let feature of features) {
                let requests = [];

                requests.push(fetchData(query, mode, feature));

                const responses = await Promise.all(requests);

                responses.forEach(response => {if (response.remark && response.remark.includes("runtime error")) {
                    alert("RAM runned out or query timed out. Please try narrowing your search scope.");
                } else
                    if (response.elements && response.elements.length > 0) {
                        elements.push(...response.elements);
                    }
                });
                writeData(elements, feature[0], js);

            }
            }
            if (js.customCoordinates.length === 0) {
                if (mode === 'area') {
                    alert("Cannot find this place, the place name you entered may be incorrect.");
                } else if (mode === 'polygon') {
                    alert("Please check if your geojson file format is correct.");
                }
            }
            if (js.customCoordinates.length > 0) {
                GM_setClipboard(JSON.stringify(js));
                alert("JSON data has been copied to your clipboard!");
            }
        } catch (error) {
            console.error("Error fetching data:", error);
        }
    }

    function getFeatureElement(f){
        for (const key in mapFeatures) {
            if (mapFeatures.hasOwnProperty(key)) {
                if (mapFeatures[key].includes(f)) {
                    return key
                }}}}

    function getRequestBody(features, mode, advanced, query) {
        let requestBody = "";

        if (mode === "area"&& !advanced) {
            requestBody = `[out:json][timeout:180];area[name="${query}"]; (${getFeatureElement(features[0])}(area)[${features[1]}]; );out geom;`;
        }
        else if (mode === "polygon"&& !advanced) {
            requestBody = `[out:json][timeout:180];${getFeatureElement(features[0])}[${features[1]}](poly:"${query}");out geom;`;
        }
        else if (advanced === "Intersection" && mode === "area") {
            requestBody = `[out:json][timeout:180];
                            area[name="${query}"];
                            way(area)[highway~"^(${features[0].join('|')})$"]->.w1;
                            way(area)[highway~"^(${features[1].join('|')})$"]->.w2;
                            node(w.w1)(w.w2);
                            out geom;
                        `;
        }
        else if (advanced === "Intersection" && mode === "polygon") {
            requestBody = `[out:json][timeout:180];
                way(poly:"${query}")[highway~"^(${features[0].join('|')})$"]->.w1;
                way(poly:"${query}")[highway~"^(${features[1].join('|')})$"]->.w2;
                node(w.w1)(w.w2);
                out geom;
                        `;
        }
        else if (advanced === "Around" && mode === "area") {
            const aroundPoint = features[1]
            const aroundFeature=features[0].find(feature => feature[0] === aroundPoint);
            const resultFeature = features[0].find(feature => feature[0] !== aroundPoint);
            const featureElement=
                  requestBody = `[out:json][timeout:180];area[name="${query}"];${getFeatureElement(aroundFeature[0])}(area)[${aroundFeature[1]}];${getFeatureElement(resultFeature[0])}(around:${features[2]})[${resultFeature[1]}];out geom;`;
        }
        else if (advanced === "Around" && mode === "polygon") {
            const aroundPoint = features[1]
            const aroundFeature=features[0].find(feature => feature[0] === aroundPoint);
            const resultFeature = features[0].find(feature => feature[0] !== aroundPoint);
            const featureElement=
                  requestBody = `[out:json][timeout:180];${getFeatureElement(aroundFeature[0])}(poly:"${query}")[${aroundFeature[1]}];${getFeatureElement(resultFeature[0])}(around:${features[2]})[${resultFeature[1]}];out geom;`;
        }
        return requestBody;
    }

    function writeData(coordinates, feature, js,advanced) {
        for (let i = 0; i < coordinates.length; i++) {
            if (coordinates[i].geometry) {
                let nodes = coordinates[i].geometry;
                let randomIndex = Math.floor(Math.random() * nodes.length);
                let randomCoordinate = nodes[randomIndex];
                let tag;

                if (coordinates[i].tags && coordinates[i].tags.highway) {
                    tag = [coordinates[i].tags.highway, feature];
                } else {
                    tag = [feature];
                }
                if (randomCoordinate.lat && randomCoordinate.lon) {
                    if (advanced=== 'Intersection') {
                        let advancedTags = [];
                        for (let j = 0; j < feature[0].length; j++) {
                            advancedTags.push(feature[0][j])
                        }
                        for (let j = 0; j < feature[1].length; j++) {
                            advancedTags.push(feature[1][j])
                        }
                        tag = advancedTags;


                    }
                    else if(advanced=== 'Around'){
                        let advancedTags = [];
                        const resultFeature = feature[0].find(feature => feature[0] !== feature[1])

                        advancedTags.push(resultFeature[0]);
                        tag = advancedTags
                        if (coordinates[i].tags && coordinates[i].tags.highway) {
                            tag .push(coordinates[i].tags.highway);
                        }
                    }

                    js.customCoordinates.push({
                        "lat": randomCoordinate.lat,
                        "lng": randomCoordinate.lon,
                        "extra": {
                            "tags": tag
                        }
                    });


                }
            } else if (coordinates[i].lat && coordinates[i].lon && !isCoordinateExists(js.customCoordinates, coordinates[i].lat, coordinates[i].lon)) {
                let tag = [feature];
                if (advanced=== 'Intersection') {
                    let advancedTags = [];
                    for (let j = 0; j < feature[0].length; j++) {
                        advancedTags.push(feature[0][j])
                    }
                    for (let j = 0; j < feature[1].length; j++) {
                        advancedTags.push(feature[1][j])
                    }
                    tag = advancedTags;


                }
                else if(advanced=== 'Around'){
                    let advancedTags = [];
                    const resultFeature = feature[0].find(feature => feature[0] !== feature[1])

                    advancedTags.push(resultFeature[0]);

                    tag = advancedTags
                    if (coordinates[i].tags && coordinates[i].tags.highway) {
                        tag.push(coordinates[i].tags.highway);
                    }
                }

                js.customCoordinates.push({
                    "lat": coordinates[i].lat,
                    "lng": coordinates[i].lon,
                    "extra": {
                        "tags": tag
                    }
                });
            }
        }
    }

    function isCoordinateExists(coordinates, lat, lon) {
        for (let i = 0; i < coordinates.length; i++) {
            if (coordinates[i].lat === lat && coordinates[i].lng === lon) {
                return true;
            }
        }

        return false;
    }

    function getInput(features, advanced) {
        const option = confirm('Do you want to upload a Geojson file? If you click "Cancel",you will need to enter a location name');

        if (option) {
            const input = document.createElement('input');
            input.type = 'file';
            input.style.position = 'absolute';
            input.style.right = '360px';
            input.style.top = '15px';
            input.addEventListener('change', async event => {
                const file = event.target.files[0];
                if (file) {
                    try {
                        var query = await readFile(file);
                        getData(query,'polygon',features,advanced)
                        document.body.removeChild(input);
                    } catch (error) {
                        console.error('Error reading file:', error);
                        document.body.removeChild(input);
                    }
                }
            });

            document.body.appendChild(input);

        } else {
            let query = prompt('Please enter a location name(eg:Paris)');
            if (query === null) {
                alert('You cancelled the input!');
            } else {
                query = query.trim();
                while (query.length === 0) {
                    alert('You must enter a valid location name!');
                    query = prompt('Please enter a location name(eg:Paris)');
                    if (query === null) {
                        alert('You cancelled the input!');
                        break;
                    }
                    query = query.trim();
                }
                if (query) {
                    getData(query,'area',features,advanced);
                }
            }
        }}

    function extractCoordinates(p) {
        let results = [];
        if (p.features){
            let polygons=p.features
            polygons.forEach(data => {
                let coordinates = [];
                data.geometry.coordinates.forEach(polygon => {
                    polygon[0].forEach(coordinatePair => {
                        let coordinate = [coordinatePair[1], coordinatePair[0]].join(' ');
                        coordinates.push(coordinate);
                    });
                });
                let result = coordinates.join(' ');
                result = result.replace(/,/g, ' ');
                results.push(result);
            });}
        else if( p.coordinates){
            let coordinates = [];
            p.coordinates.forEach(polygon => {
                polygon.forEach(subPolygon => {
                    subPolygon.forEach(coordinatePair => {
                        let coordinate = [coordinatePair[1], coordinatePair[0]].join(' ');
                        coordinates.push(coordinate);
                    });
                });
            });
            let result = coordinates.join(' ');
            result = result.replace(/,/g, ' ');
            results.push(result);

        }
        else {
            console.error('Invalid Geojson format.');
            alert('Invalid Geojson format!');
        }
        return results;

    }

    function readFile(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();

            reader.onload = function(event) {
                const jsonContent = event.target.result;

                try {
                    const data = JSON.parse(jsonContent);

                    if (data) {
                        const coordinates = extractCoordinates(data);
                        resolve(coordinates);
                    }
                } catch (error) {
                    console.error('Error parsing Geojson:', error);
                    alert('Error parsing Geojson!');
                    resolve('error')
                }
            };

            reader.readAsText(file);
        });
    }

    function runScript(features,advanced){
        if (features&&features.length>0){

            getInput(features,advanced)
        }
    }

    function getHtml(categories){
        const categoryKeys = Object.keys(categories);
        const numCategories = categoryKeys.length;



        let html = `<style>${checkboxButtonStyle}</style>`; // 添加样式

        for (let i = 0; i < numCategories; i += 2) {
            html += `<div class="category-row">`;
            const category1 = categoryKeys[i];
            const category2 = (i + 1 < numCategories) ? categoryKeys[i + 1] : null;

            html += `
                <label class="checkbox-button" for="swal-input-${category1}">
                    <input id="swal-input-${category1}" class="swal2-input" type="checkbox" value="${category1}" style=${checkboxButtonStyle}>
                    <span>${category1}</span>
                </label>
            `;

            if (category2) {
                html += `
                    <label class="checkbox-button" for="swal-input-${category2}">
                        <input id="swal-input-${category2}" class="swal2-input" type="checkbox" value="${category2}" style="display: none;">
                        <span>${category2}</span>
                    </label>
                `;
            } else {
                // 如果是奇数个类别,最后一个类别单独占据一行
                html += `<div class="flex-fill"></div>`;
            }

            html += `</div>`;
        }
        return html
    }

    function getFeaturesHtml(features){
        let featuresHtml = '';
        featuresHtml += `<style>${checkboxButtonStyle}</style>`; // 添加样式

        for (let i = 0; i < features.length; i += 2) {
            featuresHtml += `<div class="category-row">`;
            const feature1 = features[i];
            const feature2 = (i + 1 < features.length) ? features[i + 1] : null;

            featuresHtml += `<div style="display: flex; flex-direction: column; align-items: flex-start;">
                        <label class="checkbox-button">
                            <input class="swal2-input" type="checkbox" value="${feature1}" style="display: none;">
                            <span style="margin-left: 1px;">${feature1}</span>
                        </label>
                    </div>`;

            if (feature2) {
                featuresHtml += `<div style="display: flex; flex-direction: column; align-items: flex-start;">
                            <label class="checkbox-button">
                                <input class="swal2-input" type="checkbox" value="${feature2}" style="display: none;">
                                <span style="margin-left: 1px;">${feature2}</span>
                            </label>
                        </div>`;
            } else {

                featuresHtml += `<div style="flex: 1;"></div>`;
            }

            featuresHtml += `</div>`;
        }
        return featuresHtml
    }

    async function getFeatures() {
        const categories = {
            'Transportation': ['bridge', 'bus stop', 'level crossing','milestone', 'crosswalk','traffic light'],
            'Public Facilities': ['postbox', 'hydrant','utility pole', 'lamppost']

        };
        const advancedCategories={'Intersection':['motorway','trunk','primary','secondary','tertiary','unclassified','footway','path','pedestrain','river','railway','tram'],
                                 'Around Search':['bridge', 'bus stop', 'level crossing','milestone', 'crosswalk','traffic light','postbox', 'hydrant','utility pole', 'lamppost','river']}
        let selectedCategories = [];


        const { value: selectedMainCategories, dismiss: cancelInput } = await Swal.fire({
            title: 'Select Categories',
            html: getHtml(categories),
            focusConfirm: false,
            allowOutsideClick: false,
            showCancelButton: true,
            showCloseButton:true,
            cancelButtonText: 'Advanced Search',
            preConfirm: () => {
                selectedCategories = [];
                let noCategorySelected = true;
                for (let category in categories) {
                    if (document.getElementById(`swal-input-${category}`).checked) {
                        selectedCategories.push(category);
                        noCategorySelected = false;
                    }
                }
                if (noCategorySelected) {
                    Swal.showValidationMessage('Please select at least one category');
                }

                return selectedCategories;
            }
        });


        if (selectedMainCategories) {
            let selectedFeatures = [];
            for (let category of selectedMainCategories) {
                selectedFeatures = selectedFeatures.concat(categories[category]);
            }
            const { value: selectedSubFeatures, dismiss: cancelInput } = await Swal.fire({
                title: 'Select Features',
                html: getFeaturesHtml(selectedFeatures) + '<a href="https://wiki.openstreetmap.org/wiki/Map_features" target="_blank" style="color: black; font-size: 16px;">More information about features...</a>',
                focusConfirm: false,
                allowOutsideClick: 'cancel',
                showCancelButton: true,
                preConfirm: () => {
                    let selectedSubFeatures = [];
                    const checkboxes = document.querySelectorAll('.swal2-input[type="checkbox"]:checked');
                    checkboxes.forEach((checkbox) => {
                        selectedSubFeatures.push(checkbox.value);
                    });
                    if (selectedSubFeatures.length === 0) {
                        Swal.showValidationMessage('Please select at least one feature');
                    }

                    return selectedSubFeatures;
                }
            });


            if (selectedSubFeatures) {
                const features = [];
                const tags = [
                    ['bridge', '"bridge"="yes"'],
                    ['bus stop', '"highway"="bus_stop"'],
                    ['utility pole', '"power"="pole"'],
                    ['traffic light', '"highway"="traffic_signals"'],
                    ['lamppost', '"highway"="street_lamp"'],
                    ['crosswalk', '"highway"="crossing"'],
                    ['level crossing', '"railway"="level_crossing"'],
                    ['postbox', '"amenity"="post_box"'],
                    ['hydrant', '"emergency"="fire_hydrant"'],
                    ['milestone', '"highway"="milestone"'],
                    ['motorway','"highway"="motorway"'],
                    ['trunk','"highway"="trunk"'],
                    ['primary','"highway"="primary"'],
                    ['secondary','"highway"="secondary"'],
                    ['tertiary','"highway"="tertiary"'],
                    ['unclassified','"highway"="unclassified"'],
                    ['footway','"highway"="footway"'],
                    ['path','"highway"="path"'],
                    ['pedestrain','"highway"="pedestrain"'],
                    ['river','"waterway"="river"'],
                    ['railway','"railway"="rail"'],
                    ['tram','"railway"="tram"']
                ];

                const filteredTags = tags.filter(tag => selectedSubFeatures.includes(tag[0]));

                features.push(...filteredTags);
                runScript(features,'')
            }
        }

    else if (cancelInput) {
        const { value: selectedAdvancedCategories, dismiss: cancelInput } = await Swal.fire({
            title: 'Advanced Search',
            html: getHtml(advancedCategories),
            focusConfirm: false,
            allowOutsideClick: false,
            showCancelButton: true,
            showCloseButton:true,
            cancelButtonText: 'Cancel',
            preConfirm: () => {
                selectedCategories = [];
                for (let category in advancedCategories) {
                    if (document.getElementById(`swal-input-${category}`).checked) {
                        selectedCategories.push(category);
                    }
                }
                if (selectedCategories.length === 0) {
                    Swal.showValidationMessage('Please select at least one option!');
                    return false;
                } else if (selectedCategories.length >1) {
                    Swal.showValidationMessage("You're only allowed to select one option!");
                    return false;
                }


                return selectedCategories;
            }
        });

        if (selectedAdvancedCategories) {
            let selectedFeatures = [];
            for (let category of selectedAdvancedCategories) {
                selectedFeatures = selectedFeatures.concat(advancedCategories[category]);
            }
            const { value: selectedSubFeatures, dismiss: cancelInput } = await Swal.fire({
                title: 'Select Features',
                html: getFeaturesHtml(selectedFeatures)+ '<a href="https://wiki.openstreetmap.org/wiki/Map_features" target="_blank" style="color: black; font-size: 16px;">More information about features...</a>',
                focusConfirm: false,
                allowOutsideClick: 'cancel',
                showCancelButton: true,
                preConfirm: () => {
                    const checkboxes = document.querySelectorAll('.swal2-input[type="checkbox"]:checked');
                    const selectedSubFeatures = Array.from(checkboxes).map(checkbox => checkbox.value);

                    if (selectedSubFeatures.length < 1) {
                        Swal.showValidationMessage('Please select at least one option!');
                        return false;
                    }

                    if (selectedAdvancedCategories.includes('Intersection')) {
                        const minorFeatures = advancedCategories.Intersection.filter(feature => !selectedSubFeatures.includes(feature));
                        return Swal.fire({
                            title: 'Select Minor Way',

                            html: getFeaturesHtml(minorFeatures) + '<a target="_blank" style="color: black; font-size: 14px;">The script will search for intersections based on the type of minor way you selected and the type of major way you selected previously.</a>',
                            showCancelButton: true,
                            preConfirm: () => {
                                return new Promise((resolve) => {
                                    const checkboxes = document.querySelectorAll('.swal2-input[type="checkbox"]:checked');
                                    const selectedMinorFeatures = Array.from(checkboxes).map(checkbox => checkbox.value);
                                    resolve(selectedMinorFeatures);
                                }).then((selectedMinorFeatures) => {
                                    return [selectedSubFeatures, selectedMinorFeatures];
                                }).catch(() => {
                                    return false;
                                });
                            }
                        });
                    }

                    if (selectedAdvancedCategories.includes('Around Search')) {
                        if (selectedSubFeatures.length > 2) {
                            Swal.showValidationMessage('Please select up to two options!');
                            return false;
                        }
                        return Swal.fire({
                            title: 'Select Around Point',
                            html: `
                    <p>The script will first search for some points that match the feature, and then search around those points for points that match another feature.</p>
                    <div>
                        <select id="aroundPoint" class="swal2-select">
                            ${selectedSubFeatures.map(option => `<option value="${option}">${option}</option>`)}
                        </select>
                    </div>
                `,
                            showCancelButton: true,
                            preConfirm: () => {
                                const aroundPoint = document.getElementById('aroundPoint').value;

                                return Swal.fire({
                                    title: 'Please enter a radius(metre)',
                                    input: 'text',
                                    inputLabel: 'Radius',
                                    inputPlaceholder: 'Enter radius...',
                                    showCancelButton: true,
                                    inputValue: 100,
                                    inputValidator: (value) => {
                                        const radiusValue = parseInt(value);
                                        if (isNaN(radiusValue) || radiusValue < 10 || radiusValue > 5000) {
                                            return 'Please enter a valid integer between 10 and 5000!';
                                        }
                                    }
                                }).then((result) => {
                                    if (result.isConfirmed) {
                                        const radius = result.value;
                                        return [selectedSubFeatures, aroundPoint, radius ];
                                    } else {
                                        return false;
                                    }
                                });
                            }
                        });
                    }

                    else{return selectedSubFeatures}}

            });
            if (selectedSubFeatures) {

                const features = [];
                const tags = [
                    ['bridge', '"bridge"="yes"'],
                    ['bus stop', '"highway"="bus_stop"'],
                    ['utility pole', '"power"="pole"'],
                    ['traffic light', '"highway"="traffic_signals"'],
                    ['lamppost', '"highway"="street_lamp"'],
                    ['crosswalk', '"highway"="crossing"'],
                    ['level crossing', '"railway"="level_crossing"'],
                    ['postbox', '"amenity"="post_box"'],
                    ['hydrant', '"emergency"="fire_hydrant"'],
                    ['milestone', '"highway"="milestone"'],
                    ['motorway','"highway"="motorway"'],
                    ['trunk','"highway"="trunk"'],
                    ['primary','"highway"="primary"'],
                    ['secondary','"highway"="secondary"'],
                    ['tertiary','"highway"="tertiary"'],
                    ['unclassified','"highway"="unclassified"'],
                    ['footway','"highway"="footway"'],
                    ['path','"highway"="path"'],
                    ['pedestrain','"highway"="pedestrain"'],
                    ['river','"waterway"="river"'],
                    ['railway','"railway"="rail"'],
                    ['tram','"railway"="tram"']
                ];
                let filteredTags;
                try{
                    const intersectionFeatures=selectedSubFeatures.value
                    let majorTags
                    let minorTags

                    features.push(...[intersectionFeatures[0]],...[intersectionFeatures[1]]);

                    runScript(features,'Intersection')
                    }
                catch (error){
                let aroundFeatures=selectedSubFeatures.value
                filteredTags = tags.filter(tag => aroundFeatures[0].includes(tag[0]))
                features.push(...filteredTags)
                runScript([features,aroundFeatures[1],aroundFeatures[2]],'Around')
                }
            }
        }
    }
}

    async function downloadGeoJSONFromOSMID() {
        Swal.fire({
            title: 'Enter OSM ID or place name',
            input: 'text',
            inputValue: 'Paris or 71525',
            showCancelButton: true,
            confirmButtonText: 'Submit',
            cancelButtonText: 'Cancel',
            showCloseButton:true,
            inputValidator: (value) => {
                if (!value) {
                    return 'You need to enter something!';
                }
            }
        }).then(async (result) => {
            if (result.isConfirmed) {
                const userInput = result.value;
                if (!isNaN(userInput)) {
                    await downloadGeoJSON(userInput);
                } else {
                    try {
                        const osmID = await getOSMID(userInput);
                        if (osmID) {
                            await downloadGeoJSON(osmID);
                        } else {
                            console.log('OSM ID not found for the provided place name.');
                            Swal.fire('Error', 'OSM ID not found for the provided place name.', 'error');
                        }
                    } catch (error) {
                        console.error('Error:', error);
                    }
                }
            } else if (result.dismiss === Swal.DismissReason.cancel) {
                console.log('No input provided.');
            }
        });}

    async function getOSMID(placeName) {
        const nominatimURL = `https://nominatim.openstreetmap.org/search?format=json&q=${placeName}&addressdetails=1&bounded=1&viewbox=-180,-90,180,90`;
        const response = await fetch(nominatimURL, {
            headers: {
                'Accept-Language': 'en-US,en;q=0.9'
            }
        });
        const data = await response.json();
        if (data.length > 0) {
            let options = {};
            for (let i = 0; i < Math.min(5, data.length); i++) {
                options[i + 1] = `${data[i].display_name}\n${data[i].address.country}`;
            }

            const { value: chosenIndex } = await Swal.fire({
                title: "Choose a location",
                input: 'select',
                inputOptions: options,
                inputValidator: (value) => {
                    if (value === '') {
                        return 'You must select a location';
                    }
                }
            });

            if (chosenIndex !== '') {
                const index = parseInt(chosenIndex);
                return data[index - 1].osm_id;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    async function downloadGeoJSON(osmID) {
        const url = `https://polygons.openstreetmap.fr/get_geojson.py?id=${osmID}`;

        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error('Failed to fetch GeoJSON data.');
            }

            const data = await response.json();
            const geojsonString = JSON.stringify(data);
            const blob = new Blob([geojsonString], { type: 'application/json' });
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            link.download = `osm_boundary_${osmID}.geojson`;
            link.click();
        } catch (error) {
            console.error('Error downloading GeoJSON:', error);
            alert('Error downloading GeoJSON')
        }
    }







    var triggerButton = document.createElement("BUTTON");
    triggerButton.innerHTML = "Feature Your Map";
    triggerButton.style.position = 'fixed';
    triggerButton.style.right = '20px';
    triggerButton.style.top = '12px';
    triggerButton.style.borderRadius = "16px";
    triggerButton.style.padding = "10px 20px";
    triggerButton.style.border = "none";
    triggerButton.style.backgroundColor = "#4CAF50";
    triggerButton.style.color = "white";
    triggerButton.style.cursor = "pointer";
    document.body.appendChild(triggerButton);

    var button = document.createElement('button');
    button.textContent = 'Download GeoJSON'
    button.style.position = 'fixed';
    button.style.right = '180px';
    button.style.top = '12px';
    button.style.borderRadius = "16px";
    button.style.padding = "10px 20px";
    button.style.border = "none";
    button.style.backgroundColor = "#4CAF50";
    button.style.color = "white";
    button.style.cursor = "pointer";
    document.body.appendChild(button);

    button.addEventListener('click', downloadGeoJSONFromOSMID);
    triggerButton.addEventListener("click", getFeatures);


})();