Reversewigo solver ✓🐵 - solve reverse wherigo automatically on cache page. Solver code originated from Rick Rickhardson's reverse-wherigo source (public domain). Also, creates show source button for mystery cacahes.
Ekde
// ==UserScript== // @name Reversewigo solver ✓🐵 // @namespace http://tampermonkey.net/ // @version 0.09 DEV // @description Reversewigo solver ✓🐵 - solve reverse wherigo automatically on cache page. Solver code originated from Rick Rickhardson's reverse-wherigo source (public domain). Also, creates show source button for mystery cacahes. // @author [email protected] // @include https://www.geocaching.com/geocache/* // ==/UserScript== // placeholder for calculated reverse wherigo coordinates // used by pollDOM to update them to popup dialog // TODO: definitely not the best practise but a quick hack that seems to work (for now) var newCoordinates = ""; /* async */ function dd2dm(latdd, londd) { var lat = latdd; var lon = londd; var latResult, lonResult, dmsResult; lat = parseFloat(lat); lon = parseFloat(lon); latResult = /* await */ lat_dd2dm(lat); lonResult = /* await */ lon_dd2dm(lon); // Joining both variables and separate them with a space. dmsResult = latResult + ' ' + lonResult; // Return the resultant string return { result: dmsResult, lat: latResult, lon: lonResult }; } /* async */ function lat_dd2dm(val) { var valDeg, valMin, result; val = Number(val); result = (val >= 0)? 'N' : 'S'; result += " "; val = Math.abs(val); valDeg = Math.floor(val); if( valDeg < 10 ) { result += "0" + valDeg; } else { result += valDeg; } result += "\xB0"; result += " "; valMin = ((val - valDeg) * 60).toFixed(3); if( valMin < 10) { result += "0" + valMin; } else { result += valMin; } return result; } /* async */ function lon_dd2dm(val) { var valDeg, valMin, result; val = Number(val); result = (val >= 0)? 'E' : 'W'; result += " "; val = Math.abs(val); valDeg = Math.floor(val); if( valDeg < 10 ) { result += "00" + valDeg; } else if( valDeg < 100) { result += "0" + valDeg; } else { result += valDeg; } result += "\xB0"; result += " "; valMin = ((val - valDeg) * 60).toFixed(3); if( valMin < 10) { result += "0" + valMin; } else { result += valMin; } return result; } /* async */ function code2LatLon(varA, varB, varC) { let latSign, lonSign, lonValue, latValue; // 123456 => digit 1 (d1) = 6; digit 2 (d2) = 5; ... // syntax for varA => digit 1 var A = A1; digit 2 varA = A2; ... // A1 if ((varA % 1000 - varA % 100) / 100 == 1) { latSign = 1; lonSign = 1; } // A1 else if ((varA % 1000 - varA % 100) / 100 == 2) { latSign = -1; lonSign = 1; } // A1 else if ((varA % 1000 - varA % 100) / 100 == 3) { latSign = 1; lonSign = -1; } // A1 else if ((varA % 1000 - varA % 100) / 100 == 4) { latSign = -1; lonSign = -1; } //T41140 / Q1TQ01 / 14S4RS // A6 B3 B4 B6 C1 C2 C4 // TODO: how to iterate only these, not full range ?? // C (d5 + d2) eli C5 + C2 = parillinen if ( ((varC % 100000 - varC % 10000) / 10000 + (varC % 100 - varC % 10) / 10) % 2 === 0) { // A4 B2 B5 C3 A6 C2 A1 latValue = Number(((varA % 10000 - varA % 1000) / 1000 * 10 + (varB % 100 - varB % 10) / 10 + (varB % 100000 - varB % 10000) / 10000 * 0.1 + (varC % 1000 - varC % 100) / 100 * 0.01 + (varA % 1000000 - varA % 100000) / 100000 * 0.001 + (varC % 100 - varC % 10) / 10 * 1.0E-4 + varA % 10 * 1.0E-5)); // A5 C6 C1 B3 B6 A2 C5 B1 lonValue = Number(((varA % 100000 - varA % 10000) / 10000 * 100 + (varC % 1000000 - varC % 100000) / 100000 * 10 + varC % 10 + (varB % 1000 - varB % 100) / 100 * 0.1 + (varB % 1000000 - varB % 100000) / 100000 * 0.01 + (varA % 100 - varA % 10) / 10 * 0.001 + (varC % 100000 - varC % 10000) / 10000 * 1.0E-4 + varB % 10 * 1.0E-5)); } // C (d5 + d2) eli C5+C2= pariton else if ( ((varC % 100000 - varC % 10000) / 10000 + (varC % 100 - varC % 10) / 10) % 2 !== 0) { // B6 A1 A4 C6 C3 C2 A6 latValue = Number(((varB % 1000000 - varB % 100000) / 100000 * 10 + varA % 10 + (varA % 10000 - varA % 1000) / 1000 * 0.1 + (varC % 1000000 - varC % 100000) / 100000 * 0.01 + (varC % 1000 - varC % 100) / 100 * 0.001 + (varC % 100 - varC % 10) / 10 * 1.0E-4 + (varA % 1000000 - varA % 100000) / 100000 * 1.0E-5)) // B2 C1 A2 A5 B3 B1 C5 B5 lonValue = Number(((varB % 100 - varB % 10) / 10 * 100 + varC % 10 * 10 + (varA % 100 - varA % 10) / 10 + (varA % 100000 - varA % 10000) / 10000 * 0.1 + (varB % 1000 - varB % 100) / 100 * 0.01 + varB % 10 * 0.001 + (varC % 100000 - varC % 10000) / 10000 * 1.0E-4 + (varB % 100000 - varB % 10000) / 10000 * 1.0E-5)); } // A3 B4 C4 = ALWAYS ignore latValue = latSign * latValue; lonValue = lonSign * lonValue; return { lat: latValue, lon: lonValue } } function fix(v, left, right) { return (v < 0 ? '-' : ' ') + Math.abs(v).toFixed(right).padStart(left + right + 1, '0'); } function haversineDistance(lat1,lon1,lat2,lon2) { const R = 6371e3; // metres const φ1 = lat1 * Math.PI/180; // φ, λ in radians const φ2 = lat2 * Math.PI/180; const Δφ = (lat2-lat1) * Math.PI/180; const Δλ = (lon2-lon1) * Math.PI/180; const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ/2) * Math.sin(Δλ/2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); const d = R * c; // in metres return d; } function dm2dd( nsew, deg, min ) { let sign; if( nsew == 'N' || nsew == 'E' ) { sign = 1; } else { sign = -1; } let numdeg = parseInt(deg); let nummin = parseFloat(min); let dd = sign * (numdeg + nummin/60); return dd; } function createShowSourceButton() { let mybutton = document.createElement("button"); mybutton.innerHTML = "Source"; let noteref = document.getElementsByClassName("minorCacheDetails Clear")[0]; noteref.appendChild(mybutton); mybutton.addEventListener("click", function() { var desc1 = document.getElementById("ctl00_ContentBody_ShortDescription").innerHTML; var desc2 = document.getElementById("ctl00_ContentBody_LongDescription").innerHTML; alert( desc1 + desc2 ) }); } function createShowSolveREReverseButton() { let mybutton = document.createElement("solvebutton"); mybutton.innerHTML = "Solve RE"; let noteref = document.getElementsByClassName("minorCacheDetails Clear")[0]; noteref.appendChild(mybutton); mybutton.addEventListener("click", function() { solveReverseRE(); }); /* mybutton.addEventListener("click", function() { $( function() { $( "#dialog" ).dialog({width: "auto"}) .next(".ui-widget-overlay") .css({ opacity: 1.0, filter: "Alpha(Opacity=100)", backgroundColor: "black" }); } ); solveReverseRE(); }); */ } function createDialog( ) { let mydialog = document.createElement("div"); mydialog.id = "dialog"; mydialog.title = "Results"; let resultmsg = document.createElement("p"); resultmsg.id = "resultmsg"; mydialog.appendChild(resultmsg); // let noteref = document.getElementsByClassName("minorCacheDetails Clear")[0]; let noteref = document.getElementById("ctl00_ContentBody_detailWidget"); noteref.appendChild(mydialog); } // TODO: definitely not the best practice, but works (for now) function pollDOM() { const newCrd = document.getElementById('newCoordinates'); if (newCrd != null) { // Do something with el newCrd.value = newCoordinates; let submitBtn = document.getElementsByClassName("btn-cc-parse")[0]; //ssubmitBtn.click(); } else { setTimeout(pollDOM, 300); // try again in 300 milliseconds } } function updateCoordinates( newCoords ) { let crd = document.getElementById("uxLatLonLink"); crd.click(); newCoordinates = newCoords; pollDOM(); console.debug("XX: " + newCoords); } // retrieve bogus coordinates in ddm // returns array [ lat side (N/S), lat deg, lat mindec, lon side (E/W), lon deg, lon mindec ]; function getBogusCoordsFromPage() { let coords = document.getElementById("uxLatLon").textContent.split(" "); if( coords.length != 6 ) { console.error("Error parsing coordinates"); return; } return coords; } // convert latitude longitude array [ lat side (N/S), lat deg, lat mindec, lon side (E/W), lon deg, lon mindec ] to dd // return { lat: latitude dd , lon: longitude dd } function latLonDmArr2dd( latLonDmArray ) { // convert bogus coordinates from deg-mindec to deg-dec let bogusLat = dm2dd( latLonDmArray[0], latLonDmArray[1], latLonDmArray[2] ); let bogusLon = dm2dd( latLonDmArray[3], latLonDmArray[4], latLonDmArray[5] ); return { lat : bogusLat, lon : bogusLon } } // htmlsnip = html snippet in string // replace = boolean; true = replace existing html fully function addHTMLtoPage( htmlsnip, replace ) { let docEl = document.getElementsByClassName("Note Disclaimer")[0]; if( replace ) { docEl.innerHTML = htmlsnip; } else { docEl.innerHTML = docEl.innerHTML + htmlsnip; } } /* async */ function solveReverseRE() { console.debug("Attempting to parse codes from note."); //let resultmsg = document.getElementById("resultmsg"); let resultmsg = document.getElementsByClassName("Note Disclaimer")[0]; let codes = document.getElementById("viewCacheNote").textContent.split('\n'); for(let i=0; i<codes.length; i++) { codes[i] = codes[i].trim().split(" "); } if (codes.length != 3) { console.error("ERROR Parsing codes => Expected syntax: <code 1 from> <code 1 to> <code 1 regexp>\n <code 2 from> ..."); return; } console.debug("Parsed: " + JSON.stringify(codes)); let code1from = parseInt( codes[0][0] ); let code1to = parseInt( codes[0][1] ); let code2from = parseInt( codes[1][0] ); let code2to = parseInt( codes[1][1] ); let code3from = parseInt( codes[2][0] ); let code3to = parseInt( codes[2][1] ); let code1re = RegExp( codes[0][2] ); let code2re = RegExp( codes[1][2] ); let code3re = RegExp( codes[2][2] ); let bogusLatLonArr = getBogusCoordsFromPage(); // convert bogus coordinates from deg-min-dec to deg-dec let bogusDD = latLonDmArr2dd(bogusLatLonArr); let bogusLat = bogusDD.lat; let bogusLon = bogusDD.lon; console.debug("Parsed coords: " + JSON.stringify(bogusLatLonArr)); console.debug("Converted coords: " + bogusLat + " " + bogusLon); console.log("code1,code2,code3,lat(dd),lon(dd),lat(gps),lon(gps),dist(m)"); let counter = 0; console.debug("Iterating [" + code1from.toString().padStart(6, "0") + " -> " + code1to.toString().padStart(6, "0") + "],[" + code2from.toString().padStart(6, "0") + " -> " + code2to.toString().padStart(6, "0") + "],[" + code3from.toString().padStart(6, "0") + " -> " + code3to.toString().padStart(6, "0") + "]"); // [001234->001235],[321333->421333],[444111->444222] for( let i = code1from; i <= code1to; i++ ) { let code1 = i.toString().padStart(6, "0"); if( code1re.test( code1 ) == false ) { continue; } for( let j = code2from; j <= code2to; j++ ) { let code2 = j.toString().padStart(6, "0"); if( code2re.test( code2 ) == false ) { continue; } console.debug("Testing [" + i.toString().padStart(6, "0") + "],[" + j.toString().padStart(6, "0") + "],[" + code3from.toString().padStart(6, "0") + " -> " + code3to.toString().padStart(6, "0") + "]" ); for( let k = code3from; k <= code3to; k++ ) { let code3 = k.toString().padStart(6, "0"); if( code3re.test( code3 ) == false ) { continue; } let ddlatlon = /* await */ code2LatLon( code1, code2, code3 ); if( ddlatlon.lat == NaN || ddlatlon.lon == NaN ) continue; else { let dist = haversineDistance( bogusLat, bogusLon, ddlatlon.lat, ddlatlon.lon ); if( dist < 3220 ) { counter = counter+1; let dmlatlon = /* await */ dd2dm( ddlatlon.lat, ddlatlon.lon); let result = code1 + "," + code2 + "," + code3 + "," + fix(ddlatlon.lat, 2, 6) + "," + fix(ddlatlon.lon, 3, 6) + "," + dmlatlon.lat + "," + dmlatlon.lon + "," + dist.toFixed(0) ; console.log(result); resultmsg.innerText = resultmsg.innerText + "\n" + result; } } } } } console.log("Total " + counter + " matches found"); } function addBogusDDToPage() { let bogusLatLonArr = getBogusCoordsFromPage(); // convert bogus coordinates from deg-min-dec to deg-dec let bogusDD = latLonDmArr2dd(bogusLatLonArr); let bogusLat = bogusDD.lat; let bogusLon = bogusDD.lon; console.debug("Parsed coords: " + JSON.stringify(bogusLatLonArr)); console.debug("Converted coords: " + bogusLat + " " + bogusLon); // add bogus dd coordinates on page addHTMLtoPage( "<strong> Bogus: " + bogusLat + " " + bogusLon + " </strong>", true ); /*let locnode = document.getElementById("ctl00_ContentBody_LocationSubPanel"); locnode.textContent = bogusLat + " " + bogusLon; locnode.appendChild( document.createElement("br")); */ } // takes wigo codes as strings (may consist not numbers (variables) e.g. 022345 042x21 924ab9) // returns resulting coordinate (DD) string function evalPartialReverseWigo(codeA, codeB, codeC) { // validate that these are numbers console.debug("Input: " + codeA + ", " + codeB + ", " + codeC); if( isNaN( codeC[6-5] ) == false && isNaN( codeC[6-2] ) == false ) { // C5 + C2 = parillinen if( (Number(codeC[6-5]) + Number(codeC[6-2])) % 2 === 0 ) { console.debug("parillinen"); // A4 B2 B5 C3 A6 C2 A1 let resLat = "" + codeA[6-4] + codeB[6-2] + "." + codeB[6-5] + codeC[6-3] + codeA[6-6] + codeC[6-2] + codeA[6-1]; // A5 C6 C1 B3 B6 A2 C5 B1 let resLon = "" + codeA[6-5] + codeC[6-6] + codeC[6-1] + "." + codeB[6-3] + codeB[6-6] + codeA[6-2] + codeC[6-5] + codeB[6-1]; console.debug(resLat + " " + resLon); return resLat + " " + resLon; } // C (d5 + d2) eli C5+C2= pariton else if ( (Number(codeC[6-5]) + Number(codeC[6-2])) %2 !== 0 ) { console.debug("pariton"); // B6 A1 A4 C6 C3 C2 A6 let resLat = "" + codeB[6-6] + codeA[6-1] + "." + codeA[6-4] + codeC[6-6] + codeC[6-3] + codeC[6-2] + codeA[6-6]; // B2 C1 A2 A5 B3 B1 C5 B5 let resLon = "" + codeB[6-2] + codeC[6-1] + codeA[6-2] + "." + codeA[6-5] + codeB[6-3] + codeB[6-1] + codeC[6-5] + codeB[6-5]; console.debug(resLat + " " + resLon); return resLat + " " + resLon; } } else { console.debug("parillinen tai pariton"); console.warn("Cannot deduce which equation to use - showing both results"); let resLat1 = "" + codeA[6-4] + codeB[6-2] + "." + codeB[6-5] + codeC[6-3] + codeA[6-6] + codeC[6-2] + codeA[6-1]; // A5 C6 C1 B3 B6 A2 C5 B1 let resLon1 = "" + codeA[6-5] + codeC[6-6] + codeC[6-1] + "." + codeB[6-3] + codeB[6-6] + codeA[6-2] + codeC[6-5] + codeB[6-1]; let resLat2 = "" + codeB[6-6] + codeA[6-1] + "." + codeA[6-4] + codeC[6-6] + codeC[6-3] + codeC[6-2] + codeA[6-6]; // B2 C1 A2 A5 B3 B1 C5 B5 let resLon2 = "" + codeB[6-2] + codeC[6-1] + codeA[6-2] + "." + codeA[6-5] + codeB[6-3] + codeB[6-1] + codeC[6-5] + codeB[6-5]; console.debug( resLat1 + " " + resLon1 + "\n OR \n" + resLat2 + " " + resLon2 ); return resLat1 + " " + resLon1 + " OR " + resLat2 + " " + resLon2; } // A3 B4 C4 = ALWAYS ignore } (/* async */ function() { 'use strict'; let from, result; let cacheType = document.getElementsByClassName("cacheImage")[0].title; createShowSourceButton(); addBogusDDToPage(); if( cacheType == "Wherigo Cache" ) { createShowSolveREReverseButton(); createDialog(); // 1. parse from note let re1 = /RWIGO:/g; let re2 = /[0-9A-Za-z]{6}/g; console.log("Trying to find codes from user note:"); let text = document.getElementById("viewCacheNote").innerText; if( text.match( re1 ) ) { console.debug("Match: Reverse:"); let codes = text.match( re2 ); console.debug( JSON.stringify( codes ) ); if( codes !== null && (codes.length == 3 || codes.length == 6 || codes.length == 9) ) { console.debug("Match: " + codes[0] + "," + codes[1] + "," + codes[2] ); let reverseRes = evalPartialReverseWigo( codes[0], codes[1], codes[2] ); addHTMLtoPage("<br><strong>Reverse results = "+reverseRes+"</strong>", false); let ddlatlon = /* await */ code2LatLon( codes[0], codes[1], codes[2] ); let dmlatlon = /* await */ dd2dm( ddlatlon.lat, ddlatlon.lon); from = codes[0] + " " + codes[1] + " " + codes[2] + " => "; let resultDD = fix(ddlatlon.lat, 2, 6) + " " + fix(ddlatlon.lon, 3, 6); let resultDMD = dmlatlon.lat + " " + dmlatlon.lon; result = resultDMD; console.log( from + " " + resultDD + " = " + resultDMD ); addHTMLtoPage("<br><strong> Reversewigo final coordinates = " + result + " </strong>", false); updateCoordinates( resultDMD ); } else { console.warn("Error parsing coordinates from note"); } } else { console.log("Codes not found from note - trying to find from UserSuppliedContent"); let re = /[0-9]{6}/g; let codes = document.getElementsByClassName("UserSuppliedContent")[1].innerText.match( re ); if( codes !== null && (codes.length == 3 || codes.length == 6 || codes.length == 9) ) { console.debug("Parsed codes from UserSuppliedContent: " + JSON.stringify(codes) ); let reverseRes = evalPartialReverseWigo( codes[0], codes[1], codes[2] ); addHTMLtoPage("<br><strong>Reverse results = "+reverseRes+"</strong>", false); let ddlatlon = /* await */ code2LatLon( codes[0], codes[1], codes[2] ); let dmlatlon = /* await */ dd2dm( ddlatlon.lat, ddlatlon.lon); from = codes[0] + " " + codes[1] + " " + codes[2] + " => "; let resultDD = fix(ddlatlon.lat, 2, 6) + " " + fix(ddlatlon.lon, 3, 6); let resultDMD = dmlatlon.lat + " " + dmlatlon.lon; result = resultDMD; console.debug( from + " " + resultDD + " = " + resultDMD ); addHTMLtoPage("<br><strong> Reversewigo final coordinates = " + result + " </strong>", false); updateCoordinates( resultDMD ); } else { result = "Error: Three reverse wherigo codes not found"; console.log( result ); addHTMLtoPage("<br><strong> Reversewigo final coordinates = " + result + " </strong>", false); } } } })();