您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
get poi data from osm
// ==UserScript== // @name Feature Your Map // @version 2.4.2 // @description get poi data from osm // @author KaKa // @match https://map-making.app/maps/* // @grant GM_setClipboard // @license MIT // @icon https://www.google.com/s2/favicons?domain=geoguessr.com // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @namespace http://tampermonkey.net/ // ==/UserScript== (function() { 'use strict'; document.addEventListener('DOMContentLoaded', function() { document.addEventListener('change', function(event) { const checkbox = event.target; if (checkbox.type === 'checkbox') { const label = checkbox.parentElement; if (checkbox.checked) { label.classList.add('checked'); } else { label.classList.remove('checked'); } } }); }); let globalSettings = { selectedFeature: null, isIncluded: null, radius: null }; let mapFeatures={'way':['motorway','trunk','primary','secondary','tertiary','unclassified','footway','path','pedestrain','river','bridge','tunnel','roundabout','coastline'], 'node':[ 'bus stop', 'level crossing','milestone', 'crosswalk','traffic light','postbox', 'hydrant','utility pole', 'lamppost','waste basket', 'waste disposal','yield sign','stop sign','stadium','museum','school','motorway junction','tree','volcano','cape','hill'], 'relation':['grassland','forest','residental','farmland','meadow','paddy','vineyard'], 'nw':['bollard','hospital','train station','religious','government','hotel','estate agent']} let taglist = [['estate agent','"shop"="estate_agent"'], ['coastline','"natural"="coastline"'], ['bollard','"barrier"="bollard"'], ['vineyard','"landuse"="vineyard"'], ['paddy','"landuse"="paddy"'], ['meadow','"landuse"="meadow"'], ['residental','"landuse"="residental"'], ['farmland','"landuse"="farmland"'], ['hill','"natural"="hill"'], ['volcano', '"natural"="volcano"'], ['grassland','"natural"="grassland"'], ['forest','"natural"="wood"'], ['cape', '"natural"="cape"'] , ['tree', '"natural"="tree"'], ['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"'], ['tunnel','"tunnel"="yes"'], ['yield sign','"highway"="give_way"'], ['roundabout','"junction"="roundabout"'], ['waste basket','"amenity"="waste_basket"'], ['waste disposal','"amenity"="waste_disposal"'], ['hospital','"amenity"="hospital"'], ['government','"building"="government"'], ['religious','"amenity"="place_of_worship"'], ['stop sign','"highway"="stop"'], ['museum','"building"="museum"'], ['train station','"building"="train_station"'], ['stadium','"leisure"="stadium"'], ['school','"amenity"="school"'], ['hotel','"building"="hotel"'], ['motorway junction','"highway"="motorway_junction"'] ]; let categories = { 'Traffic': ['bridge', 'roundabout','tunnel', 'level crossing','bollard','milestone', 'crosswalk','yield sign','stop sign','motorway junction'], 'Public Facility': ['bus stop','postbox', 'hydrant','utility pole', 'lamppost','traffic light','waste basket','waste disposal'], 'Building':['government','school','hospital','stadium','museum','religious','hotel','estate agent'], 'Natural':['volcano','tree','cape','hill']}; let advancedCategories={'Intersection':['motorway','trunk','primary','secondary','tertiary','unclassified','footway','path','pedestrain'], 'Around Search':['bridge', 'bus stop', 'level crossing','milestone', 'crosswalk','traffic light','postbox', 'hydrant','utility pole', 'lamppost','river','government','school','hospital','waste basket','waste disposal','stadium','museum','religious', 'tunnel','roundabout','hotel','motorway junction','tree','volcano','cape','coastline','hill','forest','grassland', 'residental','farmland','meadow','paddy','vineyard']} 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.checked { 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 swal = Swal.fire({ title: 'Fetching Coordinates', text: 'Please wait...', allowOutsideClick: false, allowEscapeKey: false, showConfirmButton: false, didOpen: () => { Swal.showLoading(); } }); 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) { swal.close() if (mode === 'area') { Swal.fire('Error',"None retrived!The place name you entered may be incorrect,couldn't find this place.",'error'); } else if (mode === 'polygon') { Swal.fire('Error',"None retrived!Please check if your geojson file format is correct.",'error'); } else{ Swal.fire('Error',"None retrived!Please narrow the radius or select a less features combination.",'error')} } if (js.customCoordinates.length > 0) { swal.close() GM_setClipboard(JSON.stringify(js)); Swal.fire('Success',"JSON data has been copied to your clipboard!",'success'); } } catch (error) { Swal.fire('Error',`Error fetching data${error}:`,'error'); } } function getFeatureElement(f){ for (const key in mapFeatures) { if (mapFeatures.hasOwnProperty(key)) { if (mapFeatures[key].includes(f)) { return key }}}} function getCategoryHtml(categories) { let html = ''; for (let category in categories) { html += `<input type="radio" name="category" value="${category}" id="swal-input-${category}"> <label for="swal-input-${category}">${category}</label><br>`; } return html; } function getSettingFeaturesHtml(features) { let html = ''; for (let feature of features) { html += `<input type="radio" name="feature" value="${feature}" id="swal-input-${feature}"> <label for="swal-input-${feature}">${feature}</label><br>`; } return html; } async function getSettings() { const resetSettings = () => { globalSettings.selectedFeature = null; globalSettings.isIncluded = null; globalSettings.radius = null; }; const setSettings = (feature, isIncluded, radius) => { globalSettings.selectedFeature = feature; globalSettings.isIncluded = isIncluded; globalSettings.radius = radius; }; resetSettings(); let settingCategories = { 'Transportation': ['bridge', 'roundabout', 'tunnel', 'level crossing', 'milestone', 'crosswalk', 'yield sign', 'stop sign', 'motorway junction'], 'Public Facility': ['bus stop', 'postbox', 'hydrant', 'utility pole', 'lamppost', 'traffic light', 'waste basket', 'waste disposal'], 'Building': ['government', 'school', 'hospital', 'stadium', 'museum', 'religious', 'hotel'], 'Natural': ['volcano', 'tree', 'cape', 'hill', 'forest', 'grassland', 'coastline'], 'Landuse': ['farmland', 'paddy', 'meadow', 'vineyard', 'residental'] }; let selectedSettingCategory = null; const { value: selectedCategory, dismiss: initializeSettings } = await Swal.fire({ title: 'Setting Categories', html: getCategoryHtml(settingCategories), focusConfirm: false, allowOutsideClick: false, showCancelButton: true, showCloseButton: true, cancelButtonText: 'Reset Settings', preConfirm: () => { const selectedCategoryRadio = document.querySelector('input[name="category"]:checked'); if (!selectedCategoryRadio) { Swal.showValidationMessage('Please select a category'); return; } selectedSettingCategory = selectedCategoryRadio.value; return selectedSettingCategory; } }); if (initializeSettings=='cancel') { Swal.fire({ icon: 'success', title: 'Settings Reset', text: 'Your settings have been successfully reset.', showConfirmButton: false, timer: 1500 }); resetSettings(); } if (selectedCategory) { let selectedSettingFeatures = settingCategories[selectedSettingCategory]; const { value: selectedFeature, dismiss: cancelInput } = await Swal.fire({ title: 'Setting Features', html: getSettingFeaturesHtml(selectedSettingFeatures) + '<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: false, showCancelButton: true, preConfirm: () => { const selectedFeatureRadio = document.querySelector('input[name="feature"]:checked'); if (!selectedFeatureRadio) { Swal.showValidationMessage('Please select a feature'); return; } return selectedFeatureRadio.value; } }); if (selectedFeature) { const { value: isIncluded, dismiss: cancelInclude } = await Swal.fire({ title: 'Select POI Range', text: 'Do you want to include POIs within a certain range or outside of it?', icon: 'question', showCancelButton: true, confirmButtonText: 'Within Range', cancelButtonText: 'Outside Range', allowOutsideClick:false, }); let radius = 50; if (isIncluded !== Swal.DismissReason.cancel) { const { value: inputRadius, dismiss: cancelRadius } = await Swal.fire({ title: 'Enter Radius', input: 'number', inputLabel: 'Radius (in meters)', inputPlaceholder: 'Enter the radius for POIs', inputAttributes: { min: 5, max: 10000, step: 100, }, showCancelButton: true, confirmButtonText: 'OK', cancelButtonText: 'Cancel', allowOutsideClick:false, inputValidator: (value) => { if (!value) { return 'You need to enter a radius!'; } const radiusValue = parseInt(value); if (radiusValue < 5 || radiusValue > 10000) { return 'Radius must be between 5 and 10000 meters!'; } } }); if (inputRadius !== undefined && inputRadius !== null) { radius = parseInt(inputRadius); } } if (selectedFeature && isIncluded !== Swal.DismissReason.cancel) { const filteredTags = taglist.filter(tag => selectedFeature.includes(tag[0])); setSettings(filteredTags[0], isIncluded, radius); Swal.fire({ icon: 'success', title: 'Settings Updated', text: 'Your settings have been successfully updated.', showConfirmButton: false, timer: 1200 }); } } } } function getRequestBody(features, mode, advanced, query) { let requestBody = ""; var selectedFeatures=globalSettings.selectedFeature const outJsonTimeout = "[out:json][timeout:180];"; if (advanced === "Intersection") { if (globalSettings.selectedFeature){ if (globalSettings.isIncluded){ requestBody = `${outJsonTimeout}${getFeatureElement(selectedFeatures[0])}[${selectedFeatures[1]}](poly:"${query}"); way(poly:"${query}")[highway~"^(${features[0].join('|')})$"]->.w1; way(poly:"${query}")[highway~"^(${features[1].join('|')})$"]->.w2; node(w.w1)(w.w2)(around:${globalSettings.radius}); out geom;`;} else { requestBody = `${outJsonTimeout}(${getFeatureElement(selectedFeatures[0])}[${selectedFeatures[1]}](poly:"${query}");)->.default; way(poly:"${query}")[highway~"^(${features[0].join('|')})$"]->.w1; way(poly:"${query}")[highway~"^(${features[1].join('|')})$"]->.w2; node(w.w1)(w.w2)->.all; (node.all(around.default:${globalSettings.radius});)->.inner; (.all; - .inner;); out geom meta;` } } else { requestBody = `${outJsonTimeout} 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") { const aroundPoint = features[1]; const aroundFeature = features[0].find(feature => feature[0] === aroundPoint); const resultFeature = features[0].find(feature => feature[0] !== aroundPoint); const aroundParams = mode === "coordinate" ? `around:${features[2].join(',')}` : `around:${features[2]}`; requestBody = `${outJsonTimeout} ${getFeatureElement(aroundFeature[0])}(poly:"${query}")[${aroundFeature[1]}]; ${getFeatureElement(resultFeature[0])}(${aroundParams})[${resultFeature[1]}]; out geom;`; } else { if (globalSettings.selectedFeature){ if (globalSettings.isIncluded){ requestBody = `${outJsonTimeout}${getFeatureElement(selectedFeatures[0])}[${selectedFeatures[1]}](poly:"${query}"); ${getFeatureElement(features[0])}(around:${globalSettings.radius})[${features[1]}]; out geom;`;} else { requestBody = `${outJsonTimeout}(${getFeatureElement(selectedFeatures[0])}[${selectedFeatures[1]}](poly:"${query}");)->.default; (${getFeatureElement(features[0])}[${features[1]}](poly:"${query}");)->.all; (${getFeatureElement(features[0])}.all(around.default:${globalSettings.radius});)->.inner; (.all; - .inner;); out geom meta;` } } else{ requestBody = `${outJsonTimeout}${getFeatureElement(features[0])}[${features[1]}](poly:"${query}");out geom;`;} } return requestBody; } function writeData(coordinates, feature, js,advanced) { for (let i = 0; i < coordinates.length; i++) { let tag; if (coordinates[i].geometry) { let nodes = coordinates[i].geometry; let medianIndex = Math.floor(nodes.length / 2); let medianCoordinate = nodes[medianIndex] if (coordinates[i].tags && coordinates[i].tags.highway) { tag = [coordinates[i].tags.highway ,feature]; } else { tag = [feature]; } if (medianCoordinate.lat && medianCoordinate.lon) { if (advanced=== 'Intersection') { tag=['Intersection'] } 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); } } if(coordinates[i].tags.religion) tag.push(coordinates[i].tags.religion) js.customCoordinates.push({ "lat": medianCoordinate.lat, "lng": medianCoordinate.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') { tag=['Intersection']; } 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); } } if(coordinates[i].tags.religion) tag.push(coordinates[i].tags.religion) 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 promptInput(f,a){ const input = document.createElement('input'); input.type = 'file'; input.style.position = 'absolute'; input.style.right = '450px'; input.style.top = '15px'; input.style.display='none' input.addEventListener('change', async event => { const file = event.target.files[0]; if (file) { try { var query = await readFile(file); getData(query, 'polygon', f, a); document.body.removeChild(input); } catch (error) { console.error('Error reading file:', error); document.body.removeChild(input); } } else { if (document.getElementById('uploadButton')) { document.getElementById('uploadButton').remove(); } } }); input.click(); document.body.appendChild(input); input.addEventListener('cancel', () => { if (document.getElementById('uploadButton')) { document.getElementById('uploadButton').remove(); } }); } async function getInput(features, advanced) { const { value: upload ,dismiss:inputDismiss} = await Swal.fire({ title: 'Query Scope Setting', text: 'Do you want to upload a GeoJson file? Else you will need to enter a place name or OSM ID to get GeoJson file.You could also draw polygons on the map and download it as GeoJson file from map-making.', icon: 'question', showCancelButton: true, showCloseButton:true, allowOutsideClick: false, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Upload', cancelButtonText: 'Enter Place Name' }); if (upload) { promptInput(features,advanced) } else if(inputDismiss==='cancel') { await downloadGeoJSONFromOSMID(features,advanced) } } function extractCoordinates(p) { let results = []; if (p.features){ let polygons=p.features polygons.forEach(data => { const 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){ const 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 if(p.geometry){ const coordinates = []; p.geometry.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}"> <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}" > <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() { let selectedCategories = []; const { value: selectedMainCategories, dismiss: mainCategoriesDismiss } = 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 filteredTags = taglist.filter(tag => selectedSubFeatures.includes(tag[0])); features.push(...filteredTags); runScript(features,'') } } else if (mainCategoriesDismiss === "cancel"){ 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 = []; let titleText='Select Features'; if (selectedAdvancedCategories.includes('Intersection')) { titleText = 'Select Major way'; } for (let category of selectedAdvancedCategories) { selectedFeatures = selectedFeatures.concat(advancedCategories[category]); } const { value: selectedSubFeatures, dismiss: cancelInput } = await Swal.fire({ title: titleText, 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')) { 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> <p>You could also enter a coordinate as around point to search for points that match the features you selected(e.g. 35.12,129.08)</p> <div> <input type="text" id="coordinate" class="swal2-input"> </div> `, showCancelButton: true, preConfirm: () => { const aroundPoint = document.getElementById('aroundPoint').value; const coordinates = document.getElementById('coordinate').value.trim(); const checkFeatures = selectedSubFeatures; const aroundPointIndex = checkFeatures.indexOf(aroundPoint); checkFeatures.splice(aroundPointIndex, 1); const hasRealationFeature = checkFeatures.some(feature => mapFeatures.relation.includes(feature)); if (hasRealationFeature) { Swal.showValidationMessage('Realtion type of points must be set as around point!Better select only one relation type of feature.'); return false; } if (isNaN(coordinates) ||selectedSubFeatures.length===1) { Swal.showValidationMessage('Please enter a coordinate or select more than 2 features!'); return false; if (coordinates) { const [latitude, longitude] = coordinates.split(',').map(coord => parseFloat(coord.trim())); if (isNaN(latitude) || isNaN(longitude)) { Swal.showValidationMessage('Please enter a valid coordinate!'); return false; } } 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 > 10000) { return 'Please enter a valid integer between 10 and 10000!'; } } }).then((result) => { if (result.isConfirmed) { const radius = result.value; return [selectedSubFeatures, radius, [latitude, longitude]]; } else { return false; } }); } else { 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 > 10000) { return 'Please enter a valid integer between 10 and 10000!'; } } }).then((result) => { if (result.isConfirmed) { const radius = result.value; return [selectedSubFeatures, aroundPoint, radius]; } else { return false; } }); } } }); } else{return selectedSubFeatures}} }); if (selectedSubFeatures) { const features = []; let filteredTags; if (selectedAdvancedCategories.includes('Intersection')){ const intersectionFeatures=selectedSubFeatures.value let majorTags let minorTags features.push(...[intersectionFeatures[0]],...[intersectionFeatures[1]]); runScript(features,'Intersection') } if (selectedAdvancedCategories.includes('Around Search')){ let aroundFeatures=selectedSubFeatures.value filteredTags = taglist.filter(tag => aroundFeatures[0].includes(tag[0])) features.push(...filteredTags) if (Array.isArray(aroundFeatures[2])) { getData('','coordinate',[features,aroundFeatures[1],aroundFeatures[2]],'Around')} else{ runScript([features,aroundFeatures[1],aroundFeatures[2]],'Around')} } } } } } async function downloadGeoJSONFromOSMID(f,a) { 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); if(f||a){ setTimeout(function() {promptInput(f,a)},500) } } else { 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`; 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, showCancelButton:true, 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 = 'absolute'; triggerButton.style.right = '10px'; triggerButton.style.top = '8px'; triggerButton.style.width='160px' triggerButton.style.fontSize='16px' triggerButton.style.borderRadius = "16px"; triggerButton.style.padding = "5px 5px"; 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 = 'absolute'; button.style.right = '180px'; button.style.top = '8px'; button.style.width='160px' button.style.fontSize='16px' button.style.borderRadius = "16px"; button.style.padding = "5px 5px"; button.style.border = "none"; button.style.backgroundColor = "#4CAF50"; button.style.color = "white"; button.style.cursor = "pointer"; document.body.appendChild(button); var settingButton = document.createElement('button'); settingButton.textContent = 'Default Setting' settingButton.style.position = 'absolute'; settingButton.style.right = '350px'; settingButton.style.top = '8px'; settingButton.style.width='160px' settingButton.style.fontSize='16px' settingButton.style.borderRadius = "16px"; settingButton.style.padding = "5px 5px"; settingButton.style.border = "none"; settingButton.style.backgroundColor = "#4CAF50"; settingButton.style.color = "white"; settingButton.style.cursor = "pointer"; document.body.appendChild(settingButton); settingButton.addEventListener('click', getSettings) button.addEventListener('click', downloadGeoJSONFromOSMID); triggerButton.addEventListener("click", getFeatures); })();