// ==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://labs.os.uk/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant unsafeWindow
// @version 3.31
// ==/UserScript==
// Contains Ordnance Survey data Crown copyright and database right 2023
//
// 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
=======================================================================================================================
Only suggests Waze place names that are within 10km of current map view
Reliability improvement for street name application
Ignores address fields in other parts of the UI
Improved accuracy of gazetteer comparisons
=======================================================================================================================
Bug fixes - MUST BE CLEARED BEFORE RELEASE
=======================================================================================================================
=======================================================================================================================
Things to be checked
=======================================================================================================================
*/
/* JSHint Directives */
/* globals W: true */
/* globals OpenLayers: true */
/* globals Elgin: true */
/* globals google: 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 bitwise: false */
/* jshint evil: true */
/* jshint esversion: 11 */
const oslVersion = '3.31';
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 == 'labs.os.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);
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// 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);oslOSCityNames
}
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
{
console.debug("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 == '')
{
console.debug(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 &&= (ui.querySelector('.street-name-row') !== null);
retval &&= (ui.querySelector('.city-name') !== null);
retval &&= (ui.querySelector('.state-id') !== null);
retval &&= (ui.querySelector('.country-id') !== null);
retval &&= (ui.querySelector('.action-buttons') !== null);
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 = W.map.getExtent();
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.segmentLayer.div).top;
let divLeft = getComputedStyle(W.map.segmentLayer.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.segmentLayer.features.length; slIdx++)
{
if(W.map.segmentLayer.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 = null;
if((W.model.countries.top === undefined)||(W.model.countries.top === null))
{
if((W.model.countries.additionalInfo !== null) && (W.model.countries.additionalInfo !== undefined))
{
country = W.model.countries.additionalInfo[0].name;
}
}
else
{
country = W.model.countries.top.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)
{
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(4);
lon = gomc.lon.toFixed(4);
}
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)
{
// recalculate the map viewport extents in terms of oslEastings/oslNorthings
let vpHalfWidth = (W.map.getExtent().right-W.map.getExtent().left) / (2 * 1.61);
let vpHalfHeight = (W.map.getExtent().top-W.map.getExtent().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.segmentLayer.div).top;
oslBBDiv.style.left = getComputedStyle(W.map.segmentLayer.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.segmentLayer.div).top;
oslSegGeoDiv.style.left = getComputedStyle(W.map.segmentLayer.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.segmentLayer.div).top;
oslNamesDiv.style.left = getComputedStyle(W.map.segmentLayer.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://labs.os.uk/prototyping/osel-framework/template/openlayers.html" 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.segmentLayer.events.register("featuresadded", null, oslNameCheckTrigger);
W.map.segmentLayer.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.segmentLayer.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"]
];
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 - 1;
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);
},
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;
}
if(google === undefined)
{
waitSomeMore = true;
}
else if(google.maps === 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,
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)
{
// 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 - 12;
if(hlp_LRR.zoom > 5)
{
hlp_LRR.zoom = 5;
}
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, 500);
},
relocate: function()
{
let waitSomeMore = false;
if(map === undefined)
{
waitSomeMore = true;
}
if(OpenLayers === undefined)
{
waitSomeMore = true;
}
if(waitSomeMore)
{
return;
}
map.setCenter(new OpenLayers.LonLat(hlp_LRR.lng,hlp_LRR.lat));
map.zoomTo(hlp_LRR.zoom);
},
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,
addLog: function(logtext)
{
console.log('ODM: '+logtext);
},
checkDatalink: function()
{
let lastAccessKey = GM_getValue('_osodAccessKey', null);
if(lastAccessKey !== null)
{
let keyBits = lastAccessKey.split(',');
if(keyBits.length === 4)
{
let keyTS = parseInt(keyBits[0]);
if((Date.now() - keyTS) <= hlp_ODM.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_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, 500);
},
fakeOnload: function()
{
// maximise the map view
document.getElementsByClassName('content')[0].style.top='0px';
document.getElementsByClassName('content')[0].style.height='100%';
map.updateSize();
document.getElementsByClassName('osel-sliding-side-panel panel-left')[0].style.display='none';
document.getElementsByClassName('header')[0].style.display='none';
if(document.getElementsByClassName('ol-layer').length > 1)
{
document.getElementsByClassName('ol-layer')[1].style.display='none';
}
document.getElementsByClassName('osel-control-container container-bottom container-right')[0].style.display='none';
document.getElementsByClassName('layer-element')[0].children[0].click();
// set the desired map style...
document.getElementsByClassName('datahub-os-maps road')[0].click();
setTimeout(hlp_ODM.checkDatalink, 500);
},
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 hlp_ODM.fakeOnload();
}
};
oslBootstrap();