您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Provides access to certain OS OpenData products within the WME environment
// ==UserScript== // @name WME OpenData // @namespace http://greasemonkey.chizzum.com // @description Provides access to certain OS OpenData products within the WME environment // @include https://*.waze.com/*editor* // @exclude https://editor-beta.waze.com/* // @exclude https://beta.waze.com/* // @include https://one.network/* // @include https://public.londonworks.gov.uk/roadworks/* // @include http://public.londonworks.gov.uk/roadworks/* // @include https://search-property-information.service.gov.uk/search/search-by-map/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant unsafeWindow // @version 3.38 // ==/UserScript== // Contains Ordnance Survey data Crown copyright and database right 2024 // // Contents of the locatorData_*.js files are derived under the // Open Government Licence from the OS Open Names and Open Roads datasets // // Contents of the gazetteer.js file are derived under the // Open Government Licence from the OS Open Names dataset /* ======================================================================================================================= DONE FOR THIS RELEASE ======================================================================================================================= Improved tracking accuracy at higher zoom levels on One and OS sites, and reduced tracking lag on LRR site ======================================================================================================================= Bug fixes - MUST BE CLEARED BEFORE RELEASE ======================================================================================================================= ======================================================================================================================= Things to be checked ======================================================================================================================= */ /* JSHint Directives */ /* globals W: true */ /* globals OpenLayers: true */ /* globals Elgin: true */ /* globals gazetteerData: true */ /* globals oslRoadNameMatches: true */ /* globals map: true */ /* globals GM_setValue: true */ /* globals GM_getValue: true */ /* globals GM_addValueChangeListener: true */ /* globals unsafeWindow: true */ /* globals trustedTypes: */ /* jshint evil: true */ /* jshint bitwise: false */ /* jshint eqnull: true */ /* jshint esversion: 11 */ /* jshint undef: true */ /* jshint unused: true */ const oslVersion = '3.38'; const oslUpdateURL = 'https://greasyfork.org/scripts/1941-wme-to-os-link'; const oslBlockPath = 'https://greasemonkey.chizzum.com/osl_v3.0/'; const oslGazetteerURL = 'https://chizzum.com/greasemonkey/gaz_v5/_gazetteer.js'; const oslPi = 3.14159265358979; const oslPiDiv180 = (oslPi / 180); const osl180DivPi = (180 / oslPi); const oslLocatorBlockSize = 1000; const oslCacheDecayPeriod = 60; const oslNameTextPts = '12pt'; const oslNameHalfPix = 12; const GAZ_ELM = { Name: 0, CEast: 1, CNorth: 2, Type: 3, Area: 4, LEast: 5, LNorth: 6, REast: 7, RNorth: 8 }; const OSL_ELM = { RoadName: 0, RoadNumber: 1, BoundW: 2, BoundE: 3, BoundS: 4, BoundN: 5, Geometry: 6, AreaName: 7, AltName: 8, Classification: 9, Function: 10, Form: 11, Structure: 12, IsPrimary: 13, IsTrunk: 14, MAX: 15 }; const OSL_MODE = { Conversion: 0, OpenNames: 1, NameCheck: 2, OpenRoads: 3 }; const OSL_BBMODE = { Init: 0, Match: 1, Other: 2, Finalise: 3 }; const OSL_ROADRENDERER = { Init: 0, Render: 1, Finalise: 2, Erase: 3 }; const oslRoadClassifications = new Array ( 'Undefined', 'Motorway', 'A Road', 'B Road', 'Classified Unnumbered', 'Unclassified', 'Not Classified', 'Unknown' ); const oslRoadFunctions = new Array ( 'Undefined', 'Motorway', 'A Road', 'B Road', 'Minor Road', 'Local Road', 'Local Access Road', 'Restricted Local Access Road', 'Secondary Access Road' ); const oslStrokeColoursByFunction = new Array ( "red", "deepskyblue", "limegreen", "darkorange", "yellow", "white", "grey", "tan", "grey" ); const oslRoadStructures = new Array ( '', '[Tunnel]', '[Bridge]' ); const oslFormsOfWay = new Array ( 'Undefined', 'Single Carriageway', 'Dual Carriageway', 'Slip Road', 'Roundabout', 'Collapsed Dual Carriageway', 'Guided Busway', 'Shared Use Carriageway' ); // List of all road name suffixes, giving both their full length and abbreviated forms, // for use when automatically translating the standard OS names into the abbreviated // forms we prefer to use in WME. // // Note that some suffixes are included where the abbreviated form is identical to the // full length form - these are present to act as guidance to the translation code so // that it recognises which part of the OS name is the suffix - remember that the suffix // isn't always the last word in the name, as we also need to consider names that have // things after the suffix - e.g. Breakspear Road South, High Street Eastcote etc. If // these weren't present then we could end up incorrectly translating earlier parts of // the name which match one of the other suffixes - e.g. West Green Way... const oslNameAbbreviations = new Array ( 'Avenue','Ave', 'Boulevard','Blvd', 'Broadway','Bdwy', 'Circus','Cir', 'Close','Cl', 'Court','Ct', 'Crescent','Cr', 'Drive','Dr', 'Garden','Gdn', 'Gardens','Gdns', 'Green','Gn', 'Grove','Gr', 'Lane','Ln', 'Mews','Mews', 'Mount','Mt', 'Place','Pl', 'Park','Pk', 'Ridge','Rdg', 'Road','Rd', 'Square','Sq', 'Street','St', 'Terrace','Ter', 'Valley','Val', 'By-pass','Bypass', 'Way','Way', 'Hill','Hill' ); let oslUserPrefs = {}; let oslGazetteerData = []; let oslAdvancedMode = false; let oslEvalString = ''; let oslLoadingMsg = false; let oslMLCDiv = null; let oslOSLDiv = null; let oslBBDiv = null; let oslNamesDiv = null; let oslGazTagsDiv = null; let oslPrevHighlighted = null; let oslSegmentHighlighted = false; let oslPrevMouseX = null; let oslPrevMouseY = null; let oslDivDragging = false; let oslPrevSelected = null; let oslDoOSLUpdate = false; let oslMousepos = null; let oslMousePixelpos = null; let oslLastViewportWidth = null; let oslDoneOnload = false; let oslPrevStreetName = ''; let oslMergeGazData = false; let oslOSLMaskLayer = null; let oslOSLNameCheckTimer = 0; let oslOSLNCSegments = []; let oslInUK = false; let oslInLondon = false; let oslNorthings = null; let oslEastings = null; let oslLatitude = null; let oslLongitude = null; let oslHelmX = null; let oslHelmY = null; let oslHelmZ = null; let oslBlocksToLoad = []; let oslBlocksToTest = []; let oslVPLeft = 0; let oslVPRight = 0; let oslVPBottom = 0; let oslVPTop = 0; let oslBBDivInnerHTML = ''; let oslEvalEBlock = 0; let oslEvalNBlock = 0; let oslBlockData = null; let oslBlockCacheList = []; let oslBlockCacheTestTimer = (oslCacheDecayPeriod * 10); let oslSegGeoDivInnerHTML = ''; let oslNamesDivInnerHTML = ''; let oslONC_E = null; let oslONC_N = null; let oslEBlock_min = null; let oslEBlock_max = null; let oslNBlock_min = null; let oslNBlock_max = null; let oslOSLDivLeft; let oslOSLDivTop; let oslSegGeoDiv; let oslWazeMapElement; let oslDragBar; let oslWindow; let oslOSLDivTopMinimised; let oslNCDiv; let oslSegGeoUIDiv; let oslOffsetToolbar = false; let oslMOAdded = false; let oslLocatorElements = null; let oslUsingNewName = false; let oslUseName = false; let oslCityName = ''; let oslCountyName = ''; let oslUseAlt = false; let oslRORCenter = null; let oslRORZoom = null; function oslBootstrap() { if(document.location.host == 'one.network') { hlp_ONE.init(); } else if(document.location.host == 'search-property-information.service.gov.uk') { hlp_ODM.init(); } else if(document.location.host == 'public.londonworks.gov.uk') { hlp_LRR.init(); } else { oslInitialise(); } } function oslAddLog(logtext) { console.log('WMEOpenData: '+logtext); } function oslModifySrc(srcIn) { if(typeof trustedTypes === "undefined") { return srcIn; } else { const escapeSrcPolicy = trustedTypes.createPolicy("name", {createScriptURL: (to_escape) => to_escape}); return escapeSrcPolicy.createScriptURL(srcIn); } } function oslModifyHTML(htmlIn) { if(typeof trustedTypes === "undefined") { return htmlIn; } else { const escapeHTMLPolicy = trustedTypes.createPolicy("forceInner", {createHTML: (to_escape) => to_escape}); return escapeHTMLPolicy.createHTML(htmlIn); } } function oslGetExtent() { // From DaveAcincy let extent = new OpenLayers.Bounds(W.map.getExtent()); extent = extent.transform('EPSG:4326', 'EPSG:3857'); return extent; } //----------------------------------------------------------------------------------------------------------------------------------------- // all code between here and the next ------------- marker line is a stripped down version of the original from Paul Dixon // // * GeoTools javascript coordinate transformations // * http://www.nearby.org.uk/tests/geotools2.js // * // * This file copyright (c)2005 Paul Dixon ([email protected]) // * // * This program is free software; you can redistribute it and/or // * modify it under the terms of the GNU General Public License // * as published by the Free Software Foundation; either version 2 // * of the License, or (at your option) any later version. // * // * This program is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * GNU General Public License for more details. // * // * You should have received a copy of the GNU General Public License // * along with this program; if not, write to the Free Software // * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // * // * --------------------------------------------------------------------------- // * // * Credits // * // * The algorithm used by the script for WGS84-OSGB36 conversions is derived // * from an OSGB spreadsheet (www.gps.gov.uk) with permission. This has been // * adapted into Perl by Ian Harris, and into PHP by Barry Hunter. Conversion // * accuracy is in the order of 7m for 90% of Great Britain, and should be // * be similar to the conversion made by a typical GPSr // * // * See accompanying documentation for more information // * http://www.nearby.org.uk/tests/GeoTools2.html function oslOSGBtoWGS(oseast, osnorth) { const a = 6377563.396; const b = 6356256.910; const e0 = 400000; const n0 = -100000; const f0 = 0.999601272; const PHI0 = 49.00000; const LAM0 = -2.00000; const RadPHI0 = PHI0 * oslPiDiv180; const RadLAM0 = LAM0 * oslPiDiv180; //Compute af0, bf0, e squared (e2), n and Et const af0 = a * f0; const bf0 = b * f0; const e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2); const n = (af0 - bf0) / (af0 + bf0); let Et = oseast - e0; //Compute initial value for oslLatitude (PHId) in radians let PHI1 = ((osnorth - n0) / af0) + RadPHI0; let M = oslMarc(bf0, n, RadPHI0, PHI1); let PHId = ((osnorth - n0 - M) / af0) + PHI1; while (Math.abs(osnorth - n0 - M) > 0.00001) { PHId = ((osnorth - n0 - M) / af0) + PHI1; M = oslMarc(bf0, n, RadPHI0, PHId); PHI1 = PHId; } //Compute nu, rho and eta2 using value for PHId let nu = af0 / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(PHId),2))))); let rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(PHId),2))); let eta2 = (nu / rho) - 1; //Compute Latitude/Longitude let tanPHId = Math.tan(PHId); let tanPHIdSquared = Math.pow(tanPHId, 2); let tanPHIdPowFour = Math.pow(tanPHId, 4); let cosPHId = Math.cos(PHId); let cosPHIdPowNegOne = Math.pow(cosPHId, -1); let nuPowThree = Math.pow(nu, 3); let nuPowFive = Math.pow(nu, 5); let VII = tanPHId / (2 * rho * nu); let VIII = (tanPHId / (24 * rho * nuPowThree)) * (5 + (3 * tanPHIdSquared) + eta2 - (9 * eta2 * tanPHIdSquared)); let IX = (tanPHId / (720 * rho * nuPowFive)) * (61 + (90 * tanPHIdSquared) + (45 * tanPHIdPowFour)); oslLatitude = osl180DivPi * (PHId - (Math.pow(Et,2) * VII) + (Math.pow(Et,4) * VIII) - (Math.pow(Et, 6) * IX)); let X = cosPHIdPowNegOne / nu; let XI = (cosPHIdPowNegOne / (6 * nuPowThree)) * ((nu / rho) + (2 * tanPHIdSquared)); let XII = (cosPHIdPowNegOne / (120 * nuPowFive)) * (5 + (28 * tanPHIdSquared) + (24 * tanPHIdPowFour)); let XIIA = (cosPHIdPowNegOne / (5040 * Math.pow(nu,7))) * (61 + (662 * tanPHIdSquared) + (1320 * tanPHIdPowFour) + (720 * (Math.pow(tanPHId,6)))); oslLongitude = osl180DivPi * (RadLAM0 + (Et * X) - (Math.pow(Et,3) * XI) + (Math.pow(Et,5) * XII) - (Math.pow(Et,7) * XIIA)); let RadPHI = oslLatitude * oslPiDiv180; let RadLAM = oslLongitude * oslPiDiv180; const ee2 = (Math.pow(6377563.396,2) - Math.pow(6356256.910,2)) / Math.pow(6377563.396,2); let V = a / (Math.sqrt(1 - (ee2 * ( Math.pow(Math.sin(RadPHI),2))))); let cosRadPHI = Math.cos(RadPHI); X = V * cosRadPHI * Math.cos(RadLAM); let Y = V * cosRadPHI * Math.sin(RadLAM); let Z = (V * (1 - ee2)) * (Math.sin(RadPHI)); // do Helmert transforms const sfactor = -20.4894 * 0.000001; const RadX_Rot = (0.1502 / 3600) * oslPiDiv180; const RadY_Rot = (0.2470 / 3600) * oslPiDiv180; const RadZ_Rot = (0.8421 / 3600) * oslPiDiv180; let X2 = (X + (X * sfactor) - (Y * RadZ_Rot) + (Z * RadY_Rot) + 446.448); let Y2 = (X * RadZ_Rot) + Y + (Y * sfactor) - (Z * RadX_Rot) -125.157; let Z2 = (-1 * X * RadY_Rot) + (Y * RadX_Rot) + Z + (Z * sfactor) + 542.060; let RootXYSqr = Math.sqrt(Math.pow(X2,2) + Math.pow(Y2,2)); const eee2 = (Math.pow(6378137.000,2) - Math.pow(6356752.313,2)) / Math.pow(6378137.000,2); PHI1 = Math.atan2(Z2 , (RootXYSqr * (1 - eee2)) ); let sinPHI1 = Math.sin(PHI1); let sinPHI1Squared = Math.pow(sinPHI1, 2); V = 6378137.000 / (Math.sqrt(1 - (eee2 * sinPHI1Squared))); let PHI2 = Math.atan2((Z + (eee2 * V * sinPHI1)) , RootXYSqr); while (Math.abs(PHI1 - PHI2) > 0.000000001) { PHI1 = PHI2; let innerSinPHI1 = Math.sin(PHI1); V = 6378137.000 / (Math.sqrt(1 - (eee2 * Math.pow(innerSinPHI1,2)))); PHI2 = Math.atan2((Z2 + (eee2 * V * innerSinPHI1)) , RootXYSqr); } oslLatitude = PHI2 * osl180DivPi; oslLongitude = Math.atan2(Y2 , X2) * osl180DivPi; } function oslWGStoOSGB() { oslLatLontoHelmXYZ(); let oslLatitude2 = oslXYZtoLat(); let oslLongitude2 = Math.atan2(oslHelmY , oslHelmX) * osl180DivPi; oslLatLonoslToOSGrid(oslLatitude2,oslLongitude2); } function oslLatLontoHelmXYZ() { const a = 6378137.0; const b = 6356752.313; const DX = -446.448; const DY = 125.157; const DZ = -542.060; const rotX = -0.1502; const rotY = -0.2470; const rotZ = -0.8421; const sfactor = 20.4894 * 0.000001; const e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2); // perform initial lat-lon to cartesian coordinate translation let RadPHI = oslLatitude * oslPiDiv180; let RadLAM = oslLongitude * oslPiDiv180; let V = a / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(RadPHI),2))))); let cartX = V * (Math.cos(RadPHI)) * (Math.cos(RadLAM)); let cartY = V * (Math.cos(RadPHI)) * (Math.sin(RadLAM)); let cartZ = (V * (1 - e2)) * (Math.sin(RadPHI)); // Compute Helmert transformed coordinates let RadX_Rot = (rotX / 3600) * oslPiDiv180; let RadY_Rot = (rotY / 3600) * oslPiDiv180; let RadZ_Rot = (rotZ / 3600) * oslPiDiv180; oslHelmX = (cartX + (cartX * sfactor) - (cartY * RadZ_Rot) + (cartZ * RadY_Rot) + DX); oslHelmY = (cartX * RadZ_Rot) + cartY + (cartY * sfactor) - (cartZ * RadX_Rot) + DY; oslHelmZ = (-1 * cartX * RadY_Rot) + (cartY * RadX_Rot) + cartZ + (cartZ * sfactor) + DZ; } function oslXYZtoLat() { const a = 6377563.396; const b = 6356256.910; const e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2); let RootXYSqr = Math.sqrt(Math.pow(oslHelmX,2) + Math.pow(oslHelmY,2)); let PHI1 = Math.atan2(oslHelmZ , (RootXYSqr * (1 - e2)) ); let PHI = oslIterateOSLXYZtoLat(a, e2, PHI1, oslHelmZ, RootXYSqr); return PHI * osl180DivPi; } function oslIterateOSLXYZtoLat(a, e2, PHI1, Z, RootXYSqr) { let V = a / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(PHI1),2)))); let PHI2 = Math.atan2((Z + (e2 * V * (Math.sin(PHI1)))) , RootXYSqr); while (Math.abs(PHI1 - PHI2) > 0.000000001) { PHI1 = PHI2; let sinPHI1 = Math.sin(PHI1); V = a / (Math.sqrt(1 - (e2 * Math.pow(sinPHI1,2)))); PHI2 = Math.atan2((Z + (e2 * V * (sinPHI1))) , RootXYSqr); } return PHI2; } function oslMarc(bf0, n, PHI0, PHI) { let c1 = PHI - PHI0; let c2 = PHI + PHI0; let c3 = Math.pow(n, 2); let c4 = Math.pow(n, 3); return bf0 * ( ( (1 + n + ((5 / 4) * c3) + ((5 / 4) * c4)) * c1 ) - ( ((3 * n) + (3 * c3) + ((21 / 8) * c4)) * Math.sin(c1) * Math.cos(c2) ) + ( (((15 / 8) * c3) + ((15 / 8) * c4)) * Math.sin(2 * c1) * Math.cos(2 * c2) ) - ( ((35 / 24) * c4) * (Math.sin(3 * c1)) * (Math.cos(3 * c2)) ) ); } function oslLatLonoslToOSGrid(PHI, LAM) { const a = 6377563.396; const b = 6356256.910; const e0 = 400000; const n0 = -100000; const f0 = 0.999601272; const PHI0 = 49.00000; const LAM0 = -2.00000; const RadPHI0 = PHI0 * oslPiDiv180; const RadLAM0 = LAM0 * oslPiDiv180; const af0 = a * f0; const bf0 = b * f0; const n = (af0 - bf0) / (af0 + bf0); const e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2); let RadPHI = PHI * oslPiDiv180; let RadLAM = LAM * oslPiDiv180; let sinRadPHI = Math.sin(RadPHI); let sinRadPHISquared = Math.pow(sinRadPHI, 2); let cosRadPHI = Math.cos(RadPHI); let cosRadPHIPowThree = Math.pow(cosRadPHI, 3); let cosRadPHIPowFive = Math.pow(cosRadPHI, 5); let tanRadPHI = Math.tan(RadPHI); let tanRadPHISquared = Math.pow(tanRadPHI, 2); let tanRadPHIPowFour = Math.pow(tanRadPHI, 4); let nu = af0 / (Math.sqrt(1 - (e2 * sinRadPHISquared))); let rho = (nu * (1 - e2)) / (1 - (e2 * sinRadPHISquared)); let eta2 = (nu / rho) - 1; let p = RadLAM - RadLAM0; let M = oslMarc(bf0, n, RadPHI0, RadPHI); let I = M + n0; let II = (nu / 2) * sinRadPHI * cosRadPHI; let III = ((nu / 24) * sinRadPHI * cosRadPHIPowThree) * (5 - tanRadPHISquared + (9 * eta2)); let IIIA = ((nu / 720) * sinRadPHI * cosRadPHIPowFive) * (61 - (58 * tanRadPHISquared) + tanRadPHIPowFour); let IV = nu * cosRadPHI; let V = (nu / 6) * cosRadPHIPowThree * ((nu / rho) - tanRadPHISquared); let VI = (nu / 120) * cosRadPHIPowFive * ((5 - (18 * tanRadPHISquared)) + tanRadPHIPowFour + (14 * eta2) - (58 * tanRadPHISquared * eta2)); oslEastings = Math.round(e0 + (p * IV) + (Math.pow(p,3) * V) + (Math.pow(p,5) * VI)); oslNorthings = Math.round(I + (Math.pow(p,2) * II) + (Math.pow(p,4) * III) + (Math.pow(p,6) * IIIA)); // Conversion errors // 50.06574112187924 -5.699894626953322 // 135261,25033 // 135256,25014 // +6, +19 // 51.35363338966115 1.4443961072966522 // 639795,167306 // 639800,167284 // -5, +22 // 60.15795987870581 -1.1466271562283679 // 447367,1141743 // 447362,1141733 // +5, +10 let nCorrect = Math.round(70 - PHI); oslNorthings -= nCorrect; } //----------------------------------------------------------------------------------------------------------------------------------------- function oslCaseCorrect(wrongcase) { let correctedCase = ''; for(let loop=0;loop<wrongcase.length;loop++) { // capitalise first letter following one of these substrings if ( (loop === 0)|| (wrongcase[loop-1] == ' ')|| (wrongcase[loop-1] == '(')|| (wrongcase.substr(loop-3,3) == '-Y-')|| (wrongcase.substr(loop-4,4) == '-YR-') ) correctedCase += wrongcase[loop].toUpperCase(); else correctedCase += wrongcase[loop].toLowerCase(); } // recapitalise any roman numerals correctedCase = correctedCase.replace(' Ii ',' II '); correctedCase = correctedCase.replace(' Iii ',' III '); correctedCase = correctedCase.replace(' Iv ',' IV '); correctedCase = correctedCase.replace(' Vi ',' VI '); correctedCase = correctedCase.replace(' Vii ',' VII '); return correctedCase; } function oslSaintsPreserveUs(oslName) { let nameBits = []; if(oslName.indexOf('St ') != -1) { nameBits = oslName.split('St '); oslName = nameBits[0] + 'St. ' + nameBits[1]; } else if(oslName.indexOf('Saint ') != -1) { nameBits = oslName.split('Saint '); oslName = nameBits[0] + 'St. ' + nameBits[1]; } return oslName; } function oslWazeifyStreetName(oslName, debugOutput) { let wazeName = ''; // strip out any HTML encoding added by the server when returning the street name data... let textArea = document.createElement('textarea'); textArea.innerHTML = oslModifyHTML(oslName); oslName = textArea.value; wazeName = oslCaseCorrect(oslName); wazeName = oslSaintsPreserveUs(wazeName); let nameoslPieces = wazeName.split(' '); if(nameoslPieces.length > 1) { let dirSuffix = ''; let namePrefix = ''; if((nameoslPieces[nameoslPieces.length-1] == 'North')||(nameoslPieces[nameoslPieces.length-1] == 'South')||(nameoslPieces[nameoslPieces.length-1] == 'East')||(nameoslPieces[nameoslPieces.length-1] == 'West')) { dirSuffix = ' ' + nameoslPieces[nameoslPieces.length-1][0]; for(let loop=0;loop<nameoslPieces.length-1;loop++) { namePrefix += (nameoslPieces[loop] + ' '); } } else { for(let loop=0;loop<nameoslPieces.length;loop++) { namePrefix += (nameoslPieces[loop] + ' '); } } namePrefix = namePrefix.trimRight(1); if(debugOutput === true) console.log(oslName); // replace road type with abbreviated form for(let pass=0;pass<2;pass++) { for(let loop=0;loop<oslNameAbbreviations.length;loop+=2) { let abbrPos = namePrefix.lastIndexOf(oslNameAbbreviations[loop]); let abbrLen = oslNameAbbreviations[loop].length; let npLength = namePrefix.length; let npRemaining = npLength - abbrPos; if(debugOutput === true) console.log(pass,' ',oslNameAbbreviations[loop],' ',abbrPos,' ',abbrLen,' ',npLength,' ',npRemaining); if(abbrPos != -1) { // make sure the road type we've found comes firstly at the end of the name string, or is suffixed with a space // if there's a non-road type at the end of the string (e.g. High Road Eastcote) // isn't, then we've actually found a type match within a longer string segment (e.g. The Parkside) and so we // should leave it alone... if ( ((pass === 0) && (npRemaining == abbrLen)) || ((pass == 1) && (namePrefix[abbrPos+abbrLen] == ' ')) ) { let preName = namePrefix.substr(0,abbrPos); if((preName.length >= 4) && (preName.lastIndexOf("The") != (preName.length - 4))) { let theName = namePrefix.substr(abbrPos); theName = theName.replace(oslNameAbbreviations[loop],oslNameAbbreviations[loop+1]); wazeName = preName + theName + dirSuffix; return wazeName; } } } } } wazeName = namePrefix + dirSuffix; } return wazeName; } function oslCPDistance(cpE, cpN, posE, posN) { return Math.round(Math.sqrt(((posE - cpE) * (posE - cpE)) + ((posN - cpN) * (posN - cpN)))); } function oslGetBBCornerPixels(boxW, boxE, boxS, boxN) { let lonlat_sw = new OpenLayers.LonLat(boxW,boxS); let lonlat_se = new OpenLayers.LonLat(boxE,boxS); let lonlat_nw = new OpenLayers.LonLat(boxW,boxN); let lonlat_ne = new OpenLayers.LonLat(boxE,boxN); let pix_sw = W.map.getPixelFromLonLat(lonlat_sw); let pix_se = W.map.getPixelFromLonLat(lonlat_se); let pix_ne = W.map.getPixelFromLonLat(lonlat_ne); let pix_nw = W.map.getPixelFromLonLat(lonlat_nw); boxE = (pix_ne.x + pix_se.x) / 2; boxW = (pix_nw.x + pix_sw.x) / 2; boxN = (pix_ne.y + pix_nw.y) / 2; boxS = (pix_se.y + pix_sw.y) / 2; let boxToleranceWidth = ((boxE - boxW) * 0.05); let boxToleranceHeight = ((boxS - boxN) * 0.05); boxW -= boxToleranceWidth; boxE += boxToleranceWidth; boxS += boxToleranceHeight; boxN -= boxToleranceHeight; boxE = Math.round(boxE); boxW = Math.round(boxW); boxS = Math.round(boxS); boxN = Math.round(boxN); // extend width/height of box if the calculated dimension is too small for the box to be readily visible if(boxE-boxW < 20) { boxE += 10; boxW -= 10; } if(boxS-boxN < 20) { boxS += 10; boxN -= 10; } return [boxW, boxE, boxS, boxN]; } function oslVisualiseBoundingBox(boxW, boxE, boxS, boxN, mode) { if(oslOSLDiv.style.height == '0px') { oslBBDiv.innerHTML = ''; return; } let boxPos = [boxW, boxE, boxS, boxN]; if((mode == OSL_BBMODE.Match) || (mode == OSL_BBMODE.Other)) { boxPos = oslGetBBCornerPixels(boxW, boxE, boxS, boxN); } if(mode === OSL_BBMODE.Init) { oslBBDivInnerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="'+document.getElementById('WazeMap').offsetWidth+'px" height="'+document.getElementById('WazeMap').offsetHeight+'px" version="1.1">'; } else if(mode == OSL_BBMODE.Match) { oslBBDivInnerHTML += '<rect x="'+boxPos[0]+'" y="'+boxPos[3]+'" width="'+(boxPos[1]-boxPos[0])+'" height="'+(boxPos[2]-boxPos[3])+'" style="fill:yellow;stroke:pink;stroke-width:4;fill-opacity:0.25;stroke-opacity:0.25"/>'; } else if(mode == OSL_BBMODE.Other) { oslBBDivInnerHTML += '<rect x="'+boxPos[0]+'" y="'+boxPos[3]+'" width="'+(boxPos[1]-boxPos[0])+'" height="'+(boxPos[2]-boxPos[3])+'" style="fill:lightgrey;stroke:grey;stroke-width:4;fill-opacity:0.25;stroke-opacity:0.25"/>'; } else if(mode == OSL_BBMODE.Finalise) { oslBBDivInnerHTML += '</svg>'; oslBBDiv.innerHTML = oslBBDivInnerHTML; } } function oslMergeGazetteerData() { if(typeof(gazetteerData) == "undefined") return false; if(oslMergeGazData) { // We no longer need to inject the gazetteer data as two seperate arrays and then merge them here, // but we do still need to create a local reference to the injected array data, as trying to access // it directly as gazetteerData[] throws errors... oslGazetteerData = gazetteerData; oslMergeGazData = false; for(let idx=0;idx<oslGazetteerData.length;idx++) { oslGazetteerData[idx] = oslSaintsPreserveUs(oslGazetteerData[idx]); } oslAddLog('gazetteer data loaded, '+oslGazetteerData.length+' entries'); } return true; } function oslGetTextWidth(text) { // from https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 // re-use canvas object for better performance let canvas = oslGetTextWidth.canvas || (oslGetTextWidth.canvas = document.createElement("canvas")); let context = canvas.getContext("2d"); context.font = 'bold '+oslNameTextPts+' arial'; let metrics = context.measureText(text); return metrics.width; } function oslGetVisibleCityNames() { if(oslMergeGazetteerData() === false) return; oslNamesDivInnerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="'+document.getElementById('WazeMap').offsetWidth+'px" height="'+document.getElementById('WazeMap').offsetHeight+'px" version="1.1">'; if(document.getElementById('_cbGazTagsEnabled').checked === true) { let showCities = document.getElementById('_cbGazTagsCity').checked; let showTowns = document.getElementById('_cbGazTagsTown').checked; let showVillages = document.getElementById('_cbGazTagsVillage').checked; let showHamlets = document.getElementById('_cbGazTagsHamlet').checked; let showOthers = document.getElementById('_cbGazTagsOther').checked; for(let idx=0;idx<oslGazetteerData.length;idx++) { let gazElements = oslGazetteerData[idx].split(':'); let cnType = gazElements[GAZ_ELM.Type]; let showTag; let tagColour; if(cnType == 'C') { showTag = showCities; tagColour = "#FFFF00"; } else if(cnType == 'T') { showTag = showTowns; tagColour = "#FF00FF"; } else if(cnType == 'V') { showTag = showVillages; tagColour = "#FF8080"; } else if(cnType == 'H') { showTag = showHamlets; tagColour = "#A0A0FF"; } else { showTag = showOthers; tagColour = "#C0C0C0"; } if(showTag === true) { let cnEastings = gazElements[GAZ_ELM.CEast]; let cnNorthings = gazElements[GAZ_ELM.CNorth]; if ( ((oslVPLeft < cnEastings) && (oslVPRight > cnEastings)) && ((oslVPTop > cnNorthings) && (oslVPBottom < cnNorthings)) ) { let pix = oslOSGridRefToPixel(cnEastings,cnNorthings); let nameLength = oslGetTextWidth(gazElements[GAZ_ELM.Name]); oslNamesDivInnerHTML += '<polygon points="'; oslNamesDivInnerHTML += pix.x+','+pix.y+' '; oslNamesDivInnerHTML += (pix.x+oslNameHalfPix)+','+(pix.y-oslNameHalfPix)+' '; oslNamesDivInnerHTML += (pix.x+nameLength+oslNameHalfPix+8)+','+(pix.y-oslNameHalfPix)+' '; oslNamesDivInnerHTML += (pix.x+nameLength+oslNameHalfPix+8)+','+(pix.y+oslNameHalfPix)+' '; oslNamesDivInnerHTML += (pix.x+oslNameHalfPix)+','+(pix.y+oslNameHalfPix)+'" '; oslNamesDivInnerHTML += 'fill="'+tagColour+'" fill-opacity="0.75" stroke="black" stroke-opacity="0.75" />'; oslNamesDivInnerHTML += '<text x="'+(pix.x+oslNameHalfPix)+'" y="'+(pix.y+(oslNameHalfPix/2))+'" '; oslNamesDivInnerHTML += 'style="font-family:sans-serif;font-size:'+oslNameTextPts+';font-weight:600;fill:black;'; oslNamesDivInnerHTML += 'fill-opacity:1">'+gazElements[GAZ_ELM.Name]+'</text>'; } } } } oslNamesDivInnerHTML += '</svg>'; oslNamesDiv.innerHTML = oslNamesDivInnerHTML; } function oslGetNearbyCityNames() { if(oslMergeGazetteerData() === false) return; let names = []; for(var idx=0;idx<oslGazetteerData.length;idx++) { let gazElements = oslGazetteerData[idx].split(':'); let cnEastings = gazElements[GAZ_ELM.CEast]; let cnNorthings = gazElements[GAZ_ELM.CNorth]; if((Math.abs(cnNorthings-oslNorthings) <= 5000)&&(Math.abs(cnEastings-oslEastings) <= 5000)) { let dist = oslCPDistance(cnEastings,cnNorthings,oslEastings,oslNorthings); if(dist <= 5000) { names.push((dist * 1000000) + idx); } } } if(names.length > 1) names.sort(function(a,b){return a-b;}); let cityInTopTen = false; let matchedOSName = false; let matchedIdx = -1; let listLength = names.length; if(listLength > 10) listLength = 10; let listOpt; let gElements; let gDist; let cityName; let oOCN = document.getElementById('oslOSCityNames'); for(idx=0;idx<listLength;idx++) { gElements = oslGazetteerData[names[idx] % 1000000].split(':'); gDist = (Math.round(names[idx] / 100000000)/10); // Build namestring for entry in the drop-down list - start with the placename :-) listOpt = document.createElement('option'); cityName = gElements[GAZ_ELM.Name]; listOpt.text = cityName; // if the name is neither a city nor unique, append a (county) suffix if(gElements[GAZ_ELM.Type] == 'C') cityInTopTen = true; else { if(oslCheckCityNameDuplicates(gElements[GAZ_ELM.Name],1) > 1) { listOpt.text += ', '+gElements[GAZ_ELM.Area]; } } if(sessionStorage.cityNameRB == 'optUseOS') { if(cityName == sessionStorage.myCity) { matchedOSName = true; matchedIdx = idx; } } // Add place type and distance in [] brackets to allow easy removal later... if(gElements[GAZ_ELM.Type] == 'C') listOpt.text += ' [City, '; else if(gElements[GAZ_ELM.Type] == 'T') listOpt.text += ' [Town, '; else if(gElements[GAZ_ELM.Type] == 'V') listOpt.text += ' [Village, '; else if(gElements[GAZ_ELM.Type] == 'H') listOpt.text += ' [Hamlet, '; else listOpt.text += ' [Other, '; listOpt.text += gDist + 'km]'; oOCN.add(listOpt,null); } if((!cityInTopTen) && (names.length > 10)) { idx = 10; while((idx < names.length) && (!cityInTopTen)) { gElements = oslGazetteerData[names[idx] % 1000000].split(':'); if(gElements[GAZ_ELM.Type] == 'C') { cityInTopTen = true; gDist = ' [City, '+(Math.round(names[idx] / 100000000)/10)+'km]'; listOpt = document.createElement('option'); listOpt.text = gElements[GAZ_ELM.Name]+gDist; oOCN.add(listOpt,null); if(sessionStorage.cityNameRB == 'optUseOS') { if(gElements[GAZ_ELM.Name] == sessionStorage.myCity) { matchedOSName = true; matchedIdx = 10; break; } } } idx++; } } if(matchedOSName === true) oOCN.options.selectedIndex = matchedIdx; if((sessionStorage.cityNameRB == 'optUseOS') && (matchedOSName === false)) { oslAddLog('Selected city name no longer in nearby OS list...'); alert('City name no longer present in nearby OS data, please reselect'); sessionStorage.cityNameRB = 'optUseExisting'; document.getElementById('optUseExisting').checked = true; } } function oslCheckCityNameDuplicates(cityName, mode) { if(oslMergeGazetteerData() === false) return; let cnCount = 0; let searchDist = Math.round(oslGazetteerData.length/2); let searchIdx = searchDist; let hasCounty = false; let debugOutput = false; // remove county suffix from actual city name string if present if(cityName.indexOf('(') != -1) { cityName = cityName.substr(0,cityName.indexOf('(')); cityName = cityName.replace(/^\s+|\s+$/g, ""); hasCounty = true; } // remove script-appended county suffix from city name held in drop down if present if(cityName.indexOf(',') != -1) { cityName = cityName.substr(0,cityName.indexOf(',')); cityName = cityName.replace(/^\s+|\s+$/g, ""); } cityName = cityName.toLowerCase(); cityName = cityName.replace(/-/g, ' '); let gazName = ''; if(debugOutput === true) console.log('scan for duplicates of '+cityName); var gazElements = []; while((searchDist > 1) && (cityName.localeCompare(gazName) !== 0)) { searchDist = Math.round(searchDist/2); gazElements = oslGazetteerData[searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); if(debugOutput === true) console.log('a: '+searchDist+' '+searchIdx+' '+gazName); if(cityName.localeCompare(gazName) > 0) searchIdx += searchDist; else if(cityName.localeCompare(gazName) < 0) searchIdx -= searchDist; if(searchIdx >= oslGazetteerData.length) searchIdx = oslGazetteerData.length-1; if(searchIdx < 0) searchIdx = 0; } gazElements = oslGazetteerData[searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); while((searchIdx > 0) && (cityName.localeCompare(gazName) == 0)) { gazElements = oslGazetteerData[--searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); if(debugOutput === true) console.log('b: '+(searchIdx)+' '+gazName); } ++searchIdx; gazElements = oslGazetteerData[searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); while((searchIdx < (oslGazetteerData.length - 1)) && (cityName.localeCompare(gazName) > 0)) { ++searchIdx; try { gazElements = oslGazetteerData[searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); if(debugOutput === true) console.log('c: '+(searchIdx)+' '+gazName); } catch { oslAddLog("ERROR THROWN - "+oslGazetteerData[searchIdx]); } } while((cityName.localeCompare(gazName) === 0) && (searchIdx < oslGazetteerData.length)) { cnCount++; gazElements = oslGazetteerData[++searchIdx].split(':'); gazName = gazElements[GAZ_ELM.Name].toLowerCase(); gazName = gazName.replace(/-/g, ' '); if(debugOutput === true) console.log('d: '+(searchIdx)+' '+gazName+' '+cnCount); } if(mode === 0) { let newHTML = ''; if(cnCount === 0) newHTML = ' Place name is not in OS data'; else if(cnCount == 1) { newHTML = ' Place name is unique'; if(hasCounty) newHTML += '<br> <i>(County) suffix not required</i>'; } else { newHTML = ' Place name is not unique'; } document.getElementById('oslCNInfo').innerHTML = newHTML; } else return cnCount; } function oslHighlightAdjacentSameNameSegments(ldEastings, ldNorthings, ldIgnoreIdx, srcElements) { ldNorthings -= oslLocatorBlockSize; ldEastings -= oslLocatorBlockSize; for(let x = 0; x < 3; ++x) { for(let y = 0; y < 3; ++y) { let arrayName = 'locatorData_'+(ldEastings + (x * oslLocatorBlockSize))+'_'+(ldNorthings + (y * oslLocatorBlockSize)); oslEvalString = arrayName; if(typeof unsafeWindow[arrayName] != "undefined") { oslBlockData = unsafeWindow[arrayName]; for(let loop = 0; loop < oslBlockData.length; ++loop) { if(loop != ldIgnoreIdx) { var locatorElements = oslSplitEntry(oslBlockData[loop]); if ( (locatorElements[OSL_ELM.RoadName] == srcElements[OSL_ELM.RoadName]) && (locatorElements[OSL_ELM.RoadNumber] == srcElements[OSL_ELM.RoadNumber]) && (locatorElements[OSL_ELM.AreaName] == srcElements[OSL_ELM.AreaName]) ) { oslVisualiseBoundingBox(locatorElements[OSL_ELM.BoundW],locatorElements[OSL_ELM.BoundE],locatorElements[OSL_ELM.BoundS],locatorElements[OSL_ELM.BoundN],OSL_BBMODE.Other); } } } } } } } function oslRadioClick() { let oslElements = document.getElementById('oslRoadNameMatches'); let selectedName = ''; let arrayName; for(let loop=0;loop<oslElements.childNodes.length;loop++) { if(oslElements.childNodes[loop].nodeType == 1) { let tagname = oslElements.childNodes[loop].tagName; if(tagname !== null) { if(tagname == "LABEL") { if(oslElements.childNodes[loop].childNodes[0].checked) { let attr = oslElements.childNodes[loop].childNodes[0].attributes.getNamedItem("id").value; if((attr.indexOf('oslID_') === 0) || (attr.indexOf('alt-oslID_') === 0)) { let roadData = ''; let oslID = attr.split('_'); if(oslID[1] != 'null') { arrayName = 'locatorData_'+oslID[1]+'_'+oslID[2]; if(typeof unsafeWindow[arrayName] != "undefined") { roadData = unsafeWindow[arrayName][oslID[3]]; } } if(roadData == '') { roadData = "null:null"; } let locatorElements = oslSplitEntry(roadData); if(locatorElements[OSL_ELM.RoadName] != 'null') { selectedName = locatorElements[OSL_ELM.RoadName]+locatorElements[OSL_ELM.RoadNumber]; oslVisualiseBoundingBox(0,0,0,0,OSL_BBMODE.Init); oslVisualiseBoundingBox(locatorElements[OSL_ELM.BoundW],locatorElements[OSL_ELM.BoundE],locatorElements[OSL_ELM.BoundS],locatorElements[OSL_ELM.BoundN],OSL_BBMODE.Match); oslHighlightAdjacentSameNameSegments(oslID[1], oslID[2], oslID[3], locatorElements); } else { oslBBDiv.innerHTML = ''; } } } } } } } if(selectedName === '') { oslBBDiv.innerHTML = ''; return; } oslVisualiseBoundingBox(0,0,0,0,OSL_BBMODE.Finalise); } function oslClick() { oslCityName = ''; oslCountyName = ''; oslUsingNewName = false; if(document.getElementById('optUseNewManual').checked) { oslCityName = oslCaseCorrect(document.getElementById('myCityName').value); oslUsingNewName = true; } else if(document.getElementById('optUseExistingWME').checked) { let oWCN = document.getElementById('oslWMECityNames'); oslCityName = oWCN.options[oWCN.options.selectedIndex].text; if(oslCityName.indexOf(', ') !== -1) { oslCountyName = oslCityName.split(', ')[1]; oslCityName = oslCityName.split(', ')[0]; } oslUsingNewName = true; } else if(document.getElementById('optUseOS').checked) { let oOCN = document.getElementById('oslOSCityNames'); oslCityName = oOCN.options[oOCN.options.selectedIndex].text; oslCityName = oslCityName.substring(0,oslCityName.indexOf('[')-1); if(oslCityName.indexOf(', ') !== -1) { oslCountyName = oslCityName.split(', ')[1]; oslCityName = oslCityName.split(', ')[0]; } oslUsingNewName = true; } if(sessionStorage.myCity === '') { oslAddLog('Update city name position at '+oslEastings+'x'+oslNorthings); sessionStorage.cityChangeEastings = oslEastings; sessionStorage.cityChangeNorthings = oslNorthings; } if(oslCountyName !== '') { sessionStorage.myCity = oslCityName+', '+oslCountyName; } else { sessionStorage.myCity = oslCityName; } oslUseName = false; if((oslCityName.length > 0) && oslUsingNewName) { oslCheckCityNameDuplicates(oslCityName,0); if(oslCityName != sessionStorage.prevCityName) { oslAddLog('Change of city name at '+oslEastings+'x'+oslNorthings); sessionStorage.cityChangeEastings = oslEastings; sessionStorage.cityChangeNorthings = oslNorthings; sessionStorage.prevCityName = oslCityName; oslUseName = true; } else { let nameChangeDist = oslCPDistance(oslEastings,oslNorthings,sessionStorage.cityChangeEastings,sessionStorage.cityChangeNorthings); oslAddLog('Current name was set '+nameChangeDist+'m away from segment location'); if(nameChangeDist > 1000) { oslAddLog('Distance exceeds 1km threshold, name verification required...'); if(confirm('Confirm continued use of this city name')) { oslAddLog('Confirm city name at '+oslEastings+'x'+oslNorthings); sessionStorage.cityChangeEastings = oslEastings; sessionStorage.cityChangeNorthings = oslNorthings; oslUseName = true; } } else { oslUseName = true; } } } let oslElements = document.getElementById('oslRoadNameMatches'); let arrayName; for(let loop=0;loop<oslElements.childNodes.length;loop++) { if(oslElements.childNodes[loop].nodeType == 1) { let tagname = oslElements.childNodes[loop].tagName; if(tagname !== null) { if(tagname == "LABEL") { let rbElement = oslElements.childNodes[loop].childNodes[0]; if((rbElement.name == "oslChoice") && (rbElement.checked)) { let attr = rbElement.attributes.getNamedItem("id").value; let useMain = (attr.indexOf('oslID_') === 0); oslUseAlt = (attr.indexOf('alt-oslID_') === 0); if((useMain === true) || (oslUseAlt === true)) { let roadData = ''; let oslID = attr.split('_'); if(oslID[1] != 'null') { arrayName = 'locatorData_'+oslID[1]+'_'+oslID[2]; if(typeof unsafeWindow[arrayName] != "undefined") { roadData = unsafeWindow[arrayName][oslID[3]]; } } if(roadData == '') { oslAddLog(arrayName + "not found"); for(let i = 0; i < OSL_ELM.MAX; ++i) { roadData += ':'; } } oslLocatorElements = oslSplitEntry(roadData); oslClearAltNames(); oslApplyPrimaryStreetName(); oslApplyAltStreetName(); } } } } } } } function oslApplyAltStreetName() { if(oslUseAlt === true) { let tName = oslLocatorElements[OSL_ELM.RoadName]; oslLocatorElements[OSL_ELM.RoadName] = oslLocatorElements[OSL_ELM.AltName]; oslLocatorElements[OSL_ELM.AltName] = tName; } if(oslLocatorElements[OSL_ELM.AltName] === '') { // No alt name to apply, so exit... return; } // Open the altname edit UI if(document.querySelector('.alt-city-name') === null) { document.querySelector('.add-alt-street-btn').click(); window.setTimeout(oslApplyAltStreetName, 10); return; } let altName = oslLocatorElements[OSL_ELM.RoadNumber]; if((oslLocatorElements[OSL_ELM.RoadName].length > 0)&&(oslLocatorElements[OSL_ELM.RoadNumber].length > 0)) altName += ' - '; altName += oslWazeifyStreetName(oslLocatorElements[OSL_ELM.AltName], false); oslInitReactUpdates(); oslUpdateReactTextInput('.alt-street-name', altName); if(oslUsingNewName) { if(oslUseName) { oslUpdateReactTextInput('.alt-city-name', oslCityName); } } else if(document.getElementById('optClearExisting').checked === true) { oslUpdateReactTextInput('.alt-city-name', ''); } document.querySelector('.alt-streets-form').querySelector('.save-button').click(); } let oslPElm; let oslIElm; let oslReqValue; function oslInitReactUpdates() { oslPElm = []; oslIElm = []; oslReqValue = []; } function oslChangeReactTextInput() { let allDone = true; for(let i = 0; i < oslPElm.length; ++i) { if(oslIElm[i].value !== oslReqValue[i]) { allDone = false; oslPElm[i].dispatchEvent(new Event('focus')); oslPElm[i].value = ""; oslPElm[i].dispatchEvent(new Event('focus')); oslPElm[i].dispatchEvent(new Event('input')); oslIElm[i].dispatchEvent(new Event('focus')); oslIElm[i].value = oslReqValue[i]; oslIElm[i].dispatchEvent(new Event('focus')); oslIElm[i].dispatchEvent(new Event('input')); oslIElm[i].dispatchEvent(new Event('blur')); oslIElm[i].dispatchEvent(new Event('blur')); } } if(allDone === false) { window.setTimeout(oslChangeReactTextInput, 100); } } function oslUpdateReactTextInput(parent, value) { let pElm = document.querySelector(parent); if(pElm !== null) { let iElm = pElm.shadowRoot.querySelector('#text-input'); if(iElm !== null) { oslPElm.push(pElm); oslIElm.push(iElm); oslReqValue.push(value); oslChangeReactTextInput(); } } } function oslCheckForAddressEditUI() { let retval = false; let ui = document.querySelector('#segment-edit-general .address-form'); if(ui !== null) { retval = true; retval = retval && (ui.querySelector('.street-name-row') !== null); retval = retval && (ui.querySelector('.city-name') !== null); retval = retval && (ui.querySelector('.state-id') !== null); retval = retval && (ui.querySelector('.country-id') !== null); retval = retval && (ui.querySelector('.action-buttons') !== null); retval = retval && (ui.getBoundingClientRect().height != 0); } return retval; } function oslClearAltNames() { // Remove any alternate names on a segment let n = document.querySelectorAll('.alt-street-delete').length; while(n) { --n; document.querySelectorAll('.alt-street-delete')[n].click(); } } function oslApplyPrimaryStreetName() { if(document.querySelector('#segment-edit-general .full-address') !== null) { document.querySelector('#segment-edit-general .full-address').click(); } if(oslCheckForAddressEditUI() === false) { window.setTimeout(oslApplyPrimaryStreetName, 10); return; } // Update the Street field let oslName = oslLocatorElements[OSL_ELM.RoadNumber]; if((oslLocatorElements[OSL_ELM.RoadName].length > 0)&&(oslLocatorElements[OSL_ELM.RoadNumber].length > 0)) { oslName += ' - '; } if((oslName.length > 0)||(oslLocatorElements[OSL_ELM.RoadName].length > 0)) { oslName += oslWazeifyStreetName(oslLocatorElements[OSL_ELM.RoadName], false); oslPrevStreetName = oslName; } else { oslName = ""; } oslInitReactUpdates(); oslUpdateReactTextInput('#segment-edit-general .street-name', oslName); // Update the City field if(oslUsingNewName) { if(document.getElementById('optUseNewManual').checked === true) { sessionStorage.cityNameRB = 'optUseNewManual'; } else if(document.getElementById('optUseExistingWME').checked === true) { sessionStorage.cityNameRB = 'optUseExistingWME'; } else if(document.getElementById('optUseOS').checked === true) { sessionStorage.cityNameRB = 'optUseOS'; } if(oslUseName) { oslUpdateReactTextInput('#segment-edit-general .city-name', oslCityName); } } else if(document.getElementById('optClearExisting').checked === true) { sessionStorage.cityNameRB = 'optClearExisting'; oslUpdateReactTextInput('#segment-edit-general .city-name', ""); } else { sessionStorage.cityNameRB = 'optUseExisting'; } if(oslAdvancedMode) { // Auto-click the Apply button if the user has a suitably high editing rank document.querySelector('.address-form').querySelector('.save-button').click(); } } function oslMatch(oslLink, oslArea, oslRadioID, oslAltRadioID) { this.oslLink = oslLink; this.oslArea = oslArea; this.oslRadioID = oslRadioID; this.oslAltRadioID = oslAltRadioID; } function oslSortCandidates(a,b) { let x = a.oslArea; let y = b.oslArea; return((x<y) ? -1 : ((x>y) ? 1 : 0)); } function oslCityNameKeyup() { oslCheckCityNameDuplicates(oslCaseCorrect(document.getElementById('myCityName').value),0); document.getElementById('optUseNewManual').checked = true; } function oslSelectWMEName() { let oWCN = document.getElementById('oslWMECityNames'); let cityName = oWCN.options[oWCN.options.selectedIndex].text; oslCheckCityNameDuplicates(cityName,0); document.getElementById('optUseExistingWME').checked = true; } function oslSelectOSName() { let oOCN = document.getElementById('oslOSCityNames'); let cityName = oOCN.options[oOCN.options.selectedIndex].text; cityName = cityName.substring(0,cityName.indexOf('[')-1); document.getElementById('optUseOS').checked = true; document.getElementById('oslCNInfo').innerHTML = ''; } function oslBlockCacheObj(blockName) { this.blockName = blockName; this.lastAccessed = Math.floor(new Date().getTime() / 1000); } function oslLoadBlocks() { for(let i = 0; i < oslBlocksToLoad.length;) { // inject block data let script = document.createElement("script"); script.setAttribute('type','text/javascript'); script.setAttribute('charset','UTF-8'); script.src = oslBlocksToLoad[i]; document.head.appendChild(script); oslLoadingMsg = true; oslBlockCacheList.push(new oslBlockCacheObj(oslBlocksToLoad[i+1])); i += 2; } } function oslSplitEntry(entryString) { let elements = entryString.split(":"); if(entryString != "null:null") { let ts=elements[OSL_ELM.Geometry].split(/[ ]+/).map(Number); let tsLon=[]; let tsLat=[]; ts.forEach((a,i) => {(i % 2 === 0) ? tsLon.push(a) : tsLat.push(a);}); elements[OSL_ELM.BoundE] = Math.max(...tsLon); elements[OSL_ELM.BoundW] = Math.min(...tsLon); elements[OSL_ELM.BoundN] = Math.max(...tsLat); elements[OSL_ELM.BoundS] = Math.min(...tsLat); } return elements; } function oslToOSGrid(lat, lon, mode) { if(oslInUK === false) return; if(oslMousePixelpos == null) return; // Correct mouse X pos if map viewport width has changed (e.g. when clicking on segment causing sidepanel to open...) let vpWidthDelta = (oslLastViewportWidth - W.map.getViewport().getBoundingClientRect().width); oslLastViewportWidth = W.map.getViewport().getBoundingClientRect().width; oslMousePixelpos.x -= vpWidthDelta; if((lat !== 0) && (mode != OSL_MODE.OpenRoads)) { if(mode == OSL_MODE.NameCheck) { oslEastings = lat; oslNorthings = lon; } else { oslLatitude = lat; oslLongitude = lon; oslWGStoOSGB(); } } if((mode == OSL_MODE.OpenNames) || (mode == OSL_MODE.NameCheck)) { let eBlock; let nBlock; // determine which grid block contains the current mouse position let eBlock_point = (Math.floor(oslEastings/oslLocatorBlockSize)) * oslLocatorBlockSize; let nBlock_point = (Math.floor(oslNorthings/oslLocatorBlockSize)) * oslLocatorBlockSize; let candidates = []; oslBlocksToLoad = []; let arrayName; let oslEvalString; for(let x = -1; x < 2; x++) { for(let y = -1; y < 2; y++) { eBlock = (eBlock_point + (oslLocatorBlockSize * x)); nBlock = (nBlock_point + (oslLocatorBlockSize * y)); // check we're within the outer bounds of the current OS dataset... if ( (eBlock >= 64000) && (eBlock <= 655999) && (nBlock >= 8000) && (nBlock <= 1214999) ) { // check to see if there's a corresponding array already loaded... arrayName = 'locatorData_'+eBlock+'_'+nBlock; if(typeof unsafeWindow[arrayName] == "undefined") { // create a blank placeholder which will get replaced by the actual data if the array is present on the server... unsafeWindow[arrayName] = []; oslBlocksToLoad.push(oslBlockPath+Math.floor(eBlock / 100000)+'/'+Math.floor(nBlock / 100000)+'/'+arrayName+'.user.js'); oslBlocksToLoad.push(arrayName); } } } } if(oslBlocksToLoad.length > 0) { oslLoadBlocks(); } candidates = []; if(mode == OSL_MODE.OpenNames) candidates[candidates.length++] = new oslMatch('<label style="display:inline;"><input type="radio" name="oslChoice" id="oslID_null_null_null" />Un-named segment</label><br>',1000000000000,'oslID_null_null_null',null); for(let x = -1; x < 2; x++) { for(let y = -1; y < 2; y++) { eBlock = (eBlock_point + (oslLocatorBlockSize * x)); nBlock = (nBlock_point + (oslLocatorBlockSize * y)); // check we're within the outer bounds of the current OS dataset... if ((eBlock >= 64000) && (eBlock <= 655999) && (nBlock >= 8000) && (nBlock <= 1214999)) { arrayName = 'locatorData_'+eBlock+'_'+nBlock; oslEvalString = arrayName; if(typeof unsafeWindow[arrayName] != "undefined") { oslLoadingMsg = false; // yes... if((eBlock != oslEvalEBlock) || (nBlock != oslEvalNBlock)) { oslEvalEBlock = eBlock; oslEvalNBlock = nBlock; oslBlockData = unsafeWindow['locatorData_'+eBlock+'_'+nBlock]; } for (let bcObj in oslBlockCacheList) { if(oslBlockCacheList[bcObj].blockName == arrayName) { oslBlockCacheList[bcObj].lastAccessed = Math.floor(new Date().getTime() / 1000); } } let preselect = false; for(let loop = 0;loop < oslBlockData.length; loop++) { let locatorElements = []; // for each entry in the array, test the centrepoint position to see if it lies within the bounding box for that entry // note that we allow a 30m tolerance on all sides of the box to allow for inaccuracies in the latlon->gridref conversion, // and to increase the chance of a successful match when the road runs E-W or N-S and thus has a long but narrow bounding box locatorElements = oslSplitEntry(oslBlockData[loop]); if((locatorElements[OSL_ELM.RoadName].length > 0) || (locatorElements[OSL_ELM.RoadNumber].length > 0)) { let tolE; let tolN; if(mode == OSL_MODE.NameCheck) { // wider tolerance when doing a namecheck lookup, to reduce falsely flagging roads as being mis-named tolE = 20; tolN = 30; } else { // tighter tolerance when doing other lookups tolE = 10; tolN = 10; } let streetName = ''; if(locatorElements[OSL_ELM.RoadNumber].length > 0) { streetName += locatorElements[OSL_ELM.RoadNumber]; if(locatorElements[OSL_ELM.RoadName].length > 0) { streetName += ' - '; } } streetName += oslWazeifyStreetName(locatorElements[OSL_ELM.RoadName], false); let altName = ''; if(locatorElements[OSL_ELM.AltName] != '') { if(locatorElements[OSL_ELM.RoadNumber].length > 0) { altName += locatorElements[OSL_ELM.RoadNumber]; if(locatorElements[OSL_ELM.AltName].length > 0) { altName += ' - '; } } altName += oslWazeifyStreetName(locatorElements[OSL_ELM.AltName], false); } if(mode == OSL_MODE.OpenNames) { // Name search from mouse position... let bbPix = oslGetBBCornerPixels(parseFloat(locatorElements[OSL_ELM.BoundW]), parseFloat(locatorElements[OSL_ELM.BoundE]), parseFloat(locatorElements[OSL_ELM.BoundS]), parseFloat(locatorElements[OSL_ELM.BoundN])); bbPix[0] -= tolE; bbPix[1] += tolE; bbPix[2] += tolN; bbPix[3] -= tolN; if((oslMousePixelpos.x >= bbPix[0])&&(oslMousePixelpos.x <= bbPix[1])&&(oslMousePixelpos.y <= bbPix[2])&&(oslMousePixelpos.y >= bbPix[3])) { let area = ((bbPix[1] - bbPix[0]) * (bbPix[2] - bbPix[3])); let radioID = 'oslID_'+eBlock+'_'+nBlock+'_'+loop; let altRadioID = null; let oslLink = '<label style="display:inline;"><input type="radio" name="oslChoice" id="'+radioID+'"'; if((streetName == oslPrevStreetName)&&(preselect === false)) { oslLink += 'checked="true"'; preselect = true; } oslLink += '/>'; oslLink += streetName+' [<i>'+locatorElements[OSL_ELM.AreaName]+'</i>]</label>'; if(altName != '') { altRadioID = 'alt-'+radioID; oslLink += '<br> <label style="display:inline;"><input type="radio" name="oslChoice" id="'+altRadioID+'"'; if((altName == oslPrevStreetName)&&(preselect === false)) { oslLink += 'checked="true"'; preselect = true; } oslLink += '/>'; oslLink += '<i>Alt name: '+altName+'</i></label>'; } if(locatorElements[OSL_ELM.Form] == 'Collapsed Dual Carriageway') { locatorElements[OSL_ELM.Form] = 'Dual Carriageway'; } oslLink += '<br> <i>'+oslRoadClassifications[locatorElements[OSL_ELM.Classification]] + ' - '; oslLink += oslRoadFunctions[locatorElements[OSL_ELM.Function]] + ' - ' + oslFormsOfWay[locatorElements[OSL_ELM.Form]]; if(locatorElements[OSL_ELM.Structure] !== '') { oslLink += ' ' + oslRoadStructures[locatorElements[OSL_ELM.Structure]]; } if(locatorElements[OSL_ELM.IsPrimary] == 'Y') { oslLink += ' (PRN)'; } if(locatorElements[OSL_ELM.IsTrunk] == 'Y') { oslLink += ' (TRN)'; } oslLink += '</i><br>'; candidates[candidates.length++] = new oslMatch(oslLink,area,radioID,altRadioID); } } else if(mode == OSL_MODE.NameCheck) { // NameCheck comparisons... let bbW = parseFloat(locatorElements[OSL_ELM.BoundW]) - tolE; let bbE = parseFloat(locatorElements[OSL_ELM.BoundE]) + tolE; let bbS = parseFloat(locatorElements[OSL_ELM.BoundS]) - tolN; let bbN = parseFloat(locatorElements[OSL_ELM.BoundN]) + tolN; for(let i=0;i<oslOSLNCSegments.length;i++) { if(oslOSLNCSegments[i].match === false) { if ( ((oslOSLNCSegments[i].lonA >= bbW) && (oslOSLNCSegments[i].lonA <= bbE)) && ((oslOSLNCSegments[i].lonB >= bbW) && (oslOSLNCSegments[i].lonB <= bbE)) && ((oslOSLNCSegments[i].latA >= bbS) && (oslOSLNCSegments[i].latA <= bbN)) && ((oslOSLNCSegments[i].latB >= bbS) && (oslOSLNCSegments[i].latB <= bbN)) ) { if((oslOSLNCSegments[i].streetname == streetName) || (oslOSLNCSegments[i].streetname == altName)) oslOSLNCSegments[i].match = true; } } } } } } } } } } if(mode == OSL_MODE.OpenNames) { let newHTML = '<b>Matches at '+oslEastings+', '+oslNorthings+'</b>'; if(candidates.length > 0) { let btnStyle = "cursor:pointer;"; btnStyle += "font-size:14px;"; btnStyle += "border: thin outset black;"; btnStyle += "padding:2px 10px 2px 10px;"; btnStyle += "background: #ccccff;"; newHTML += '<div style="margin:10px;"><span id="oslSelect" style="'+btnStyle+'">'; if(oslAdvancedMode) newHTML += 'Apply to Properties'; else newHTML += 'Copy to Properties'; newHTML += '</span></div>'; if(candidates.length > 1) candidates.sort(oslSortCandidates); for(let loop=0;loop<candidates.length;loop++) { newHTML += candidates[loop].oslLink; } newHTML += '<br>City name:<br>'; newHTML += '<label style="display:inline;"><input type="radio" name="oslCityNameOpt" id="optUseExisting"/>Use existing segment name(s)</label><br>'; newHTML += '<label style="display:inline;"><input type="radio" name="oslCityNameOpt" id="optClearExisting" />Clear existing segment name(s)</label><br>'; newHTML += '<label style="display:inline;"><input type="radio" name="oslCityNameOpt" id="optUseNewManual" />Use new name:</label><br>'; newHTML += ' <input id="myCityName" style="font-size:14px; line-height:16px; height:22px; margin-bottom:4px; transition:none; focus:none; box-shadow:none" type="text"'; if(sessionStorage.cityNameRB == 'optUseNewManual') newHTML += 'value="'+sessionStorage.myCity+'"/><br>'; else newHTML += 'value=""/><br>'; newHTML += '<label style="display:inline;"><input type="radio" name="oslCityNameOpt" id="optUseExistingWME" />Use name from map:</label><br>'; newHTML += ' <select id="oslWMECityNames"></select><br>'; newHTML += '<label style="display:inline;"><input type="radio" name="oslCityNameOpt" id="optUseOS" />Use name from OS Gazetteer:</label><br>'; newHTML += ' <select id="oslOSCityNames"></select><br>'; newHTML += '<div id="oslCNInfo"></div><br>'; oslRoadNameMatches.innerHTML = newHTML; oslGetNearbyCityNames(); let oWCN = document.getElementById('oslWMECityNames'); var nameList = []; let mc = W.map.getCenter(); for(let cityObj in W.model.cities.objects) { if(W.model.cities.objects.hasOwnProperty(cityObj)) { let cityAttrs = W.model.cities.objects[cityObj].attributes; if(cityAttrs.countryID === 234) { let cityname = cityAttrs.name; if(cityname !== '') { // WME now pollutes W.model.cities.objects with a myriad of places entirely unconnected to // anything in the current WME view, issue tracker list etc. The countryID check above // manages to at least filter out the obviously useless entries, however that still leaves // all the "local" ones which may well be at t'other end of the country and thus local only // insofar as they're within the UK somewhere... // // To therefore deal with this, and continue presenting a genuinely useful list of potential // city names to the user, we now perform a further distance-based filtering step to cull // any entries for places more than 10km away from the present centrepoint of the WME view. let cLL = new OpenLayers.LonLat(cityAttrs.geometry.x, cityAttrs.geometry.y); let diffX = mc.lon - cLL.lon; let diffY = mc.lat - cLL.lat; let distToSquared = (diffX * diffX) + (diffY * diffY); if(distToSquared < (10000 * 10000)) { let countyID = []; countyID[0] = cityAttrs.stateID; let countyName = W.model.states.getByIds(countyID)[0].attributes.name; if(countyName !== '') { cityname += ', '+countyName; } nameList.push(cityname); } } } } } nameList.sort(); let matchedWMEName = false; for(let i=0; i<nameList.length; i++) { let listOpt = document.createElement('option'); listOpt.text = nameList[i]; oWCN.add(listOpt,null); if(sessionStorage.cityNameRB == 'optUseExistingWME') { if(nameList[i] == sessionStorage.myCity) { oWCN.options.selectedIndex = i; matchedWMEName = true; } } } if((!matchedWMEName) && (sessionStorage.cityNameRB == 'optUseExistingWME')) { oWCN.options.selectedIndex = 0; } document.getElementById('oslSelect').addEventListener("click", oslClick, true); document.getElementById('oslWMECityNames').addEventListener("click", oslSelectWMEName, true); document.getElementById('optUseExistingWME').addEventListener("click", oslSelectWMEName, true); document.getElementById('oslOSCityNames').addEventListener("click", oslSelectOSName, true); document.getElementById('optUseOS').addEventListener("click", oslSelectOSName, true); document.getElementById('myCityName').addEventListener("keyup", oslCityNameKeyup, true); document.getElementById('optUseNewManual').addEventListener("click", oslCityNameKeyup, true); for(let loop=0;loop<candidates.length;loop++) { document.getElementById(candidates[loop].oslRadioID).addEventListener("click", oslRadioClick, true); if(candidates[loop].oslAltRadioID != null) { document.getElementById(candidates[loop].oslAltRadioID).addEventListener("click", oslRadioClick, true); } } document.getElementById(sessionStorage.cityNameRB).checked = true; oslKeepUIVisible(); } else oslRoadNameMatches.innerHTML = newHTML; } } if(mode == OSL_MODE.OpenRoads) { // OpenRoads check let eastingsLeft = (Math.floor(oslVPLeft/oslLocatorBlockSize) - 1) * oslLocatorBlockSize; let eastingsRight = (Math.ceil(oslVPRight/oslLocatorBlockSize) + 1) * oslLocatorBlockSize; let northingsTop = (Math.ceil(oslVPTop/oslLocatorBlockSize) + 1) * oslLocatorBlockSize; let northingsBottom = (Math.floor(oslVPBottom/oslLocatorBlockSize) - 1) * oslLocatorBlockSize; let highlightMode = OSL_ROADRENDERER.Init; for(let eLoop = eastingsLeft; eLoop <= eastingsRight; eLoop += oslLocatorBlockSize) { for(let nLoop = northingsBottom; nLoop <= northingsTop; nLoop += oslLocatorBlockSize) { oslHighlightOpenRoads(eLoop,nLoop,highlightMode); highlightMode = OSL_ROADRENDERER.Render; } } oslHighlightOpenRoads(0,0,OSL_ROADRENDERER.Finalise); } else return '?e='+oslEastings+'&n='+oslNorthings; } function oslRemoveDirSuffix(currentName, dirSuffix) { let dPos = currentName.indexOf(dirSuffix); if(dPos != -1) { let dLength = dirSuffix.length; currentName = currentName.substr(0,dPos) + currentName.substr(dPos+dLength); } return currentName; } function oslNameCheckTrigger() { oslOSLNameCheckTimer = 2; } function oslNameComparison() { for(;oslONC_E<=oslEBlock_max;) { for(;oslONC_N<=oslNBlock_max;) { oslToOSGrid(oslONC_E*oslLocatorBlockSize, oslONC_N*oslLocatorBlockSize, OSL_MODE.NameCheck); if(oslLoadingMsg === true) { window.setTimeout(oslNameComparison,500); return; } oslONC_N++; } oslONC_N = oslNBlock_min; oslONC_E++; } for(let i=0;i<oslOSLNCSegments.length;i++) { if(oslOSLNCSegments[i].match === false) { let pline = oslOSLNCSegments[i].pline; pline.setAttribute("stroke","#000000"); pline.setAttribute("stroke-opacity","0.5"); pline.setAttribute("stroke-width","10"); pline.setAttribute("stroke-dasharray","none"); } } } function oslNCCandidateNew(pline, lonA, latA, lonB, latB, streetname) { this.pline = pline; this.lonA = lonA; this.latA = latA; this.lonB = lonB; this.latB = latB; this.streetname = streetname; this.match = false; } function oslNCStateChange() { if(document.getElementById('_cbNCEnabled').checked === false) { for(let segObj in W.model.segments.objects) { if(W.model.segments.objects.hasOwnProperty(segObj)) { let seg = W.model.segments.objects[segObj]; let pline = W.userscripts.getFeatureElementByDataModel(seg); if(pline !== null) { pline.setAttribute("stroke-width","5"); pline.setAttribute("stroke","#dd7700"); pline.setAttribute("stroke-opacity","0.001"); pline.setAttribute("stroke-dasharray","none"); } } } } else { if(W.map.getZoom() >= 16) oslNameCheck(); } } function oslOpenRoadsStateChange(e) { oslMapIsStaticProcessing(); oslUpdateHighlightCBs(e.srcElement.id); } function oslNameCheck() { if((document.getElementById('_cbNCEnabled').checked === false) || (W.map.getZoom() < 16)) return; let geoCenter=new OpenLayers.LonLat(W.map.getCenter().lon,W.map.getCenter().lat); oslToOSGrid(geoCenter.lat, geoCenter.lon, OSL_MODE.Conversion); if(oslLoadingMsg === true) { window.setTimeout(oslNameCheck,500); return; } oslEBlock_min = 99999; oslEBlock_max = -1; oslNBlock_min = 99999; oslNBlock_max = -1; let mapExtents = oslGetExtent(); let ignoreSegment; oslOSLNCSegments = []; for(let segObj in W.model.segments.objects) { if(W.model.segments.objects.hasOwnProperty(segObj)) { ignoreSegment = false; let seg = W.model.segments.objects[segObj]; let segRT = seg.attributes.roadType; let segBounds = seg.getOLGeometry().getBounds(); if ( (segBounds.left > mapExtents.right) || (segBounds.right < mapExtents.left) || (segBounds.top < mapExtents.bottom) || (segBounds.bottom > mapExtents.top) ) { // ignore segment as it's not visible... ignoreSegment = true; } if ( (segRT < 1) || ((segRT > 3) && (segRT < 6)) || ((segRT > 8) && (segRT < 17)) || ((segRT > 17) && (segRT < 20)) || (segRT > 21) ) { // ignore segment as it's non-driveable... ignoreSegment = true; } if(ignoreSegment === false) { let streetObj = W.model.streets.objects[seg.attributes.primaryStreetID]; if(streetObj !== undefined) { let currentName = streetObj.attributes.name; let pline = W.userscripts.getFeatureElementByDataModel(seg); if((currentName !== null) && (pline !== null)) { currentName = oslRemoveDirSuffix(currentName,' (N)'); currentName = oslRemoveDirSuffix(currentName,' (S)'); currentName = oslRemoveDirSuffix(currentName,' (E)'); currentName = oslRemoveDirSuffix(currentName,' (W)'); currentName = oslRemoveDirSuffix(currentName,' (CW)'); currentName = oslRemoveDirSuffix(currentName,' (ACW)'); let endPointA = new OpenLayers.LonLat(seg.getOLGeometry().components[0].x,seg.getOLGeometry().components[0].y); endPointA.transform(new OpenLayers.Projection("EPSG:900913"),new OpenLayers.Projection("EPSG:4326")); oslToOSGrid(endPointA.lat, endPointA.lon, OSL_MODE.Conversion); let eBlock = Math.floor(oslEastings/oslLocatorBlockSize); let nBlock = Math.floor(oslNorthings/oslLocatorBlockSize); if(eBlock < oslEBlock_min) oslEBlock_min = eBlock; if(eBlock > oslEBlock_max) oslEBlock_max = eBlock; if(nBlock < oslNBlock_min) oslNBlock_min = nBlock; if(nBlock > oslNBlock_max) oslNBlock_max = nBlock; let endPointB = new OpenLayers.LonLat(seg.getOLGeometry().components[seg.getOLGeometry().components.length-1].x,seg.getOLGeometry().components[seg.getOLGeometry().components.length-1].y); endPointB.transform(new OpenLayers.Projection("EPSG:900913"),new OpenLayers.Projection("EPSG:4326")); oslToOSGrid(endPointB.lat, endPointB.lon, OSL_MODE.Conversion); oslOSLNCSegments.push(new oslNCCandidateNew(pline, endPointA.lon, endPointA.lat, endPointB.lon, endPointB.lat, currentName)); eBlock = Math.floor(oslEastings/oslLocatorBlockSize); nBlock = Math.floor(oslNorthings/oslLocatorBlockSize); if(eBlock < oslEBlock_min) oslEBlock_min = eBlock; if(eBlock > oslEBlock_max) oslEBlock_max = eBlock; if(nBlock < oslNBlock_min) oslNBlock_min = nBlock; if(nBlock > oslNBlock_max) oslNBlock_max = nBlock; } } } } } if(oslOSLNCSegments.length > 0) { oslONC_E = oslEBlock_min; oslONC_N = oslNBlock_min; oslNameComparison(); } } function oslTenthSecondTick() { if(oslMOAdded === false) { if(document.getElementById('edit-panel') !== null) { oslAddLog('edit-panel mutation observer added...'); let editPanelMO = new MutationObserver(oslEditPanelCheck); editPanelMO.observe(document.getElementById('edit-panel'),{childList:true,subtree:true}); oslMOAdded = true; } } let hideUI = Boolean ( (document.getElementsByClassName('menu').length > 0) && (document.getElementsByClassName('menu')[0].className.indexOf('not-visible') === -1) && (document.getElementsByClassName('menu')[0].className.indexOf('hide') === -1) ); hideUI = Boolean(hideUI || (document.getElementsByClassName('toolbar-group open').length > 0)); if(hideUI === false) { oslWindow.style.zIndex = 2000; } else { oslWindow.style.zIndex = -2000; } if(!oslAdvancedMode) oslEnableAdvancedOptions(); let oslSelectedItems = W.selectionManager.getSelectedDataModelObjects(); if(oslSelectedItems.length == 1) { if(oslPrevSelected === null) { oslDoOSLUpdate = true; } else if(oslSelectedItems[0].attributes.id != oslPrevSelected) { oslDoOSLUpdate = true; } oslPrevSelected = oslSelectedItems[0].attributes.id; } else { oslPrevSelected = null; } if(document.getElementById('oslSelect') !== null) { let editDisabled = ((document.getElementsByClassName('full-address disabled').length > 0) || (document.getElementsByClassName('full-address-container').length === 0)); if(editDisabled === true) { document.getElementById('oslSelect').style.background = "rgb(160, 160, 160)"; } else { document.getElementById('oslSelect').style.background = "rgb(204, 204, 255)"; } document.getElementById('oslSelect').disabled = editDisabled; } if(oslOSLNameCheckTimer > 0) { if(--oslOSLNameCheckTimer === 0) oslNameCheck(); } if(--oslBlockCacheTestTimer === 0) { oslBlockCacheTestTimer = (oslCacheDecayPeriod * 10); let timeNow = Math.floor(new Date().getTime() / 1000); for(let bcIdx = oslBlockCacheList.length-1; bcIdx >= 0; bcIdx--) { if((timeNow - oslBlockCacheList[bcIdx].lastAccessed) > oslCacheDecayPeriod) { delete unsafeWindow[oslBlockCacheList[bcIdx].blockName]; oslBlockCacheList.splice(bcIdx,1); } } } if((oslDoOSLUpdate === true) && (oslMousepos !== null)) { // update the OS Locator matches oslToOSGrid(oslMousepos.lat,oslMousepos.lon,OSL_MODE.OpenNames); oslDoOSLUpdate = oslLoadingMsg; if(!oslDoOSLUpdate) oslRadioClick(); } if(oslBlocksToTest.length > 0) { for(let i=0; i<oslBlocksToTest.length; i++) { if(typeof unsafeWindow[oslBlocksToTest[i]] != undefined) { oslBlocksToTest.splice(i, 1); } } if(oslBlocksToTest.length === 0) { oslMapIsStaticProcessing(); } } } function oslEnableAdvancedOptions() { if (oslAdvancedMode) return; if(W.loginManager === null) return; if(W.loginManager.isLoggedIn() === true) { let thisUser = W.loginManager.user; if (thisUser !== null && thisUser.getRank() >= 2) { oslAdvancedMode = true; oslAddLog('advanced mode enabled'); } } } function oslUpdateLiveMapLink() { let lmLink = document.getElementById('livemap-link'); if(lmLink === null) { window.setTimeout(oslUpdateLiveMapLink,100); return; } // translate the zoom level between WME and live map. let livemap_zoom = parseInt(sessionStorage.zoom); if (livemap_zoom > 17) livemap_zoom = 17; let livemap_url = 'https://www.waze.com/livemap/?'; livemap_url += 'lon='+sessionStorage.lon; livemap_url += '&lat='+sessionStorage.lat; livemap_url += '&zoom='+livemap_zoom; // Modify existing livemap link to reference current position in WME lmLink.href = livemap_url; lmLink.target = '_blank'; } function oslLonLatToPixel(lon, lat) { let lonlat = new OpenLayers.LonLat(); lonlat.lon = parseFloat(lon); lonlat.lat = parseFloat(lat); return W.map.getPixelFromLonLat(lonlat); } function oslOSGridRefToPixel(osEast, osNorth) { oslOSGBtoWGS(osEast,osNorth); let lonlat = new OpenLayers.LonLat(oslLongitude,oslLatitude); return W.map.getPixelFromLonLat(lonlat); } function oslBoundsCheck(pix1, pix2, width, height) { let xmin = Math.min(pix1.x,pix2.x); let xmax = Math.max(pix1.x,pix2.x); let ymin = Math.min(pix1.y,pix2.y); let ymax = Math.max(pix1.y,pix2.y); let retval = ( (xmin <= width) && (xmax >= 0) && (ymin <= height) && (ymax >= 0) ); return retval; } function oslHighlightOpenRoads(oslEastings, oslNorthings, mode) { oslBlocksToLoad = []; if((mode === OSL_ROADRENDERER.Init) || (mode == OSL_ROADRENDERER.Render)) { let arrayName = 'locatorData_'+oslEastings+'_'+oslNorthings; // check to see if there's a corresponding array loaded already if(typeof unsafeWindow[arrayName] == "undefined") { // create a blank placeholder which will get replaced by the actual data if the array is present on the server... unsafeWindow[arrayName] = []; oslBlocksToLoad.push(oslBlockPath+Math.floor(oslEastings / 100000)+'/'+Math.floor(oslNorthings / 100000)+'/'+arrayName+'.user.js'); oslBlocksToLoad.push(arrayName); oslBlocksToTest.push(arrayName); oslLoadBlocks(); } let divWidth = document.getElementById('WazeMap').offsetWidth; let divHeight = document.getElementById('WazeMap').offsetHeight; let displayPoints = false; if(mode === OSL_ROADRENDERER.Init) { // initialise SVG container oslSegGeoDivInnerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="'+divWidth+'px" height="'+divHeight+'px" version="1.1">'; } if(typeof unsafeWindow[arrayName] == "undefined") return; let oslOpenRoadsData; oslOpenRoadsData = unsafeWindow[arrayName]; // calculate an appropriate stroke width for the current zoom level let strokeWidth = W.map.getZoom() - 12; if(strokeWidth < 2) strokeWidth = 2; if(strokeWidth > 9) strokeWidth = 9; let renderByFunction = []; renderByFunction.push(true); // Undefined roads always get rendered when the OpenRoads layer is enabled renderByFunction.push(document.getElementById('_cbSegGeoMotorway').checked); renderByFunction.push(document.getElementById('_cbSegGeoARoad').checked); renderByFunction.push(document.getElementById('_cbSegGeoBRoad').checked); renderByFunction.push(document.getElementById('_cbSegGeoMinor').checked); renderByFunction.push(document.getElementById('_cbSegGeoLocal').checked); if(oslAdvancedMode == true) { renderByFunction.push(document.getElementById('_cbSegGeoLocalAccess').checked); renderByFunction.push(document.getElementById('_cbSegGeoRestricted').checked); renderByFunction.push(document.getElementById('_cbSegGeoSecondary').checked); } else { renderByFunction.push(false); renderByFunction.push(false); renderByFunction.push(false); } let useFullGeo = document.getElementById('_cbOpenRoadsUseGeo').checked; let enhancePolylines = document.getElementById('_cbOpenRoadsEnhanceGeoVis').checked; let highlightPRN = document.getElementById('_cbOpenRoadsHighlightPRN').checked; let pix1; let pix2; let nameColour; for(let roadIdx = 0; roadIdx < oslOpenRoadsData.length; roadIdx++) { let roadEntry = oslOpenRoadsData[roadIdx]; let elements = oslSplitEntry(roadEntry); let roadFunction = parseInt(elements[OSL_ELM.Function]); if ((elements[OSL_ELM.RoadName] == '') && (elements[OSL_ELM.RoadNumber] == '')) { nameColour = 'cyan'; } else { nameColour = 'magenta'; } let renderPrimary = ((highlightPRN == true) && (elements[OSL_ELM.IsPrimary] == 'Y')); if((renderPrimary == true) || (renderByFunction[roadFunction] == true)) { pix1 = oslLonLatToPixel(elements[OSL_ELM.BoundW], elements[OSL_ELM.BoundS]); pix2 = oslLonLatToPixel(elements[OSL_ELM.BoundE], elements[OSL_ELM.BoundN]); if(oslBoundsCheck(pix1, pix2, divWidth, divHeight) == true) { if(useFullGeo == true) { let geoPairs = elements[OSL_ELM.Geometry].split(' '); let geoPoints = geoPairs[0].split(' '); let isTunnel = (elements[OSL_ELM.Structure] == 1); pix1 = oslLonLatToPixel(geoPoints[0], geoPoints[1]); let pline = ''; for(let pts=1; pts < geoPairs.length; pts++) { geoPoints = geoPairs[pts].split(' '); pix2 = oslLonLatToPixel(geoPoints[0],geoPoints[1]); if(oslBoundsCheck(pix1, pix2, divWidth, divHeight) == true) { if(displayPoints === false) { pline += '<polyline points="'; displayPoints = true; } pline += pix1.x+','+pix1.y+' '+pix2.x+','+pix2.y+' '; } pix1 = pix2; } if(displayPoints === true) { let strokeColour = oslStrokeColoursByFunction[roadFunction]; let strokeArray = ''; if(isTunnel == true) { strokeArray = 'stroke-dasharray:10,10;'; } if(renderPrimary == true) { oslSegGeoDivInnerHTML += pline + '" style="stroke:black'; oslSegGeoDivInnerHTML += ';'+strokeArray+'stroke-width:'+(strokeWidth+25)+';stroke-linecap:round;fill:none"/>'; oslSegGeoDivInnerHTML += pline + '" style="stroke:green'; oslSegGeoDivInnerHTML += ';'+strokeArray+'stroke-width:'+(strokeWidth+21)+';stroke-linecap:round;fill:none"/>'; } if(enhancePolylines == true) { oslSegGeoDivInnerHTML += pline + '" style="stroke:black'; oslSegGeoDivInnerHTML += ';'+strokeArray+'stroke-width:'+(strokeWidth+13)+';stroke-linecap:round;fill:none"/>'; oslSegGeoDivInnerHTML += pline + '" style="stroke:'; oslSegGeoDivInnerHTML += nameColour; oslSegGeoDivInnerHTML += ';'+strokeArray+'stroke-width:'+(strokeWidth+11)+';stroke-linecap:round;fill:none"/>'; } oslSegGeoDivInnerHTML += pline + '" style="stroke:black;'+strokeArray+'stroke-width:'+(strokeWidth+2)+';stroke-linecap:round;fill:none"/>'; oslSegGeoDivInnerHTML += pline + '" style="stroke:'+strokeColour+';'+strokeArray+'stroke-width:'+strokeWidth+';stroke-linecap:round;fill:none"/>'; displayPoints = false; } } else { const minDim = 20; let bWidth = Math.abs(pix2.x-pix1.x); if(bWidth < minDim) bWidth = minDim; let bHeight = Math.abs(pix2.y-pix1.y); if(bHeight < minDim) bHeight = minDim; oslSegGeoDivInnerHTML += '<rect x="'+pix1.x+'" y="'+pix2.y+'" width="'+bWidth+'" height="'+bHeight+'" style="fill:'; oslSegGeoDivInnerHTML += nameColour; oslSegGeoDivInnerHTML += ';stroke:black;stroke-width:4;fill-opacity:0.25;stroke-opacity:0.25"/>'; } } } } } else if(mode == OSL_ROADRENDERER.Finalise) { // finalise SVG oslSegGeoDivInnerHTML += '</svg>'; oslSegGeoDiv.innerHTML = oslSegGeoDivInnerHTML; } else if(mode == OSL_ROADRENDERER.Erase) { // erase SVG oslSegGeoDiv.innerHTML = ''; } } function oslRepositionOverlays() { let divTop = getComputedStyle(W.map.getLayerByUniqueName('segments').div).top; let divLeft = getComputedStyle(W.map.getLayerByUniqueName('segments').div).left; oslBBDiv.style.top = divTop; oslBBDiv.style.left = divLeft; oslSegGeoDiv.style.top = divTop; oslSegGeoDiv.style.left = divLeft; oslNamesDiv.style.top = divTop; oslNamesDiv.style.left = divLeft; } function oslGetCorrectedLonLatFromPixelPos(px, py, toolbarCompensation) { if((toolbarCompensation) && (oslOffsetToolbar)) py -= document.getElementById('toolbar').clientHeight; py -= document.getElementById('topbar-container').clientHeight; let pixelPos = new OpenLayers.Pixel(px, py); let llfp = W.map.getLonLatFromPixel(pixelPos); return new OpenLayers.LonLat(llfp.lon, llfp.lat); } function oslGetOffsetMapCentre() { // get lon/lat of viewport centrepoint for modifying the livemap link and for passing to the external mapping sites. // shift the longitude pixel offset by half the width of the WME sidebar to account for the lateral offset that would // otherwise occur when switching between the WME tab and the other map tabs - all of those use a full-width map view, // so their map centre is further to the left within the browser window than the WME centrepoint... let mapVPX; let mapVPY; if(W.map.olMap == undefined) { mapVPX = (W.map.getOLMap().getViewport().clientWidth / 2) - (document.getElementById('sidebar').clientWidth / 2); mapVPY = W.map.getOLMap().getViewport().clientHeight / 2; } else { mapVPX = (W.map.olMap.getViewport().clientWidth / 2) - (document.getElementById('sidebar').clientWidth / 2); mapVPY = W.map.olMap.getViewport().clientHeight / 2; } return oslGetCorrectedLonLatFromPixelPos(mapVPX, mapVPY, false); } function oslMouseMoveAndUp(e) { if(oslUserPrefs.ui.state == 'minimised') { return; } if((e.pageX !== undefined) && (e.pageY !== undefined)) { // Only valid when a mousemove event occurs... let mapBCR = document.getElementById('map').getBoundingClientRect(); let mouseX = e.pageX - mapBCR.left; let mouseY = e.pageY - mapBCR.top; oslMousePixelpos = new OpenLayers.Pixel(mouseX, mouseY); oslLastViewportWidth = W.map.getViewport().getBoundingClientRect().width; let mouseLonLat = oslGetCorrectedLonLatFromPixelPos(mouseX, mouseY, true); oslMousepos = mouseLonLat; if ( (oslMousepos != sessionStorage.oslMousepos) || ( (oslLoadingMsg == true) && (typeof unsafeWindow[oslEvalString] != undefined) ) ) { oslLoadingMsg = false; sessionStorage.oslMousepos = oslMousepos; oslDoOSLUpdate = false; // update the OSL results if there are no selected segments, but there is a highlighted segment // which we haven't already done an update for oslSegmentHighlighted = false; let noneSelected = (W.selectionManager.getSelectedDataModelObjects().length === 0); if(noneSelected === true) { for(let slIdx=0; slIdx < W.map.getLayerByUniqueName('segments').features.length; slIdx++) { if(W.map.getLayerByUniqueName('segments').features[slIdx].renderIntent == 'highlight') { if(slIdx != oslPrevHighlighted) { oslPrevHighlighted = slIdx; } oslDoOSLUpdate = true; oslSegmentHighlighted = true; } } } } } // Valid for both mousemove and zoomend events... let gomc = oslGetOffsetMapCentre(); let lat = gomc.lat; let lon = gomc.lon; let zoom = W.map.getZoom(); // compare the new parameters against the persistent copies, and update the external links // only if there's a change required - the newly-inserted <a> element can't be clicked // on until the insertion process is complete, and if we were to re-insert it every timeout // then it'd spend a lot of its time giving the appearance of being clickable but without // actually doing anything... if((zoom != parseInt(sessionStorage.zoom))||(lat != parseFloat(sessionStorage.lat))||(lon != parseFloat(sessionStorage.lon))) { let country = W.model.getTopCountry()?.attributes?.name; if(country == "United Kingdom") { if (oslInUK === false) { oslAddLog('location is the UK, enabling full UI...'); oslInUK = true; document.getElementById('oslOSLDiv').style.display = "block"; document.getElementById('oslNCDiv').style.display = "block"; document.getElementById('oslSegGeoUIDiv').style.display = "block"; document.getElementById('oslGazTagsDiv').style.display = "block"; document.getElementById('_extlinksUK').style.display = "block"; } } else { // ...somewhere not yet supported, or WME isn't telling us just yet... oslAddLog('location not recognised, disabling UK-specific parts of UI...'); oslInUK = false; oslInLondon = false; document.getElementById('oslOSLDiv').style.display = "none"; document.getElementById('oslNCDiv').style.display = "none"; document.getElementById('oslSegGeoUIDiv').style.display = "none"; document.getElementById('oslGazTagsDiv').style.display = "none"; document.getElementById('_extlinksUK').style.display = "none"; } if(oslInUK === true) { // we're in the UK, so test to see if we're within the approximate Greater London bounding box if((lon >= -0.55) && (lon <= 0.30) && (lat >= 51.285) && (lat <= 51.695)) { oslInLondon = true; document.getElementById('lrrCtrls').style.display='inline'; } else { oslInLondon = false; document.getElementById('lrrCtrls').style.display='none'; } } if(zoom != sessionStorage.zoom) { if(zoom < 16) document.getElementById('_cbNCEnabled').disabled = true; else document.getElementById('_cbNCEnabled').disabled = false; } // update the persistent vars with the new position sessionStorage.zoom = zoom; sessionStorage.lat = lat; sessionStorage.lon = lon; if(oslInUK === true) { // update the external site datalinks with the new coords/zoom if(document.getElementById('_cbAutoTrackOSOD').checked == 1) { oslOSODClick(); } if(document.getElementById('_cbAutoTrackRWO').checked == 1) { oslUpdateOneKey(); } if(document.getElementById('_cbAutoTrackLRR').checked == 1) { oslUpdateLRRKey(); } } // wait to update the livemap link, as WME now does its own update after this point so any changes we make here // end up being wiped out... window.setTimeout(oslUpdateLiveMapLink,100); document.getElementById('_linkPermalink').href = document.getElementsByClassName('WazeControlPermalink')[0].getElementsByClassName('permalink')[0].href; } if((e.type == "mouseup") || (e.type == "zoomend")) { oslMapIsStaticProcessing(); } } function oslSetAccessKey(keyName, requiresTransform, requiresOSCoords) { let lat = "0.0"; let lon = "0.0"; let zoom = W.map.getZoom(); if(requiresOSCoords === true) { let geoCenter=new OpenLayers.LonLat(W.map.getCenter().lon,W.map.getCenter().lat); geoCenter.transform(new OpenLayers.Projection("EPSG:900913"),new OpenLayers.Projection("EPSG:4326")); oslToOSGrid(geoCenter.lat, geoCenter.lon, OSL_MODE.Conversion); lat = oslNorthings; lon = oslEastings; } else { let gomc = oslGetOffsetMapCentre(); if(requiresTransform === true) { gomc = gomc.transform(new OpenLayers.Projection("EPSG:4326"),new OpenLayers.Projection("EPSG:900913")); } lat = gomc.lat.toFixed(8); lon = gomc.lon.toFixed(8); } GM_setValue(keyName,Date.now()+','+lat+','+lon+','+zoom); } function oslOSODClick() { oslSetAccessKey('_osodAccessKey', true, false); } function oslUpdateOneKey() { oslSetAccessKey('_oneAccessKey', false, false); } function oslUpdateLRRKey() { oslSetAccessKey('_lrrAccessKey', false, true); } function oslMapIsStaticProcessing() { if(oslUserPrefs.ui.state == 'maximised') { let tCenter = W.map.getCenter(); let tZoom = W.map.getZoom(); if ( (oslRORCenter == null) || (tCenter.lat != oslRORCenter.lat) || (tCenter.lon != oslRORCenter.lon) || (oslRORZoom == null) || (tZoom != oslRORZoom) ) { oslRORCenter = tCenter; oslRORZoom = tZoom; window.setTimeout(oslMapIsStaticProcessing, 50); return; } let geoCenter=new OpenLayers.LonLat(W.map.getCenter().lon,W.map.getCenter().lat); geoCenter.transform(new OpenLayers.Projection("EPSG:900913"),new OpenLayers.Projection("EPSG:4326")); oslToOSGrid(geoCenter.lat, geoCenter.lon, OSL_MODE.Conversion); oslNameCheck(); if(oslInUK === true) { let extent = oslGetExtent(); // recalculate the map viewport extents in terms of oslEastings/oslNorthings let vpHalfWidth = (extent.right - extent.left) / (2 * 1.61); let vpHalfHeight = (extent.top - extent.bottom) / (2 * 1.61); oslVPLeft = oslEastings - vpHalfWidth; oslVPRight = oslEastings + vpHalfWidth; oslVPBottom = oslNorthings - vpHalfHeight; oslVPTop = oslNorthings + vpHalfHeight; if(document.getElementById('_cbOpenRoadsEnabled').checked === true) { oslToOSGrid(oslEastings,oslNorthings,OSL_MODE.OpenRoads); } else { oslHighlightOpenRoads(0,0,OSL_ROADRENDERER.Erase); } oslGetVisibleCityNames(); } else { oslHighlightOpenRoads(0,0,OSL_ROADRENDERER.Erase); } oslRepositionOverlays(); oslRadioClick(); // this regenerates the bounding boxes on the repositioned overlay... } } function oslTestPointerOutsideMap(mX, mY) { let mapElm = document.getElementById("map"); if(mapElm === undefined) return false; let bLeft = mapElm.parentElement.offsetLeft; let bRight = (bLeft + mapElm.offsetWidth); let bTop = (mapElm.parentElement.offsetTop + document.getElementById("topbar-container").clientHeight); let bBottom = (mapElm.parentElement.offsetTop + mapElm.offsetHeight + document.getElementById("topbar-container").clientHeight - document.getElementsByClassName("wz-map-ol-footer")[0].clientHeight); if((mX < bLeft) || (mX > bRight) || (mY < bTop) || (mY > bBottom)) return true; else return false; } function oslMouseOut(e) { if(oslTestPointerOutsideMap(e.clientX, e.clientY)) { // when the mouse pointer leaves the map area, WME treats it similarly to a mouseup // event without generating an actual mouseup event, so we need to do the same... oslMapIsStaticProcessing(); } } function oslCancelEvent(e) { e = e ? e : window.event; if(e.stopPropagation) e.stopPropagation(); if(e.preventDefault) e.preventDefault(); e.cancelBubble = true; e.cancel = true; e.returnValue = false; return false; } function oslOSLDivMouseDown(e) { oslPrevMouseX = e.pageX; oslPrevMouseY = e.pageY; oslDivDragging = true; oslDragBar.style.cursor = 'move'; document.body.addEventListener('mousemove', oslOSLDivMouseMove, false); document.body.addEventListener('mouseup', oslOSLDivMouseUp, false); // lock the UI width during a drag so we can correctly detect for falling off the window edge oslWindow.style.width = oslWindow.getBoundingClientRect().width+'px'; return true; } function oslOSLDivMouseUp() { if(oslDivDragging) { oslDivDragging = false; oslUserPrefs.ui.x = oslOSLDivLeft; oslUserPrefs.ui.y = oslOSLDivTop; oslWriteUserPrefs(); // unlock the UI width again so we can expand/contract as required based on other script behaviour oslWindow.style.width = "auto"; } oslDragBar.style.cursor = 'auto'; document.body.removeEventListener('mousemove', oslOSLDivMouseMove, false); document.body.removeEventListener('mouseup', oslOSLDivMouseUp, false); return true; } function oslOSLDivMouseMove(e) { let vpHeight = window.innerHeight; let vpWidth = window.innerWidth; oslOSLDivTop = parseInt(oslOSLDivTop) + parseInt((e.pageY - oslPrevMouseY)); oslOSLDivLeft = parseInt(oslOSLDivLeft) + parseInt((e.pageX - oslPrevMouseX)); oslPrevMouseX = e.pageX; oslPrevMouseY = e.pageY; if(oslOSLDivTop < 0) oslOSLDivTop = 0; if(oslOSLDivTop + 16 >= vpHeight) oslOSLDivTop = vpHeight-16; if(oslOSLDivLeft < 0) oslOSLDivLeft = 0; if(oslOSLDivLeft + 32 >= vpWidth) oslOSLDivLeft = vpWidth-32; oslWindow.style.top = oslOSLDivTop+'px'; oslWindow.style.left = oslOSLDivLeft+'px'; return oslCancelEvent(e); } function oslKeepUIVisible() { let vpHeight = window.innerHeight; let topbarHeight = document.getElementById('toolbar').offsetHeight; let maxUIHeight = (vpHeight - topbarHeight); // Restore auto-sizing so we can see the effect of whatever UI change caused us to get here... oslWindow.style.height = "auto"; oslWindow.style.overflow = "hidden"; // If the bottom edge of the ui would fall off the bottom edge of the map viewport, nudge // the UI up as far as required to keep the whole UI visible. If this would however require // the top edge of the UI to be pushed off the top of the map viewport, constrain the UI // height to the viewport height and enable a scrollbar... if(oslWindow.getBoundingClientRect().bottom >= maxUIHeight) { let newTop = (vpHeight-oslWindow.getBoundingClientRect().height); if(newTop < topbarHeight) { newTop = topbarHeight; oslWindow.style.height = maxUIHeight+'px'; oslWindow.style.overflow = "scroll"; } oslOSLDivTop = newTop; oslUserPrefs.ui.y = oslOSLDivTop; oslWindow.style.top = oslOSLDivTop+'px'; } } function oslMinimiseDiv(divID) { let tDiv = document.getElementById(divID); if(tDiv != null) { tDiv.style.height = '0px'; tDiv.style.padding = '0px'; tDiv.style.overflow = 'hidden'; } } function oslMaximiseDiv(divID) { let tDiv = document.getElementById(divID); if(tDiv != null) { tDiv.style.height = 'auto'; tDiv.style.padding = '2px'; tDiv.style.overflow = 'auto'; } } function oslWindowMaximise() { let tHTML = ''; tHTML += '<span style="float:left"><a href="'+oslUpdateURL+'" target="_blank"><b>WMEOpenData</a> v'+oslVersion+'</b></span>'; tHTML += '<span id="_minimax" style="float:right"><i class="fa fa-chevron-circle-up"></i></span>'; tHTML += '<br>'; oslDragBar.innerHTML = tHTML; document.getElementById('_minimax').addEventListener('click', oslWindowMinimise, false); oslMaximiseDiv('oslOSLDiv'); oslMaximiseDiv('oslNCDiv'); oslMaximiseDiv('oslGazTagsDiv'); oslMaximiseDiv('oslSegGeoUIDiv'); oslMaximiseDiv('oslMLCDiv'); oslKeepUIVisible(); oslUserPrefs.ui.state = 'maximised'; oslWriteUserPrefs(); oslRepositionOverlays(); oslMapIsStaticProcessing(); } function oslWindowMinimise() { let tHTML = ''; tHTML += '<span style="float:left"><b>WMEOpenData v'+oslVersion+'</b></span>'; tHTML += '<span id="_minimax" style="float:right"><i class="fa fa-chevron-circle-down"></i></span>'; tHTML += '<br>'; oslDragBar.innerHTML = tHTML; document.getElementById('_minimax').addEventListener('click', oslWindowMaximise, false); oslMinimiseDiv('oslOSLDiv'); oslMinimiseDiv('oslNCDiv'); oslMinimiseDiv('oslGazTagsDiv'); oslMinimiseDiv('oslSegGeoUIDiv'); oslMinimiseDiv('oslMLCDiv'); oslKeepUIVisible(); oslBBDiv.innerHTML = ''; oslNamesDiv.innerHTML = ''; oslSegGeoDiv.innerHTML = ''; oslUserPrefs.ui.state = 'minimised'; oslWriteUserPrefs(); } function oslSubDivSetState(divID, state) { let tSubDiv = document.getElementById(divID).children[1]; let tToggle = document.getElementById(divID).children[0].children[1].children[0]; if(state == 'minimised') { tSubDiv.style.height = "0px"; tSubDiv.style.padding = "0px"; tSubDiv.style.overflow = "hidden"; tToggle.className = "fa fa-chevron-circle-down"; } else { tSubDiv.style.height = "auto"; tSubDiv.style.width = "100%"; tSubDiv.style.padding = "2px"; tSubDiv.style.overflow = "auto"; tToggle.className = "fa fa-chevron-circle-up"; } } function oslUIMinMax(e) { let headerDivID = e.srcElement.parentElement.parentElement.parentElement.id; let tSubDiv = document.getElementById(headerDivID).children[1]; if(tSubDiv != null) { let newState; if(tSubDiv.style.height == "auto") { newState = 'minimised'; } else { newState = 'maximised'; } oslSubDivSetState(headerDivID, newState); oslUpdateMinMax(headerDivID, newState); } oslKeepUIVisible(); } function oslIsLanesTabActive() { let retval = false; if(document.querySelector('wz-tab.lanes-tab') !== null) { if(document.querySelector('wz-tab.lanes-tab').getAttribute("is-active") !== null) { retval = true; } } return retval; } function oslEditPanelCheck() { if ( (oslIsLanesTabActive() == true) || (document.getElementById('edit-panel')?.getElementsByClassName('map-comment-feature-editor').length) ) { document.body.style.overflow = "auto"; } else { document.body.style.overflow = "hidden"; } } function oslSetupBBDiv() { // add a new div to the map viewport, to hold the bounding box SVG oslAddLog('create bounding box DIV'); oslBBDiv = document.createElement('div'); oslBBDiv.id = "oslBBDiv"; oslBBDiv.style.position = 'absolute'; oslBBDiv.style.top = getComputedStyle(W.map.getLayerByUniqueName('segments').div).top; oslBBDiv.style.left = getComputedStyle(W.map.getLayerByUniqueName('segments').div).left; oslBBDiv.style.overflow = 'hidden'; oslBBDiv.style.width = window.innerWidth; oslBBDiv.style.height = window.innerHeight; oslWazeMapElement.appendChild(oslBBDiv); } function oslSetupORDiv() { // add a new div to the map viewport, to hold the OpenRoads SVG oslAddLog('create OpenRoads DIV'); oslSegGeoDiv = document.createElement('div'); oslSegGeoDiv.id = "oslSegGeoDiv"; oslSegGeoDiv.style.position = 'absolute'; oslSegGeoDiv.style.top = getComputedStyle(W.map.getLayerByUniqueName('segments').div).top; oslSegGeoDiv.style.left = getComputedStyle(W.map.getLayerByUniqueName('segments').div).left; oslSegGeoDiv.style.overflow = 'hidden'; oslSegGeoDiv.style.width = window.innerWidth; oslSegGeoDiv.style.height = window.innerHeight; oslWazeMapElement.appendChild(oslSegGeoDiv); } function oslSetupNamesDiv() { // add a new div to the map viewport, to hold the place names SVG oslAddLog('create place names DIV'); oslNamesDiv = document.createElement('div'); oslNamesDiv.id = "oslNamesDiv"; oslNamesDiv.style.position = 'absolute'; oslNamesDiv.style.pointerEvents = 'none'; oslNamesDiv.style.top = getComputedStyle(W.map.getLayerByUniqueName('segments').div).top; oslNamesDiv.style.left = getComputedStyle(W.map.getLayerByUniqueName('segments').div).left; oslNamesDiv.style.overflow = 'hidden'; oslNamesDiv.style.width = window.innerWidth; oslNamesDiv.style.height = window.innerHeight; oslWazeMapElement.appendChild(oslNamesDiv); } function oslGenerateOpenRoadsCBHTML(isStd, ID, label, indentLevel) { let tHTML = ''; let indent = 'padding-left: '+(indentLevel * 0.5)+'em;'; tHTML += '<label style="display: inline; '+indent+'">'; tHTML += '<input type="checkbox" name="oslOpenRoads_'; if(isStd == true) tHTML += 'std'; else tHTML += 'adv'; tHTML += '" id="' + ID + '" />'; tHTML += label + '</label>'; return tHTML; } function oslGenerateCollapsibleSubDiv(headerText, subDivHTML) { let tHTML; tHTML = '<div><span style="float:left"><b>'+headerText+'</b></span>'; tHTML += '<span name="oslMinMaxToggle" style="float:right"><i class="fa fa-chevron-circle-down"></i></span></div>'; tHTML += '<div style="height: 0px; padding: 0px; overflow: hidden;">'; tHTML += subDivHTML; tHTML += '</div>'; return tHTML; } function oslSetupUI() { let subDivHTML; // add a new div to hold the OS Locator results, in the form of a draggable window oslAddLog('create lookup results DIV'); oslWindow = document.createElement('div'); oslWindow.id = "oslWindow"; oslWindow.style.position = 'absolute'; oslWindow.style.border = '1px solid #BBDDBB'; oslWindow.style.borderRadius = '4px'; oslWindow.style.overflow = 'hidden'; oslWindow.style.zIndex = 2000; oslWindow.style.opacity = 0; oslWindow.style.transitionProperty = "opacity"; oslWindow.style.transitionDuration = "1000ms"; oslWindow.style.webkitTransitionProperty = "opacity"; oslWindow.style.webkitTransitionDuration = "1000ms"; oslWindow.style.boxShadow = '5px 5px 10px Silver'; document.body.appendChild(oslWindow); // dragbar div oslAddLog('create dragbar DIV'); oslDragBar = document.createElement('div'); oslDragBar.id = "oslDragBar"; oslDragBar.style.backgroundColor = '#D0D0D0'; oslDragBar.style.padding = '4px'; oslDragBar.style.fontSize = '16px'; oslDragBar.style.lineHeight = '18px'; oslWindow.appendChild(oslDragBar); // OS results div oslAddLog('create results DIV'); oslOSLDiv = document.createElement('div'); oslOSLDiv.id = "oslOSLDiv"; oslOSLDiv.style.backgroundColor = '#DDFFDD'; oslOSLDiv.style.padding = '2px'; oslOSLDiv.style.fontSize = '14px'; oslOSLDiv.style.lineHeight = '16px'; oslOSLDiv.style.display = 'none'; subDivHTML = "<div id='oslRoadNameMatches'></div>"; oslOSLDiv.innerHTML = oslGenerateCollapsibleSubDiv("OS Open Names", subDivHTML); oslWindow.appendChild(oslOSLDiv); // Segment geometry control div oslAddLog('create SegGeo control DIV'); oslSegGeoUIDiv = document.createElement('div'); oslSegGeoUIDiv.id = "oslSegGeoUIDiv"; oslSegGeoUIDiv.style.backgroundColor = '#40A040'; oslSegGeoUIDiv.style.padding = '2px'; oslSegGeoUIDiv.style.fontSize = '14px'; oslSegGeoUIDiv.style.lineHeight = '16px'; oslSegGeoUIDiv.style.display = 'none'; subDivHTML = oslGenerateOpenRoadsCBHTML(true, "_cbOpenRoadsEnabled", "Highlight by Classification:", 0) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbSegGeoMotorway", "Motorway", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbSegGeoARoad", "A Road", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbSegGeoBRoad", "B Road", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbSegGeoMinor", "Minor Road", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbSegGeoLocal", "Local Road", 1) + '<br>'; if(oslAdvancedMode == true) { subDivHTML += oslGenerateOpenRoadsCBHTML(false, "_cbSegGeoLocalAccess", "Local Access Road", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(false, "_cbSegGeoRestricted", "Restricted Access Road", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(false, "_cbSegGeoSecondary", "Secondary Access Road", 1) + '<br>'; } subDivHTML += '<br>' + oslGenerateOpenRoadsCBHTML(true, "_cbOpenRoadsUseGeo", "Show as polylines", 0) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbOpenRoadsEnhanceGeoVis", "Enhance polyline visibility", 1) + '<br><br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbOpenRoadsHighlightPRN", "Highlight PRN", 1); oslSegGeoUIDiv.innerHTML = oslGenerateCollapsibleSubDiv("OS Open Roads", subDivHTML); oslWindow.appendChild(oslSegGeoUIDiv); // Gazetteer Tags div oslAddLog('create GazTags DIV'); oslGazTagsDiv = document.createElement('div'); oslGazTagsDiv.id = "oslGazTagsDiv"; oslGazTagsDiv.style.backgroundColor = '#FFFF80'; oslGazTagsDiv.style.padding = '2px'; oslGazTagsDiv.style.fontSize = '14px'; oslGazTagsDiv.style.lineHeight = '16px'; oslGazTagsDiv.style.display = 'none'; subDivHTML = oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsEnabled", "Show by type:", 0) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsCity", "City", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsTown", "Town", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsVillage", "Village", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsHamlet", "Hamlet", 1) + '<br>'; subDivHTML += oslGenerateOpenRoadsCBHTML(true, "_cbGazTagsOther", "Other", 1); oslGazTagsDiv.innerHTML = oslGenerateCollapsibleSubDiv("Gazetteer Tags", subDivHTML); oslWindow.appendChild(oslGazTagsDiv); // NameCheck div oslAddLog('create NameCheck DIV'); oslNCDiv = document.createElement('div'); oslNCDiv.id = "oslNCDiv"; oslNCDiv.style.backgroundColor = '#DDDDFF'; oslNCDiv.style.padding = '2px'; oslNCDiv.style.fontSize = '14px'; oslNCDiv.style.lineHeight = '16px'; oslNCDiv.style.display = 'none'; subDivHTML = '<label style="display:inline;"><input type="checkbox" id="_cbNCEnabled" />Highlight potential naming errors</label>'; subDivHTML += '<br><i>Note: only active at at zoom level 16 and above</i>'; oslNCDiv.innerHTML = oslGenerateCollapsibleSubDiv("NameCheck", subDivHTML); oslWindow.appendChild(oslNCDiv); // external links div oslAddLog('create extern links DIV'); oslMLCDiv = document.createElement('div'); oslMLCDiv.id = "oslMLCDiv"; oslMLCDiv.style.backgroundColor = '#EEFFEE'; oslMLCDiv.style.padding = '2px'; oslMLCDiv.style.fontSize = '14px'; oslMLCDiv.style.lineHeight = '16px'; // add the anchors and auto-track checkboxes for external sites. Note that some urls are blank at this stage, // they'll be filled in by oslMouseMoveAndUp()... subDivHTML = '<div id="_extlinksUK" style="display: none;">'; subDivHTML += '<a href="https://search-property-information.service.gov.uk/search/search-by-map/" id="_linkOSOD" target="_osopendata">OS OpenData</a> <input type="checkbox" id="_cbAutoTrackOSOD"></input> | '; subDivHTML += '<a href="https://one.network?showlinks=true" id="_linkRWO" target="_roadworksorg">one.network</a> <input type="checkbox" id="_cbAutoTrackRWO"></input><br>'; subDivHTML += '<div id="lrrCtrls"><a href="https://public.londonworks.gov.uk/roadworks/home" id="_linkLRR" target="_londonregister">London Roadworks</a> <input type="checkbox" id="_cbAutoTrackLRR"></input></div>'; subDivHTML += '</div>'; subDivHTML += '<br>(Checkboxes enable auto-tracking)'; subDivHTML += '<br><br><a href="" id="_linkPermalink">Permalink</a>'; oslMLCDiv.innerHTML = oslGenerateCollapsibleSubDiv("External resources", subDivHTML); oslWindow.appendChild(oslMLCDiv); } function oslSetupEventListeners() { oslAddLog('adding event listeners'); oslDragBar.addEventListener('mousedown', oslOSLDivMouseDown, false); oslDragBar.addEventListener('mouseup', oslOSLDivMouseUp, false); W.map.events.register("zoomend", null, oslMouseMoveAndUp); W.map.events.register("mouseout", null, oslMouseOut); document.getElementById('_cbNCEnabled').addEventListener('click', oslNCStateChange, false); document.getElementById('_linkOSOD').addEventListener('click', oslOSODClick, false); document.getElementById('_linkRWO').addEventListener('click', oslUpdateOneKey, false); document.getElementById('_linkLRR').addEventListener('click', oslUpdateLRRKey, false); for(let idx = 0; idx < document.getElementsByName('oslOpenRoads_std').length; ++idx) { document.getElementsByName('oslOpenRoads_std')[idx].addEventListener('click', oslOpenRoadsStateChange, false); } if(oslAdvancedMode == true) { for(let idx = 0; idx < document.getElementsByName('oslOpenRoads_adv').length; ++idx) { document.getElementsByName('oslOpenRoads_adv')[idx].addEventListener('click', oslOpenRoadsStateChange, false); } } for(let idx = 0; idx < document.getElementsByName('oslMinMaxToggle').length; ++idx) { document.getElementsByName('oslMinMaxToggle')[idx].addEventListener('click', oslUIMinMax, false); } W.map.events.register("mousemove", null, oslMouseMoveAndUp); W.map.events.register("mouseup", null, oslMouseMoveAndUp); W.map.getLayerByUniqueName('segments').events.register("featuresadded", null, oslNameCheckTrigger); W.map.getLayerByUniqueName('segments').events.register("featuresremoved", null, oslNameCheckTrigger); } function oslSetupUIPosition() { oslAddLog('adjusting UI position...'); document.body.style.overflow = 'hidden'; let vpHeight = window.innerHeight; let vpWidth = window.innerWidth; if ( (oslUserPrefs.ui.y === null)|| (oslUserPrefs.ui.x === null)|| (oslUserPrefs.ui.y > vpHeight)|| (oslUserPrefs.ui.x > vpWidth)|| (oslUserPrefs.ui.y < 0)|| (oslUserPrefs.ui.x < 0) ) { oslOSLDivTop = document.getElementById('sidebar').getBoundingClientRect().top + (document.getElementById('sidebar').getBoundingClientRect().height / 2); oslOSLDivLeft = 8; } else { oslOSLDivTop = oslUserPrefs.ui.y; oslOSLDivLeft = oslUserPrefs.ui.x; } if(oslUserPrefs.ui.state === undefined) oslUserPrefs.ui.state = 'maximised'; oslOSLDivTopMinimised = oslOSLDivTop; oslWindow.style.left = oslOSLDivLeft+'px'; oslWindow.style.top = oslOSLDivTop+'px'; if(oslUserPrefs.ui.state == 'maximised') oslWindowMaximise(); else oslWindowMinimise(); oslWindow.style.opacity = 1; } function oslSetLayerZIndices() { oslAddLog('setting layer zIndices...'); for(let i=0; i < W.map.layers.length; i++) { if((W.map.layers[i].uniqueName == 'satellite_imagery')||(W.map.layers[i].name == 'satellite_imagery')) { oslBBDiv.style.zIndex = parseInt(W.map.layers[i].div.style.zIndex) + 1; oslSegGeoDiv.style.zIndex = parseInt(W.map.layers[i].div.style.zIndex) + 2; oslNamesDiv.style.zIndex = parseInt(W.map.layers[i].div.style.zIndex) + 1000; } if(W.map.layers[i].name == 'Spotlight') oslOSLMaskLayer = W.map.layers[i].div; } } function oslSetupAccessKey() { oslAccessKeyUpdate(); GM_addValueChangeListener("_rwoPositionToWME", function() { if(arguments[3] === true) { oslAddLog('WME reposition request from one.network...'); let tPos = new OpenLayers.LonLat(); let posBits = arguments[2].split(','); tPos.lat = parseFloat(posBits[2]); tPos.lon = parseFloat(posBits[3]); tPos.transform(new OpenLayers.Projection("EPSG:4326"),new OpenLayers.Projection("EPSG:900913")); W.map.setCenter(tPos); let tZoom = parseInt(posBits[4]); if(tZoom < 12) tZoom = 12; if(tZoom > 22) tZoom = 22; let cZoom = W.map.getZoom(); if(tZoom > cZoom) { oslAddLog('Zoom in from '+cZoom+' to '+tZoom); for(let i = 0; i < (tZoom - cZoom); ++i) { W.map.zoomIn(); } } else if(tZoom < cZoom) { oslAddLog('Zoom out from '+cZoom+' to '+tZoom); for(let i = 0; i < (cZoom - tZoom); ++i) { W.map.zoomOut(); } } } }); window.setInterval(oslAccessKeyUpdate,5000); } function oslLoadOrInitPrefs() { // See if this system already has something in localStorage let userPrefs = localStorage.oslUserPrefs; if(userPrefs != undefined) { oslUserPrefs = JSON.parse(userPrefs); } // Fill out any undefined parts of the prefs object with default values - this works both to initialise // the object if nothing at all is stored in localStorage, and can also be used to initialise any newer // parameters that might get added later on which aren't yet defined in the localStorage copy if(oslUserPrefs.minmax == undefined) oslUserPrefs.minmax = []; if(oslUserPrefs.openRoads == undefined) oslUserPrefs.openRoads = []; if(oslUserPrefs.ui == undefined) oslUserPrefs.ui = {}; if(oslUserPrefs.ui.x == undefined) oslUserPrefs.ui.x = null; if(oslUserPrefs.ui.y == undefined) oslUserPrefs.ui.y = null; if(oslUserPrefs.ui.state == undefined) oslUserPrefs.ui.state = null; // Import any older preferences set on this system, then remove from localStorage - this should only ever // occur the first time this version of the script is run after updating an existing setup, so there // wouldn't be anything already stored in these three properties that would get wiped out by the import. if(localStorage.oslOSLDivState != undefined) { oslUserPrefs.ui.state = localStorage.oslOSLDivState; localStorage.removeItem("oslOSLDivState"); } if(localStorage.oslOSLDivLeft != undefined) { oslUserPrefs.ui.x = localStorage.oslOSLDivLeft; localStorage.removeItem("oslOSLDivLeft"); } if(localStorage.oslOSLDivTop != undefined) { oslUserPrefs.ui.y = localStorage.oslOSLDivTop; localStorage.removeItem("oslOSLDivTop"); } } function oslApplyPrefs() { // subDiv states for(let i = 0; i < oslUserPrefs.minmax.length; ++i) { oslSubDivSetState(oslUserPrefs.minmax[i][0], oslUserPrefs.minmax[i][1]); } // Open Roads options for(let i = 0; i < oslUserPrefs.openRoads.length; ++i) { let cbID = oslUserPrefs.openRoads[i][0]; if(document.getElementById(cbID) != undefined) { document.getElementById(cbID).checked = oslUserPrefs.openRoads[i][1]; } } // Force-disable Open Roads display on startup to avoid problems if the current WME viewport is // showing a more densely mapped area than before... document.getElementById('_cbOpenRoadsEnabled').checked = false; // Similarly, force-disable Gazetteer Tags display - this isn't quite as resource-sapping as // the Open Roads display could be, but it's still preferable to leave it off until the user is // ready to show the tags during this session document.getElementById('_cbGazTagsEnabled').checked = false; } function oslWriteUserPrefs() { localStorage.oslUserPrefs = JSON.stringify(oslUserPrefs); } function oslUpdateMinMax(key, value) { let found = false; for(let i = 0; i < oslUserPrefs.minmax.length; ++i) { if(oslUserPrefs.minmax[i][0] == key) { oslUserPrefs.minmax[i][1] = value; found = true; break; } } if(found == false) { oslUserPrefs.minmax.push([key, value]); } oslWriteUserPrefs(); } function oslUpdateHighlightCBs(key) { let found = false; let value = document.getElementById(key).checked; for(let i = 0; i < oslUserPrefs.openRoads.length; ++i) { if(oslUserPrefs.openRoads[i][0] == key) { oslUserPrefs.openRoads[i][1] = value; found = true; break; } } if(found == false) { oslUserPrefs.openRoads.push([key, value]); } oslWriteUserPrefs(); } function oslFinaliseSetup() { oslWazeMapElement = document.getElementById(W.map.getLayerByUniqueName('segments').div.parentElement.id); oslEnableAdvancedOptions(); oslLoadOrInitPrefs(); oslSetupBBDiv(); oslSetupORDiv(); oslSetupNamesDiv(); oslSetupUI(); oslSetupEventListeners(); oslSetupUIPosition(); oslSetLayerZIndices(); oslSetupAccessKey(); oslApplyPrefs(); oslOffsetToolbar = document.getElementById('map').contains(document.getElementById('toolbar')); window.setInterval(oslTenthSecondTick,100); oslDoneOnload = true; } function oslWaitForW() { if(document.getElementsByClassName("sandbox").length > 0) { oslAddLog('WME practice mode detected, script is disabled...'); return; } if(document.location.href.indexOf('user/') !== -1) { oslAddLog('User profile page detected, script is disabled...'); return; } if(typeof W === "undefined") { window.setTimeout(oslWaitForW, 1000); return; } if (W.userscripts?.state?.isReady) { oslFinaliseSetup(); } else { document.addEventListener("wme-ready", oslFinaliseSetup, {once: true}); } } function oslAccessKeyUpdate() { GM_setValue('_rwoAccessKey',Date.now()); } function oslSNATest() { // Checks the street name abbreviation processing code in oslWazeifyStreetName(), // to make sure it still correctly handles names of a form that are particularly // problematic, whilst also still correctly handling regular forms... const testNames = [ // Standard forms for all the defined abbreviations ['Electric Avenue', 'Electric Ave'], ['Hollywood Boulevard', 'Hollywood Blvd'], ['Ealing Broadway', 'Ealing Bdwy'], ['Hillingdon Circus', 'Hillingdon Cir'], ['Glenn Close', 'Glenn Cl'], ['Crown Court', 'Crown Ct'], ['Red Crescent', 'Red Cr'], ['Test Drive', 'Test Dr'], ['Graham Garden', 'Graham Gdn'], ['Kensington Gardens', 'Kensington Gdns'], ['Theresa Green', 'Theresa Gn'], ['Byker Grove', 'Byker Gr'], ['Lois Lane', 'Lois Ln'], ['My Cat Mews', 'My Cat Mews'], ['Tripod Mount', 'Tripod Mt'], ['Last Place', 'Last Pl'], ['Gosford Park', 'Gosford Pk'], ['Sophy Ridge', 'Sophy Rdg'], ['Take The High Road', 'Take The High Rd'], ['Four Square', 'Four Sq'], ['Baker Street', 'Baker St'], ['No Idea Terrace', 'No Idea Ter'], ['Happy Valley', 'Happy Val'], ['Hyperspace By-pass', 'Hyperspace Bypass'], ['This Is The Way', 'This Is The Way'], ['Damon Hill', 'Damon Hill'], // Forms including cardinals as prefixes ["North Street", "North St"], ["South Street", "South St"], ["East Street", "East St"], ["West Street", "West St"], ["North Town Street", "North Town St"], ["South Town Street", "South Town St"], ["East Town Street", "East Town St"], ["West Town Street", "West Town St"], ["West View Lane", "West View Ln"], // Forms including cardinals as suffixes ["Aston Boulevard West", "Aston Blvd W"], ["Breakspear Road North", "Breakspear Rd N"], ["Breakspear Road South", "Breakspear Rd S"], ["Breakspear Road East", "Breakspear Rd E"], ["Breakspear Road West", "Breakspear Rd W"], // Forms including non-cardinal suffixes ["High Road Ickenham", "High Rd Ickenham"], // Forms including "The" ["Orchard On The Green", "Orchard On The Green"], ["The Orchard On The Green", "The Orchard On The Green"], ["The Avenue", "The Avenue"], // Forms including abbreviatable words elsewhere in the name ["Green Street", "Green St"], ["Kensal Green Way", "Kensal Green Way"], ["Green Lane Hill", "Green Lane Hill"], ["Court Court", "Court Ct"], ["The Street Road", "The Street Rd"], ["Great North Road", "Great North Rd"], ["North Park Brook Road", "North Park Brook Rd"], // Forms where abbreviatable words are found at the start of longer words ["Westway Avenue", "Westway Ave"], ["Parkway Park", "Parkway Pk"], ["Parkway Crescent", "Parkway Cr"], // Test for correct abbreviation of Saint -> St. ["Saint Albans Way", "St. Albans Way"], // Random names taken from forum etc. discussions on this topic... ["Bowling Green Road", "Bowling Green Rd"], ["Crescent Road Lane", "Crescent Road Ln"], ["Edge Lane Road", "Edge Lane Rd"], ["North East Road", "North East Rd"] ]; let nTests = testNames.length; for(let i = 0; i < nTests; ++i) { let result = oslWazeifyStreetName(testNames[i][0], false); if(result !== testNames[i][1]) { console.log(oslWazeifyStreetName(testNames[i][0], true)); } } } function oslInitialise() { oslAddLog('initialise()'); oslSNATest(); // inject gazetteer data let gazscript = document.createElement("script"); gazscript.setAttribute('type','text/javascript'); gazscript.setAttribute('charset','UTF-8'); gazscript.src = oslModifySrc(oslGazetteerURL); document.head.appendChild(gazscript); oslMergeGazData = true; // initialise persistent vars sessionStorage.zoom = 0; sessionStorage.lat = ''; sessionStorage.lon = ''; sessionStorage.myCity = ''; sessionStorage.prevCity = ''; sessionStorage.cityChangeEastings = 0; sessionStorage.cityChangeNorthings = 0; sessionStorage.cityNameRB = 'optUseExisting'; sessionStorage.oslTabCreated = 0; oslWaitForW(); } // External site helper functions const hlp_ONE = { // one.network helper functions lat: 0, lng: 0, zoom: 0, checkInterval: 10000, addLog: function(logtext) { console.log('ONE: '+logtext); }, checkDatalink: function() { let lastAccessKey = GM_getValue('_oneAccessKey', null); if(lastAccessKey !== null) { let keyBits = lastAccessKey.split(','); if(keyBits.length === 4) { let keyTS = parseInt(keyBits[0]); if((Date.now() - keyTS) <= hlp_ONE.checkInterval) { // the access key was written recently enough to imply an active WME tab, so reposition if required based on the lat/lon/zoom values in the key let lat = parseFloat(keyBits[1]); let lng = parseFloat(keyBits[2]); let zoom = parseFloat(keyBits[3]); if((lat != hlp_ONE.lat) || (lng != hlp_ONE.lng) || (zoom != hlp_ONE.zoom)) { hlp_ONE.lat = lat; hlp_ONE.lng = lng; hlp_ONE.zoom = zoom; hlp_ONE.relocate(); } hlp_ONE.checkInterval = 1000; // reduce the interval after the first check, to minimise the period where manually panning/zooming is overridden } } } setTimeout(hlp_ONE.checkDatalink, 50); }, relocate: function() { Elgin.map.setCenter([hlp_ONE.lng, hlp_ONE.lat]); Elgin.map.setZoom(hlp_ONE.zoom - 1); }, clickToWME: function() { let elginLat = Elgin.map.getCenter().lat; let elginLon = Elgin.map.getCenter().lng; let elginZoom = Elgin.map.getZoom(); if(elginZoom < 12) elginZoom = 12; if(elginZoom > 22) elginZoom = 22; elginZoom += 1; // check for an active WME tab... let tabFound = false; let lastAccessKey = GM_getValue('_rwoAccessKey', null); if(lastAccessKey !== null) { lastAccessKey = parseInt(lastAccessKey); if((Date.now() - lastAccessKey) <= 10000) { // the access key was written within the last 10s which implies an active tab, so send it the repositioning data let toWrite = Date.now()+','+lastAccessKey+','+elginLat+','+elginLon+','+elginZoom; GM_setValue('_rwoPositionToWME',toWrite); tabFound = true; } } if(tabFound === false) { // couldn't find a WME tab to reposition, so open a new one... let wmeURL = 'https://www.waze.com/editor?'; wmeURL += 'lon='+elginLon; wmeURL += '&lat='+elginLat; wmeURL += '&zoomLevel='+elginZoom; window.open(wmeURL); } }, init: function() { hlp_ONE.addLog('initialise()'); hlp_ONE.addLog('waiting for map objects...'); let waitSomeMore = false; try { if(Elgin === undefined) { waitSomeMore = true; } else if (Elgin.map === undefined) { waitSomeMore = true; } } catch { waitSomeMore = true; } if(document.getElementById('map-canvas') === null) { waitSomeMore = true; } if(waitSomeMore) { window.setTimeout(hlp_ONE.init,500); return; } hlp_ONE.addLog('all required objects found...'); // Add "open in WME" button... let tBtnDiv = document.createElement('div'); tBtnDiv.id = 'rwoWMELink'; let tHTML = '<div class="gmnoprint" draggable="false" controlwidth="40" controlheight="40" style="margin: 10px; user-select: none; position: absolute; bottom: 200px; right: 40px;">'; tHTML += '<div class="gmnoprint" controlwidth="40" controlheight="40" style="position: absolute; left: 0px; top: 0px;">'; tHTML += '<div draggable="false" style="user-select: none; box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px; border-radius: 2px; cursor: pointer; background-color: rgb(255, 255, 128); width: 40px; height: 40px;">'; tHTML += '<button draggable="false" title="Open in WME" aria-label="Open in WME" type="button" class="gm-control-active" style="background: none; display: block; border: 0px; margin: 0px; padding: 0px; position: relative; cursor: pointer; user-select: none; overflow: hidden; width: 40px; height: 40px; top: 0px; left: 0px;"><strong>WME</strong></button>'; tHTML += '</div></div></div>'; tBtnDiv.innerHTML = tHTML; document.getElementById('map-canvas').firstChild.appendChild(tBtnDiv); document.getElementById('rwoWMELink').addEventListener('click', hlp_ONE.clickToWME, false); setTimeout(hlp_ONE.checkDatalink, 500); } }; const hlp_LRR = { // London Roadworks Register helper functions lat: 0, lng: 0, zoom: 0, checkInterval: 10000, wmePresent: false, addLog: function(logtext) { console.log('LRR: '+logtext); }, checkDatalink: function() { let lastAccessKey = GM_getValue('_lrrAccessKey', null); if(lastAccessKey !== null) { let keyBits = lastAccessKey.split(','); if(keyBits.length === 4) { let keyTS = parseInt(keyBits[0]); if((Date.now() - keyTS) <= hlp_LRR.checkInterval) { if(hlp_LRR.wmePresent === false) { hlp_LRR.wmePresent = true; document.querySelector('#messageContainer').style.display = "none"; } // the access key was written recently enough to imply an active WME tab, so reposition if required based on the lat/lon/zoom values in the key let lat = parseFloat(keyBits[1]); let lng = parseFloat(keyBits[2]); let zoom = parseFloat(keyBits[3]); if((lat != hlp_LRR.lat) || (lng != hlp_LRR.lng) || (zoom != hlp_LRR.zoom)) { hlp_LRR.lat = lat; hlp_LRR.lng = lng; hlp_LRR.zoom = zoom; hlp_LRR.relocate(); } hlp_LRR.checkInterval = 1000; // reduce the interval after the first check, to minimise the period where manually panning/zooming is overridden } } } setTimeout(hlp_LRR.checkDatalink, 50); }, relocate: function() { let waitSomeMore = false; if(map === undefined) { waitSomeMore = true; } if(OpenLayers === undefined) { waitSomeMore = true; } if(waitSomeMore) { return; } let tz = hlp_LRR.zoom - 12; if(tz > 5) { tz = 5; } map.panTo(new OpenLayers.LonLat(hlp_LRR.lng,hlp_LRR.lat)); map.zoomTo(tz); }, init: function() { // trap the page reload that occurs when the map view is generated... if(document.location.href.indexOf('home') == -1) { hlp_LRR.addLog('page reloaded during map generation...'); setTimeout(hlp_LRR.checkDatalink, 500); return; } hlp_LRR.addLog('initialise()'); hlp_LRR.addLog('waiting for search page to load...'); var waitSomeMore = false; if(document.getElementsByName('mapResults')[0].length < 1) { waitSomeMore = true; } if(document.getElementsByName('postCode')[0].length < 1) { waitSomeMore = true; } if(waitSomeMore === true) { window.setTimeout(hlp_LRR.init,500); return; } hlp_LRR.addLog('accessing map...'); // first set a "safe" postcode as the search criteria - whilst asking for the map view to be generated without any // search terms usually works OK, from time to time the site throws a wobbler and refuses to do anything without // having the search narrowed down a bit... document.getElementsByName('postCode')[0].value="EC1A 1AA"; document.getElementsByName('mapResults')[0].click(); setTimeout(hlp_LRR.checkDatalink, 500); } }; const hlp_ODM = { // OS OpenData map helper functions lat: 0, lng: 0, zoom: 0, checkInterval: 10000, wmePresent: false, lastAccessKey: null, addLog: function(logtext) { console.log('ODM: '+logtext); }, checkDatalink: function() { let thisAccessKey = GM_getValue('_osodAccessKey', null); if((thisAccessKey !== null) && (thisAccessKey !== hlp_ODM.lastAccessKey)) { hlp_ODM.lastAccessKey = thisAccessKey; let keyBits = thisAccessKey.split(','); if(keyBits.length === 4) { let keyTS = parseInt(keyBits[0]); let keyInterval = Date.now() - keyTS; if(keyInterval <= hlp_ODM.checkInterval) { if(hlp_ODM.wmePresent === false) { hlp_ODM.addLog("WME detected"); hlp_ODM.wmePresent = true; hlp_ODM.fakeOnload(); } // the access key was written recently enough to imply an active WME tab, so reposition if required based on the lat/lon/zoom values in the key let lat = parseFloat(keyBits[1]); let lng = parseFloat(keyBits[2]); let zoom = parseFloat(keyBits[3]); if((lat != hlp_ODM.lat) || (lng != hlp_ODM.lng) || (zoom != hlp_ODM.zoom)) { hlp_ODM.lat = lat; hlp_ODM.lng = lng; hlp_ODM.zoom = zoom; hlp_ODM.relocate(); } hlp_ODM.checkInterval = 1000; // reduce the interval after the first check, to minimise the period where manually panning/zooming is overridden } } } setTimeout(hlp_ODM.checkDatalink, 50); }, hideElement: function(elm) { let i = document.querySelectorAll(elm).length; while(i > 0) { document.querySelectorAll(elm)[--i].style.display="none"; } }, fakeOnload: function() { // maximise the map view hlp_ODM.hideElement('.govuk-grid-row'); hlp_ODM.hideElement('.govuk-phase-banner'); hlp_ODM.hideElement('.before-content-links'); hlp_ODM.hideElement('.govuk-heading-l'); hlp_ODM.hideElement('.govuk-header '); hlp_ODM.hideElement('.govuk-footer'); hlp_ODM.hideElement('.govuk-list'); hlp_ODM.hideElement('.govuk-body'); hlp_ODM.hideElement('.govuk-heading-m'); hlp_ODM.hideElement('#gcd-container'); document.querySelector('.govuk-main-wrapper ').style.padding="0px 0px 0px 0px"; let i = document.querySelectorAll('.govuk-width-container ').length; while(i > 0) { document.querySelectorAll('.govuk-width-container ')[--i].style.maxWidth="100%"; } document.querySelector('#map').style.position="absolute"; document.querySelector('#map').style.height="100%"; }, relocate: function() { map.getView().setCenter([hlp_ODM.lng, hlp_ODM.lat]); map.getView().setZoom(hlp_ODM.zoom); }, init: function() { hlp_ODM.addLog('initialise()'); if(map === undefined) window.setTimeout(hlp_ODM.init,100); else window.setTimeout(hlp_ODM.checkDatalink, 500); } }; oslBootstrap();