Map-Making Shortcuts

use shortcut to help you mapping on map-making app

// ==UserScript==
// @name         Map-Making Shortcuts
// @namespace    https://greasyfork.org/users/1179204
// @description  use shortcut to help you mapping on map-making app
// @version      1.1.8
// @license      BSD
// @author       KaKa
// @match        *://map-making.app/maps/*
// @icon         https://www.svgrepo.com/show/521871/switch.svg
// ==/UserScript==
(function() {
    let editor,selections,currentIndex,map,mapListener,isApplied=false,customZoom=0.43,isCustom=false,isASV,isYSV

    function getEditor() {
        map=unsafeWindow.map
        if(!map)getMap()
        editor = unsafeWindow.editor
        const activeSelections = editor.selections;
        const locations=unsafeWindow.locations
        selections = activeSelections.length > 0 ? activeSelections.flatMap(selection => selection.locations) : locations;
    }

    function exportAsCsv(){
        if(!selections)getEditor()
        const csvContent = jsonToCSV(selections);
        downloadCSV(csvContent);
    }

    function downloadCSV(csvContent, fileName = "output.csv") {
        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        if (link.download !== undefined) {
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', fileName);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }

    function getTagsForRow(item, maxTags) {
        const tags = item.tags || [];
        return Array.from({ length: maxTags }, (_, index) => tags[index] || '');
    }

    function getFormattedDate(dateStr) {
        if (!dateStr) return '';
        const date = new Date(dateStr);
        if (isNaN(date.getTime())) return '';
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        return `${year}-${month}`;
    }

    function getMaxTagCount(jsonData) {
        let maxTags = 0;
        jsonData.forEach(item => {
            if (item.tags && item.tags.length > maxTags) {
                maxTags = item.tags.length;
            }
        });
        return maxTags;
    }

    function jsonToCSV(jsonData) {
        const maxTags = getMaxTagCount(jsonData);
        const tagHeaders = Array.from({ length: maxTags }, (_, i) => `tag${i + 1}`);
        const headers = ["lat", "lng", "panoId", "heading", "pitch", "zoom", "date", ...tagHeaders];
        const rows = jsonData.map(item => {
            const lat = item.location.lat|| '';
            const lng = item.location.lng|| '';
            const panoId = item.panoId|| '';
            const heading = item.heading|| '';
            const pitch = item.pitch|| '';
            const zoom = item.zoom || '';
            const date = getFormattedDate(item.panoDate)||'';
            const tags = getTagsForRow(item, maxTags);

            return [
                lat,
                lng,
                panoId,
                heading,
                pitch,
                zoom,
                date,
                ...tags
            ];
        });

        const csvContent = [headers, ...rows].map(row => row.join(",")).join("\n");
        return csvContent;
    }

    function switchLoc(locs) {
        if(editor.currentLocation) editor.closeLocation(editor.currentLocation.updatedProps)
        if (!currentIndex) {
            currentIndex =1;
        } else {
            currentIndex +=1
            if (currentIndex>locs.length){
                currentIndex=1
            }
        }
        editor.openLocation(locs[currentIndex-1]);
        focusOnLoc(locs[currentIndex-1])
        if(isCustom)setTimeout(function(){setZoom(customZoom)},100)
    }

    function rewindLoc(locs) {
        if(editor.currentLocation) editor.closeLocation(editor.currentLocation.updatedProps)
        if (!currentIndex) {
            currentIndex =1;
        }
        else {
            currentIndex -=1
            if (currentIndex<1) currentIndex=selections.length
        }
        editor.openLocation(locs[currentIndex-1]);
        focusOnLoc(locs[currentIndex-1])
        if(isCustom)setTimeout(function(){setZoom(customZoom)},100)
    }

    function focusOnLoc(loc){
        map.setCenter(loc.location)
        map.setZoom(17)
    }

    function deleteLoc(loc){
        editor.closeAndDeleteLocation(loc)
    }

    function rewindSelections(loc){
        currentIndex=1
        editor.openLocation(selections[0])
        if(isCustom)setTimeout(function(){setZoom(customZoom)},100)
    }

    function setZoom(z){
        if(z<0)z=0
        if(z>4)z=4
        const svControl=unsafeWindow.streetView
        svControl.setZoom(z)

    }

    function resetDefaultZoom(){
        if(mapListener) return
        mapListener=function(){
            if(isCustom){
                let intervalId=setInterval(function(){
                    editor = unsafeWindow.editor
                    if(editor.currentLocation){
                        setZoom(customZoom)
                        clearInterval(intervalId)}
                },50);

            }
        }
        map.addListener('click', mapListener);
    }

    function getTag(index){

        const tags=unsafeWindow.editor.tags
        const result = Object.keys(tags).find(tag => tags[tag].order === index - 1)
        if(result){
            return result.trim()}
    }
    function customLayer(name,tileUrl,maxZoom,minZoom){
        return new google.maps.ImageMapType({
            getTileUrl: function(coord, zoom) {
                return tileUrl
                    .replace('{z}', zoom)
                    .replace('{x}', coord.x)
                    .replace('{y}', coord.y);
            },
            tileSize: new google.maps.Size(256, 256),
            name: name,
            maxZoom:maxZoom,
            minZoom:minZoom||1
        });
    }


    function setHW(){
        const tileUrl = `https://maprastertile-drcn.dbankcdn.cn/display-service/v1/online-render/getTile/23.12.09.11/{z}/{x}/{y}/?language=zh&p=46&scale=2&mapType=ROADMAP&presetStyleId=standard&pattern=JPG&key=DAEDANitav6P7Q0lWzCzKkLErbrJG4kS1u%2FCpEe5ZyxW5u0nSkb40bJ%2BYAugRN03fhf0BszLS1rCrzAogRHDZkxaMrloaHPQGO6LNg==`
        const tileLayer=customLayer('Petal_Maps',tileUrl,20)
        map.mapTypes.stack.layers[0]=tileLayer
        map.setMapTypeId('stack')
    }

    function setYandex(){
        const svUrl=`https://core-stv-renderer.maps.yandex.net/2.x/tiles?l=stv&x={x}&y={y}&z={z}&scale=1&v=2024.12.13.20.46-1_24.12.12-3-22942`
        const svLayer=customLayer('Yandex_StreetView',svUrl,20,7)
        map.mapTypes.stack.layers.splice(2, 0,svLayer)
        map.mapTypes.set("stack",map.mapTypes.stack.layers)
        map.setMapTypeId('stack')
    }
    function setOriginGoogleMap(){
        const svUrl=`https://www.google.com/maps/vt?pb=!1m5!1m4!1i{z}!2i{x}!3i{y}!4i256!2m1!2sm!3m17!2sen!3sUS!5e18!12m4!1e68!2m2!1sset!2sRoadmap!12m3!1e37!2m1!1ssmartmaps!12m4!1e26!2m2!1sstyles!2ss.e:l|p.v:off,s.t:0.8|s.e:g.s|p.v:on!5m1!5f1.5`
        const svLayer=customLayer('Google_Maps',svUrl,20)
        map.mapTypes.stack.layers.splice(0, 0,svLayer)
        map.setMapTypeId('stack')
    }
    function setApple(){
        const svUrl=`https://lookmap.eu.pythonanywhere.com/bluelines_raster_2x/{z}/{x}/{y}.png`
        const svLayer=customLayer('Apple_StreetView',svUrl,16)
        map.mapTypes.stack.layers.splice(2, 0,svLayer)
        map.setMapTypeId('stack')

    }
    async function downloadTile(id,g) {
        try {
            const response = await fetch(`https://streetviewpixels-pa.googleapis.com/v1/tile?cb_client=apiv3&panoid=${id}&output=tile&x=${g==='Gen4'?18:16}&y=${g==='Gen4'?13:11}&zoom=5&nbt=1&fover=2`);
            const imageBlob = await response.blob();
            const img = new Image();
            img.onload = function() {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = img.width;
                canvas.height = img.height;
                ctx.drawImage(img, 0, 0);

                const dataUrl = canvas.toDataURL('image/jpeg');
                const link = document.createElement('a');
                link.href = dataUrl;
                link.download = id+'.jpg';
                link.click();
            };
            img.src = URL.createObjectURL(imageBlob);
        } catch (error) {
            console.error('Error:', error);
        }
    }
    function lon2tile(lng,zoom) {
        return (lng+180)/360*Math.pow(2,zoom);
    }
    function lat2tile(lat,zoom){
        return (1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom);
    }

    function lonLatToEpsg3395Tile(lng, lat, zoom) {

        return [lon2tile(lng,zoom), lat2tile(lat,zoom)];

    }
    function getMap(){
        let element = document.getElementsByClassName("map-embed")[0]
        try{
            //if (!element) element=document.getElementsByClassName("coordinate-result-map_map__Yh2Il")[0]
            const keys = Object.keys(element)
            const key = keys.find(key => key.startsWith("__reactFiber$"))
            const props = element[key]
            map=props.pendingProps.children[1].props.children[1].props.map
        }
        catch(error){
            console.error('Failed to get map')
        }
    }

    let onKeyDown = async (e) => {
        if (e.target.tagName === 'TEXTAREA' || e.target.isContentEditable||!isApplied) {
            return;
        }
        if(!editor)getEditor()
        if (e.key === 'q' || e.key === 'Q') {
            e.stopImmediatePropagation();
            switchLoc(selections)
        };

        if (e.key === 'e' || e.key === 'E') {
            e.stopImmediatePropagation();
            rewindLoc(selections)
        };
        if (e.key === 'c' || e.key === 'C') {
            e.stopImmediatePropagation();
            deleteLoc(selections[currentIndex-1])
        };
        if (e.key === 'v' || e.key === 'V') {
            e.stopImmediatePropagation();
            if(editor.currentLocation) editor.closeLocation(editor.currentLocation.updatedProps)
        };
        if (e.key === 'r' || e.key === 'R') {
            e.stopImmediatePropagation();
            rewindSelections()
        };
        if ((e.shiftKey)&&(e.key === 'd' || e.key === 'D')) {
            exportAsCsv()
        }
        if ((e.shiftKey)&&(e.key === 'h' || e.key === 'H')) {
            setHW()
        }
        if ((e.shiftKey)&&(e.key === 'y' || e.key === 'Y')) {
            setYandex()
        }
        if ((e.shiftKey)&&(e.key === 'p' || e.key === 'P')) {
            setApple()
        }
        if (e.key === 'g' || e.key === 'G') {
            e.stopImmediatePropagation();
            resetDefaultZoom()
            if(!isCustom) {
                var input = prompt('please enter a zoom value(0-4):');

                var parsedValue = parseFloat(input);

                if (isNaN(parsedValue)) {
                    alert('The input is not a valid zoom! Please try again.');
                    isCustom=false
                } else {
                    customZoom=parsedValue
                    isCustom=true
                    resetDefaultZoom()
                    alert('Custom zoom has been applied!');
                }
            }
            else {
                isCustom=false
                alert('Zoom customizing is cancelled!')}
        }

        if(e.key === 'j' || e.key === 'J'){
            const button = Array.from(document.querySelectorAll('.map-control__menu-button')).find(function(b) {
                return b.textContent.includes('Settings');
            });
            button.click()
            setTimeout(function(){
                const label = Array.from(document.querySelectorAll('label')).find(function(label) {
                    return label.textContent.includes('Show crosshair');
                });

                if (label) {
                    const checkbox = label.querySelector('input[type="checkbox"]');

                    if (checkbox) {
                        checkbox.click()
                        button.click()
                    }

                }
            },200)

        }
    }
    document.addEventListener("keydown", onKeyDown);

    var shortCutButton = document.createElement('button');
    shortCutButton.textContent = 'Shortcut Off';
    shortCutButton.style.position = 'absolute';
    shortCutButton.style.top = '8px';
    shortCutButton.style.right = '700px';
    shortCutButton.style.zIndex = '9999';
    shortCutButton.style.borderRadius = "18px";
    shortCutButton.style.padding = "5px 10px";
    shortCutButton.style.border = "none";
    shortCutButton.style.backgroundColor = "#4CAF50";
    shortCutButton.style.color = "white";
    shortCutButton.style.cursor = "pointer";
    shortCutButton.addEventListener('click', function(){
        if(isApplied){
            isApplied=false
            shortCutButton.style.border='none'
            shortCutButton.textContent = 'Shortcut Off';
        }
        else {isApplied=true
              shortCutButton.textContent = 'ShortCut On';
              shortCutButton.style.border='2px solid #fff'}

    });
    document.body.appendChild(shortCutButton)
})();