// ==UserScript==
// @name Feature Your Map
// @namespace http://tampermonkey.net/
// @version 1.1
// @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';
const API_URL = "https://overpass-api.de/api/interpreter";
async function fetchData(query, mode,feature) {
const requestBody = !mode?
`[out:json][timeout:180];area[name="${query}"]; (nwr(area)[${feature}]; );out geom;` :
`[out:json][timeout:180];nwr[${feature}](poly:"${query}");out geom;`;
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) {
try {
const js = {
"name": "",
"customCoordinates": [],
"extra": {
"tags": {},
"infoCoordinates": []
}
};
let elements = [];
for (let feature of features) {
let requests = [];
if ( mode === 'polygon') {
requests.push(fetchData(query, true, feature[1]));
} else {
requests.push(fetchData(query, false, feature[1]));
}
const responses = await Promise.all(requests);
responses.forEach(response => {
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 writeData(coordinates, feature, js) {
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) {
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)) {
js.customCoordinates.push({
"lat": coordinates[i].lat,
"lng": coordinates[i].lon,
"extra": {
"tags": [feature]
}
});
}
}
}
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) {
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 = '120px';
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)
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);
}
}
}
}
function extractCoordinates(p) {
let results = [];
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);
});
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.features && data.features.length > 0) {
const coordinates = extractCoordinates(data);
resolve(coordinates);
}
else {
console.error('Invalid Geojson format.');
alert('Invalid Geojson format!');
resolve('error')
}
} catch (error) {
console.error('Error parsing Geojson:', error);
alert('Error parsing Geojson!');
resolve('error')
}
};
reader.readAsText(file);
});
}
function runScript(features){
if (features&&features.length>0){
getInput(features)}
else{
alert('Please select a feature at least!')}
}
async function getFeatures() {
const { value: formValues } = await Swal.fire({
title: 'Feature Your Map',
html:
'<div style="display: flex; justify-content: space-between;"><div style="display: flex; align-items: center;"><input id="swal-input1" class="swal2-input" type="checkbox" value="bridge"><span style="margin-left: 1px;">bridge</span></div><div style="display: flex; align-items: center;"><input id="swal-input2" class="swal2-input" type="checkbox" value="bus stop"><span style="margin-right: 95px;">bus stop</span></div></div>' +
'<div style="display: flex; justify-content: space-between;"><div style="display: flex; align-items: center;"><input id="swal-input3" class="swal2-input" type="checkbox" value="utility pole"><span style="margin-left: 1px;">utility pole</span></div><div style="display: flex; align-items: center;"><input id="swal-input4" class="swal2-input" type="checkbox" value="traffic light"><span style="margin-right:78px;">traffic light</span></div></div>' +
'<div style="display: flex; justify-content: space-between;"><div style="display: flex; align-items: center;"><input id="swal-input5" class="swal2-input" type="checkbox" value="lamppost"><span style="margin-left: 1px;">lamppost</span></div><div style="display: flex; align-items: center;"><input id="swal-input6" class="swal2-input" type="checkbox" value="crosswalk"><span style="margin-right: 85px;">crosswalk</span></div></div>' +
'<div style="display: flex; justify-content: space-between;"><div style="display: flex; align-items: center;"><input id="swal-input7" class="swal2-input" type="checkbox" value="level crossing"><span style="margin-left: 0.5px;">level crossing</span></div><div style="display: flex; align-items: center;"><input id="swal-input8" class="swal2-input" type="checkbox" value="postbox"><span style="margin-right: 85px;">postbox</span></div></div>' +
'<div style="display: flex; justify-content: space-between;"><div style="display: flex; align-items: center;"><input id="swal-input9" class="swal2-input" type="checkbox" value="hydrant"><span style="margin-left: 1px;">hydrant</span></div><div style="display: flex; align-items: center;"><input id="swal-input10" class="swal2-input" type="checkbox" value="milestone"><span style="margin-right: 82px;">milestone</span></div></div>',
focusConfirm: false,
preConfirm: () => {
return [
document.getElementById('swal-input1').checked,
document.getElementById('swal-input2').checked,
document.getElementById('swal-input3').checked,
document.getElementById('swal-input4').checked,
document.getElementById('swal-input5').checked,
document.getElementById('swal-input6').checked,
document.getElementById('swal-input7').checked,
document.getElementById('swal-input8').checked,
document.getElementById('swal-input9').checked,
document.getElementById('swal-input10').checked
]
}
})
if (formValues) {
var features = []
var 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"']]
for (var i = 0; i < formValues.length; i++) {
if (formValues[i]) {
features.push(tags[i])
}
}
runScript(features)
}
}
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 = "12px";
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);
triggerButton.addEventListener("click", getFeatures);
})();