1var viewer; // Cesium Viewer 2var handler; // Cesium Handler 3var mapMode = 2; // 1 = 2D, 2 = 3D 4var terrainMode = 1; // 1 = flat, 2 = hills 5var terrainProvider; 6var mapMarkers = []; 7var mapMarkers2 = []; 8var mapLines = []; 9var mapLines2 = []; 10var mapCircles = []; 11var mode = 0; 12var dispersionMode = 0; 13var initName = ""; 14var initHole = 1; 15var distLat1 = 0; 16var distLon1 = 0; 17var distLat2 = 0; 18var distLon2 = 0; 19var elevationReqTimestamp = []; 20var elevationLat = []; 21var elevationLon = []; 22var elevation = []; 23var elevation2Lat = []; 24var elevation2Lon = []; 25var elevation2 = []; 26var elevationTimestamp = 0; 27var viewTimestamp = 0; 28var teeGridCount = 0; 29var greenGridCount = 0; 30var greenSlopesCount = 0; 31var greenSlopesBearing = 0; 32var greenContoursCount = 0; 33var distCount = 0; 34var coneCount = 0; 35var bestClubID = []; 36 37async function bodyOnLoadHandler() 38{ 39 if (!PV.auth.cookieConsentIsValid()) { window.location.href = "support/privacypolicy.php"; return; } 40 if (!PV.auth.localStorageConsentIsValid()) { window.location.href = "support/privacypolicy.php"; return; } 41 if (!PV.auth.termsAgreementIsValid()) { window.location.href = "support/terms.php"; return; } 42 initProfile(); 43 initCourseData(0); 44 populateCourseSelect(); 45 initSelectedUnits(); 46 initTeeCircles(); 47 initLineColor(); 48 initLineBrightness(); 49 initCircleColor(); 50 initCircleBrightness(); 51 initAutoRotate(); 52 initCompass(); 53 initDispersion(); 54 initCones(); 55 processLinkParameters(); 56 document.getElementById("saveasinput").value = initName; 57 document.getElementById("layerselect").selectedIndex = 0; 58 document.getElementById("terrainselect").selectedIndex = 0; 59 document.getElementById("geheightselect").selectedIndex = 0; 60 document.getElementById("greenspeedselect").selectedIndex = 4; 61 document.getElementById("greenslopecolorselect").selectedIndex = 0; 62 document.getElementById("greenslopesizeselect").selectedIndex = 1; 63 document.getElementById("greenslopebrightnessselect").selectedIndex = 4; 64 document.getElementById("greencontourselect").selectedIndex = 1; 65 document.getElementById("greencontourminselect").selectedIndex = 0; 66 document.getElementById("greencontourbrightnessselect").selectedIndex = 4; 67 document.getElementById("puttingspeedselect").selectedIndex = 0; 68 setSelectedHole(initHole); 69 var p = navigator.platform; 70 if (p == "iPad" || p == "iPhone") 71 { 72 document.getElementById('maptd').style.width = '65%'; 73 document.getElementById('buttontd').style.width = '25%'; 74 } 75 bodyOnResizeHandler(); 76 var imageryProvider = await PV.auth.newCesiumBingMapsImageryProviderSatellite(); 77 var baseLayer = new Cesium.ImageryLayer(imageryProvider); 78 terrainProvider = new Cesium.EllipsoidTerrainProvider(); 79 viewer = new Cesium.Viewer( 80 'mapdiv', 81 { 82 baseLayer : baseLayer, 83 terrainProvider : terrainProvider, 84 animation : false, 85 baseLayerPicker : false, 86 fullscreenButton : false, 87 vrButton : false, 88 geocoder : false, 89 homeButton : false, 90 infoBox : true, 91 sceneModePicker : false, 92 selectionIndicator : false, 93 timeline : false, 94 useDefaultRenderLoop : true 95 }); 96 viewer.scene.globe.maximumScreenSpaceError = 2.0; 97 viewer.scene.globe.showGroundAtmosphere = false; 98 viewer.scene.screenSpaceCameraController._zoomFactor = 50/minWheel; 99 viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); 100 viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); 101 viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK); 102 handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); 103 handler.setInputAction(mapLeftClickHandler, Cesium.ScreenSpaceEventType.LEFT_CLICK); 104 handler.setInputAction(mapRightClickHandler, Cesium.ScreenSpaceEventType.RIGHT_CLICK); 105 handler.setInputAction(mapMouseMoveHandler, Cesium.ScreenSpaceEventType.MOUSE_MOVE); 106 handler.setInputAction(mapWheelHandler, Cesium.ScreenSpaceEventType.WHEEL); 107 if (mapMode == 2 && getCourseHoles(currentCourseID()) < 1) toggle3D(); 108 zoomToCourseHole(0, initHole, true, true, 0); 109 window.setTimeout(compassRenderLoop, 1000); 110 window.setTimeout(refreshElevation, 3000); 111 window.setTimeout(viewerRenderLoop, 5000); 112 window.setTimeout(satelliteProviderLoop1, 5000); 113 startHeartbeat("3dplanner", 60); 114} 115 116var minWheel = 1000000; 117function mapWheelHandler(wheel) 118{ 119 var w = Math.abs(wheel); 120 if (w < 1.0) w = 1.0; 121 if (w < minWheel)// || w > 100*minWheel) 122 { 123 minWheel = w; 124 viewer.scene.screenSpaceCameraController._zoomFactor = 50/minWheel; 125 } 126} 127 128var fastRenderStopTime = 0; 129function fastRender(seconds) 130{ 131 var t = Date.now() + Math.round(1000*seconds) + 500; 132 if (fastRenderStopTime < t) 133 fastRenderStopTime = t; 134} 135 136var showTileCount = false;
137function viewerRenderLoop() 138{ 139 viewer.useDefaultRenderLoop = false; 140 viewer.resize(); 141 viewer.render(); 142 if (showTileCount) 143 { 144 var s = "Bing Tile Count = "+PV.auth.BingTileCount.toString()+", unique = "+PV.auth.BingTileCounts.size.toString(); 145 s += "<br/>Esri Tile Count = "+PV.auth.EsriTileCount.toString()+", unique = "+PV.auth.EsriTileCounts.size.toString(); 146 s += "<br/>NASA Tile Count = "+PV.auth.NASATileCount.toString()+", unique = "+PV.auth.NASATileCounts.size.toString(); 147 s += "<br/>OSM Tile Count = "+PV.auth.OSMTileCount.toString()+", unique = "+PV.auth.OSMTileCounts.size.toString(); 148 document.getElementById('coordsdiv').innerHTML = s; 149 } 150 var renderDelay = 50; 151 if (Date.now() < fastRenderStopTime) 152 { 153 viewer.scene.screenSpaceCameraController._zoomFactor = 20; 154 renderDelay = 20; 155 } 156 else 157 { 158 viewer.scene.screenSpaceCameraController._zoomFactor = 50; 159 } 160 window.setTimeout(viewerRenderLoop, renderDelay); 161} 162 163function flyToHeightAboveGround(lat, lon, hlat, hlon, heightAboveGround, heading, angle, durationSeconds) 164{ 165 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(hlat, hlon)); 166 flyToHeightAboveEllipsoid(lat, lon, h + heightAboveGround, heading, angle, durationSeconds); 167} 168 169function flyToHeightAboveEllipsoid(lat, lon, heightAboveEllipsoid, heading, angle, durationSeconds) 170{ 171 fastRender(durationSeconds); 172 viewer.camera.flyTo({ 173 destination : Cesium.Cartesian3.fromDegrees(lon, lat, heightAboveEllipsoid), 174 orientation : { 175 heading : heading * Math.PI / 180, 176 pitch : angle * Math.PI / 180, 177 roll : 0 178 }, 179 duration : durationSeconds 180 }); 181} 182 183var compassCanvas = null; 184var compassCtx = null; 185var weatherCanvas = null; 186var weatherCtx = null; 187 188function initCompass() 189{ 190 compassCanvas = document.getElementById("compasscanvas"); 191 compassCtx = compassCanvas.getContext("2d"); 192 weatherCanvas = document.getElementById("weathercanvas"); 193 weatherCtx = weatherCanvas.getContext("2d"); 194} 195 196function compassRenderLoop() 197{ 198 var cameraHeading = viewer.camera.heading; 199 var theta = -cameraHeading; 200 var c = Math.cos(theta); 201 var s = Math.sin(theta); 202 var x = []; var y = []; 203 // Cross 204 x[0] = -6; y[0] = 0; 205 x[1] = 6; y[1] = 0; 206 x[2] = 0; y[2] = -6; 207 x[3] = 0; y[3] = 6; 208 // North 209 x[4] = -3; y[4] = 8; 210 x[5] = -3; y[5] = 17; 211 x[6] = 3; y[6] = 9; 212 x[7] = 3; y[7] = 18; 213 // South 214 x[8] = -3; y[8] = -16; 215 x[9] = -3; y[9] = -17; 216 x[10] = 2; y[10] = -17; 217 x[11] = 3; y[11] = -17; 218 x[12] = 3; y[12] = -13; 219 x[13] = 2; y[13] = -13; 220 x[14] = -2; y[14] = -13; 221 x[15] = -3; y[15] = -13; 222 x[16] = -3; y[16] = -9; 223 x[17] = -2; y[17] = -9; 224 x[18] = 3; y[18] = -9; 225 x[19] = 3; y[19] = -10; 226 // East 227 x[20] = 16; y[20] = -4; 228 x[21] = 10; y[21] = -4; 229 x[22] = 10; y[22] = 4; 230 x[23] = 16; y[23] = 4; 231 x[24] = 10; y[24] = 0; 232 x[25] = 16; y[25] = 0; 233 // West 234 x[26] = -16; y[26] = 4; 235 x[27] = -15; y[27] = -4; 236 x[28] = -13; y[28] = 0; 237 x[29] = -11; y[29] = -4; 238 x[30] = -10; y[30] = 4; 239 var x2 = []; var y2 = []; 240 for (var i = 0; i <= 30; ++i) 241 { 242 x2[i] = 20 + 0.9*(c*x[i] + s*y[i]); 243 y2[i] = 20 + 0.9*(s*x[i] - c*y[i]); 244 } 245 compassCtx.clearRect(0,0,40,40); 246 compassCtx.lineWidth = 1; 247 compassCtx.strokeStyle = "#FF0000"; 248 compassCtx.beginPath(); 249 // North 250 compassCtx.moveTo(x2[4],y2[4]); 251 compassCtx.lineTo(x2[5],y2[5]); 252 compassCtx.lineTo(x2[6],y2[6]); 253 compassCtx.lineTo(x2[7],y2[7]); 254 compassCtx.stroke(); 255 compassCtx.strokeStyle = "#888888"; 256 compassCtx.beginPath(); 257 // Cross 258 compassCtx.moveTo(x2[0],y2[0]); 259 compassCtx.lineTo(x2[1],y2[1]); 260 compassCtx.moveTo(x2[2],y2[2]); 261 compassCtx.lineTo(x2[3],y2[3]); 262 // South 263 compassCtx.moveTo(x2[8],y2[8]); 264 compassCtx.lineTo(x2[9],y2[9]); 265 compassCtx.lineTo(x2[10],y2[10]); 266 compassCtx.lineTo(x2[11],y2[11]); 267 compassCtx.lineTo(x2[12],y2[12]); 268 compassCtx.lineTo(x2[13],y2[13]); 269 compassCtx.lineTo(x2[14],y2[14]); 270 compassCtx.lineTo(x2[15],y2[15]); 271 compassCtx.lineTo(x2[16],y2[16]); 272 compassCtx.lineTo(x2[17],y2[17]); 273 compassCtx.lineTo(x2[18],y2[18]); 274 compassCtx.lineTo(x2[19],y2[19]); 275 // East 276 compassCtx.moveTo(x2[20],y2[20]); 277 compassCtx.lineTo(x2[21],y2[21]); 278 compassCtx.lineTo(x2[22],y2[22]); 279 compassCtx.lineTo(x2[23],y2[23]); 280 compassCtx.moveTo(x2[24],y2[24]);
281 compassCtx.lineTo(x2[25],y2[25]); 282 // West 283 compassCtx.moveTo(x2[26],y2[26]); 284 compassCtx.lineTo(x2[27],y2[27]); 285 compassCtx.lineTo(x2[28],y2[28]); 286 compassCtx.lineTo(x2[29],y2[29]); 287 compassCtx.lineTo(x2[30],y2[30]); 288 compassCtx.stroke(); 289 if (weatherIndex >= 0) 290 renderWind(cameraHeading); 291 var renderDelay = 500; 292 if (Date.now() < fastRenderStopTime) 293 renderDelay = 100; 294 window.setTimeout(compassRenderLoop, renderDelay); 295} 296 297function renderWind(cameraHeading) 298{ 299 var theta = weatherWindBearing*(Math.PI/180.0) - cameraHeading; 300 var k = (weatherWindSpeed > 0 ? 1.4 : 0.0); 301 var c = Math.cos(theta); 302 var s = Math.sin(theta); 303 var x = []; var y = []; 304 x[0] = 0; y[0] = 10; 305 x[1] = -3; y[1] = 6; 306 x[2] = 3; y[2] = 6; 307 x[3] = 0; y[3] = -10; 308 var x2 = []; var y2 = []; 309 for (var i = 0; i <= 3; ++i) 310 { 311 x2[i] = 20 + k*(c*x[i] + s*y[i]); 312 y2[i] = 20 + k*(s*x[i] - c*y[i]); 313 } 314 weatherCtx.clearRect(0,0,40,40); 315 weatherCtx.lineWidth = 1; 316 weatherCtx.strokeStyle = "#888888"; 317 weatherCtx.beginPath(); 318 weatherCtx.moveTo(x2[0],y2[0]); 319 weatherCtx.lineTo(x2[1],y2[1]); 320 weatherCtx.moveTo(x2[0],y2[0]); 321 weatherCtx.lineTo(x2[2],y2[2]); 322 weatherCtx.moveTo(x2[0],y2[0]); 323 weatherCtx.lineTo(x2[3],y2[3]); 324 weatherCtx.stroke(); 325} 326 327function bodyOnResizeHandler() 328{ 329 var x = document.getElementById("mapdiv"); 330 x.style.height = Math.round(window.innerHeight - 10).toString() + 'px'; 331} 332 333function layerHelp() 334{ 335 alert("Use this drop-down to select the imagery to display. The 'Terrain' overlay is based on the SRTM 1 Arc-Second Global Dataset between 60-degrees north and 56-degrees south, and the ASTER GDEM V3 Dataset above 60-degrees north and below 56-degrees south. Note that the bright lines on the Terrain overlay are 10-metre contours. The 'Lidar' overlay is currently only available in the United States and England. Note that the Lidar imagery is generated on-demand and typically takes around 10-20 seconds to load. If it has not loaded after 30 seconds, it probably means it is not available in your region."); 336} 337 338async function layerSelectHandler() 339{ 340 setMode(0); 341 var i = document.getElementById("layerselect").selectedIndex; 342 var layers = viewer.scene.imageryLayers; 343 while (layers.length > 1) layers.remove(layers.get(layers.length - 1), true); 344 satelliteProvider = 0; 345 if (i == 1) 346 { 347 terrainModel = 1; 348 var s = document.getElementById('saveasinput').value; 349 if (s != null && s.indexOf('!!dsm') == 0) { terrainModel = 2; } 350 var courseID = currentCourseID(); 351 var courseName = getCourseNameFromID(courseID); 352 if (courseName == "") courseName = cleanString(document.getElementById("saveasinput").value); 353 var minLat = getCourseMinLat(courseID); 354 var minLon = getCourseMinLon(courseID); 355 var maxLat = getCourseMaxLat(courseID); 356 var maxLon = getCourseMaxLon(courseID); 357 getProVisualizerTerrainData1(minLat, minLon, maxLat, maxLon, 200, viewer, courseName); 358 } 359 else if (i == 2) 360 { 361 var courseID = currentCourseID(); 362 var minLat1 = getCourseMinLat(courseID); 363 var minLon1 = getCourseMinLon(courseID); 364 var maxLat1 = getCourseMaxLat(courseID); 365 var maxLon1 = getCourseMaxLon(courseID); 366 var minLat = Math.round(moveLat(minLat1, minLon1, 200, 180)*10000)/10000; 367 var minLon = Math.round(moveLon(minLat1, minLon1, 200, 270)*10000)/10000; 368 var maxLat = Math.round(moveLat(maxLat1, maxLon1, 200, 0)*10000)/10000; 369 var maxLon = Math.round(moveLon(maxLat1, maxLon1, 200, 90)*10000)/10000; 370 var azimuth = 0.0; 371 var altitude = 40.0; 372 var zFactor = 1.0; 373 var degToRad = Math.PI / 180.0; 374 var dx = 2*geogDist(minLat, minLon, minLat, maxLon)/Math.cos(((minLat+maxLat)/2)*degToRad); 375 var dy = 2*geogDist(minLat, minLon, maxLat, minLon); 376 var k = Math.sqrt(1+dx*dy)/3500.0; 377 if (k > 1.0) { dx /= k; dy /= k; } 378 var s = document.getElementById('saveasinput').value; 379 if (s == null) s = ""; 380 if (s.indexOf("!!dsmEN") == 0) 381 { 382 // England DSM 383 var url = 'https://environment.data.gov.uk/geoservices/datasets/df4e3ec3-315e-48aa-aaaf-b5ae74d7b2bb/wms?'; 384 url += 'VERSION=1.3.0'; 385 url += '&SERVICE=WMS'; 386 url += '&REQUEST=GetMap'; 387 url += '&LAYERS=Lidar_Composite_Hillshade_FZ_DSM_1m'; 388 url += '&STYLES=hillshade'; 389 url += '&CRS=CRS%3A84'; 390 url += '&BBOX='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString(); 391 url += '&WIDTH='+Math.round(dx).toString(); 392 url += '&HEIGHT='+Math.round(dy).toString(); 393 url += '&FORMAT=image%2Fpng'; 394 url += '&TRANSPARENT=true'; 395 url += '&BGCOLOR=0x000000'; 396 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 397 { 398 url : url, 399 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 400 })); 401 } 402 else if (s.indexOf("!!dsmWA") == 0) 403 { 404 // Wales DSM 405 var url = 'https://datamap.gov.wales/geoserver/ows?'; 406 url += 'VERSION=1.3.0'; 407 url += '&SERVICE=WMS'; 408 url += '&REQUEST=GetMap'; 409 url += '&LAYERS=geonode%3Awales_lidar_dsm_1m_hillshade_cog'; 410 url += '&STYLES='; 411 url += '&CRS=CRS%3A84'; 412 url += '&BBOX='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString(); 413 url += '&WIDTH='+Math.round(dx).toString(); 414 url += '&HEIGHT='+Math.round(dy).toString(); 415 url += '&FORMAT=image%2Fpng'; 416 url += '&TILED=true';
417 url += '&TRANSPARENT=true'; 418 url += '&BGCOLOR=0x000000'; 419 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 420 { 421 url : url, 422 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 423 })); 424 } 425 else if (s.indexOf("!!dtmSC") == 0) 426 { 427 // Scotland DTM 428 var phase = "1"; 429 if (s.indexOf("!!dtmSC2") == 0) phase = "2"; 430 if (s.indexOf("!!dtmSC3") == 0) phase = "3"; 431 if (s.indexOf("!!dtmSC4") == 0) phase = "4"; 432 if (s.indexOf("!!dtmSC5") == 0) phase = "5"; 433 if (s.indexOf("!!dtmSC6") == 0) phase = "6"; 434 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 435 { 436 url : 'https://srsp-ows.jncc.gov.uk/ows?service=WMS&request=GetMap&layers=scotland%3Ascotland-lidar-'+phase+'-dtm&styles=scotland%3Alidar-dem&format=image%2Fpng&transparent=true&version=1.1.1&tiled=true&bbox='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString()+'&srs=EPSG%3A4326&width='+Math.round(dx).toString()+'&height='+Math.round(dy).toString(), 437 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 438 })); 439 } 440 else if (s.indexOf("!!dsmSC") == 0) 441 { 442 // Scotland DSM 443 var phase = "1"; 444 if (s.indexOf("!!dsmSC2") == 0) phase = "2"; 445 if (s.indexOf("!!dsmSC3") == 0) phase = "3"; 446 if (s.indexOf("!!dsmSC4") == 0) phase = "4"; 447 if (s.indexOf("!!dsmSC5") == 0) phase = "5"; 448 if (s.indexOf("!!dsmSC6") == 0) phase = "6"; 449 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 450 { 451 url : 'https://srsp-ows.jncc.gov.uk/ows?service=WMS&request=GetMap&layers=scotland%3Ascotland-lidar-'+phase+'-dsm&styles=scotland%3Alidar-dem&format=image%2Fpng&transparent=true&version=1.1.1&tiled=true&bbox='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString()+'&srs=EPSG%3A4326&width='+Math.round(dx).toString()+'&height='+Math.round(dy).toString(), 452 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 453 })); 454 } 455 else if (minLat > 49 && maxLat < 56 && minLon > -8 && maxLon < 3 && dx < 10000 && dy < 10000) 456 { 457 // England DTM 458 var url = 'https://environment.data.gov.uk/geoservices/datasets/13787b9a-26a4-4775-8523-806d13af58fc/wms?'; 459 url += 'VERSION=1.3.0'; 460 url += '&SERVICE=WMS'; 461 url += '&REQUEST=GetMap'; 462 url += '&LAYERS=Lidar_Composite_Hillshade_DTM_1m'; 463 url += '&STYLES=hillshade'; 464 url += '&CRS=CRS%3A84'; 465 url += '&BBOX='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString(); 466 url += '&WIDTH='+Math.round(dx).toString(); 467 url += '&HEIGHT='+Math.round(dy).toString(); 468 url += '&FORMAT=image%2Fpng'; 469 url += '&TRANSPARENT=true'; 470 url += '&BGCOLOR=0x000000'; 471 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 472 { 473 url : url, 474 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 475 })); 476 } 477 else if (minLat > 0 && maxLat < 90 && minLon > -180 && maxLon < -20 && dx < 10000 && dy < 10000) 478 { 479 // United Statesa DTM 480 var url = 'https://elevation.nationalmap.gov/arcgis/rest/services/3DEPElevation/ImageServer/exportImage?'; 481 url += 'f=image'; 482 url += '&bandIds='; 483 url += '&renderingRule=' + encodeURIComponent('%7B"rasterFunction"%3A"Hillshade","rasterFunctionArguments"%3A%7B"Azimuth"%3A'+azimuth.toString()+',"Altitude"%3A'+altitude.toString()+',"ZFactor"%3A'+zFactor.toString()+'%7D,"variableName"%3A"DEM"%7D'); 484 url += '&bbox='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString(); 485 url += '&imageSR=4326&bboxSR=4326&size='+Math.round(dx).toString()+'%2C'+Math.round(dy).toString(); 486 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 487 { 488 url : url, 489 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 490 })); 491 } 492 } 493 else if (i == 3) 494 { 495 var s = document.getElementById('saveasinput').value; 496 if (s != null && s.indexOf('!!esri12') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(12)); return; } 497 if (s != null && s.indexOf('!!esri13') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(13)); return; } 498 if (s != null && s.indexOf('!!esri14') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(14)); return; } 499 if (s != null && s.indexOf('!!esri15') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(15)); return; } 500 if (s != null && s.indexOf('!!esri16') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(16)); return; } 501 if (s != null && s.indexOf('!!esri17') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(17)); return; } 502 if (s != null && s.indexOf('!!esri18') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(18)); return; } 503 if (s != null && s.indexOf('!!esri') == 0) { layers.addImageryProvider(await PV.auth.newCesiumEsriImageryProviderSatellite(19)); return; } 504 if (s != null && s.indexOf('!!nasa+bm+osm') == 0) 505 { 506 layers.addImageryProvider(newNasaImageryProviderTerrain()); 507 var bmLayer = layers.addImageryProvider(PV.auth.newCesiumSingleTileImageryProviderNASABlueMarble()); bmLayer.alpha = 0.5; 508 var osmLayer = layers.addImageryProvider(await PV.auth.newCesiumOSMImageryProviderMap(18)); osmLayer.alpha = 0.65; 509 return; 510 } 511 if (s != null && s.indexOf('!!nasa+osm') == 0) 512 { 513 layers.addImageryProvider(newNasaImageryProviderTerrain()); 514 var osmLayer = layers.addImageryProvider(await PV.auth.newCesiumOSMImageryProviderMap(18)); osmLayer.alpha = 0.65; 515 return; 516 } 517 if (s != null && s.indexOf('!!bm+osm') == 0) 518 { 519 layers.addImageryProvider(PV.auth.newCesiumSingleTileImageryProviderNASABlueMarble()); 520 var osmLayer = layers.addImageryProvider(await PV.auth.newCesiumOSMImageryProviderMap(18)); osmLayer.alpha = 0.65; 521 return; 522 } 523 if (s != null && s.indexOf('!!nasa') == 0) { layers.addImageryProvider(newNasaImageryProviderTerrain()); return; } 524 if (s != null && s.indexOf('!!bm') == 0) { layers.addImageryProvider(PV.auth.newCesiumSingleTileImageryProviderNASABlueMarble()); return; } 525 if (s != null && s.indexOf('!!osm') == 0) 526 { 527 var zMax = 18; 528 if (s.length > 5 && !isNaN(parseInt(s.substring(5)))) zMax = parseInt(s.substring(5)); 529 if (zMax < 0) zMax = 0; 530 if (zMax > 20) zMax = 20; 531 PV.auth.clearOSMTileRanges(); 532 var courseID = currentCourseID(); 533 for (var hole = 1; hole <= 18; ++hole) 534 { 535 for (var j = 1; j <= 5; ++j) 536 { 537 var lat = 0; 538 var lon = 0; 539 if (j == 1) { lat = getCourseHoleTeeLat(courseID, hole); lon = getCourseHoleTeeLon(courseID, hole); } 540 if (j == 2) { lat = getCourseHolePinLat(courseID, hole); lon = getCourseHolePinLon(courseID, hole); } 541 if (j == 3) { lat = getCourseHoleTargetLat(courseID, hole, 1); lon = getCourseHoleTargetLon(courseID, hole, 1); } 542 if (j == 4) { lat = getCourseHoleTargetLat(courseID, hole, 2); lon = getCourseHoleTargetLon(courseID, hole, 2); } 543 if (j == 5) { lat = getCourseHoleTargetLat(courseID, hole, 3); lon = getCourseHoleTargetLon(courseID, hole, 3); } 544 if (lat != 0 || lon != 0) 545 { 546 var buffer = 200; 547 minLat = moveLat(lat, lon, buffer, 180); 548 minLon = moveLon(lat, lon, buffer, 270); 549 maxLat = moveLat(lat, lon, buffer, 0); 550 maxLon = moveLon(lat, lon, buffer, 90); 551 PV.auth.addOSMTileRange(zMax, minLat, minLon, maxLat, maxLon); 552 for (var z = zMax - 1; z >= 15; --z) 553 PV.auth.addOSMTileRange(z, minLat, minLon, maxLat, maxLon); 554 } 555 } 556 } 557 if (zMax > 15) layers.addImageryProvider(await PV.auth.newCesiumOSMImageryProviderMap(15)); 558 layers.addImageryProvider(await PV.auth.newCesiumOSMImageryProviderMap(zMax)); 559 return; 560 } 561 var imageryProvider = await PV.auth.newCesiumBingMapsImageryProviderMap(); 562 var layer = new Cesium.ImageryLayer(imageryProvider); 563 layers.add(layer); 564 } 565 else if (i == 4) 566 { 567 var courseID = currentCourseID(); 568 var minLat1 = getCourseMinLat(courseID); 569 var minLon1 = getCourseMinLon(courseID); 570 var maxLat1 = getCourseMaxLat(courseID); 571 var maxLon1 = getCourseMaxLon(courseID); 572 var buffer = 100.0; 573 var minLat = Math.round(moveLat(minLat1, minLon1, buffer, 180)*1000000)/1000000; 574 var minLon = Math.round(moveLon(minLat1, minLon1, buffer, 270)*1000000)/1000000; 575 var maxLat = Math.round(moveLat(maxLat1, maxLon1, buffer, 0)*1000000)/1000000; 576 var maxLon = Math.round(moveLon(maxLat1, maxLon1, buffer, 90)*1000000)/1000000; 577 customImageryFile = null; 578 customImageryFilename = ""; 579 document.getElementById('customimageryminlat').value = minLat.toString(); 580 document.getElementById('customimageryminlon').value = minLon.toString(); 581 document.getElementById('customimagerymaxlat').value = maxLat.toString(); 582 document.getElementById('customimagerymaxlon').value = maxLon.toString(); 583 document.getElementById('customimagefilename').value = ""; 584 document.getElementById('customimageryselect').value = ""; 585 document.getElementById('customimagerydiv').style.display = ''; 586 } 587} 588 589function copyCustomImageFilenameToClipboard() 590{ 591 var filename = document.getElementById('customimagefilename').value; 592 if (filename == null) filename = ''; 593 navigator.clipboard.writeText(filename); 594} 595 596function getCustomImageryBBoxKML() 597{ 598 var minLat = parseFloat(document.getElementById('customimageryminlat').value); 599 var minLon = parseFloat(document.getElementById('customimageryminlon').value); 600 var maxLat = parseFloat(document.getElementById('customimagerymaxlat').value); 601 var maxLon = parseFloat(document.getElementById('customimagerymaxlon').value); 602 if (isNaN(minLat) || minLat < -90 || minLat > +90) { alert("Please enter valid Min Latitude in range -90 .. +90"); return; }
603 if (isNaN(maxLat) || maxLat < -90 || maxLat > +90) { alert("Please enter valid Max Latitude in range -90 .. +90"); return; } 604 if (isNaN(minLon) || minLon < -180 || minLon > +180) { alert("Please enter valid Min Longitude in range -180 .. +180"); return; } 605 if (isNaN(maxLon) || maxLon < -180 || maxLon > +180) { alert("Please enter valid Max Longitude in range -180 .. +180"); return; } 606 if (maxLat <= minLat) { alert("Max Latitude must be greater than Min Latitude"); return; } 607 if (maxLat >= minLat+1) { alert("Max Latitude must be less than Min Latitude + 1 degree"); return; } 608 while (maxLon > minLon + 180) maxLon -= 360; 609 while (maxLon < minLon - 180) maxLon += 360; 610 if (Math.abs(maxLon - minLon) > 1) { alert("Max Longitude must be less than Min Longitude + 1 degree"); return; } 611 var courseID = currentCourseID(); 612 var courseName = getCourseNameFromID(courseID); 613 if (courseName == null || courseName == "") courseName = cleanString(document.getElementById("saveasinput").value); 614 if (courseName == null || courseName == "") courseName = "CourseName"; 615 var filename = courseName 616 +"_"+minLat.toString() 617 +"_"+maxLat.toString() 618 +"_"+minLon.toString() 619 +"_"+maxLon.toString() 620 +".jpg"; 621 var x = document.getElementById('customimagefilename'); 622 x.value = filename; 623 window.location.href = "bboxkml.php?" 624 +"minlat="+minLat.toString() 625 +"&minlon="+minLon.toString() 626 +"&maxlat="+maxLat.toString() 627 +"&maxlon="+maxLon.toString(); 628} 629 630function cancelCustomImageryDialog() 631{ 632 document.getElementById('customimagerydiv').style.display = 'none'; 633 document.getElementById("layerselect").selectedIndex = 0; 634 layerSelectHandler(); 635} 636 637function confirmCustomImageryDialog() 638{ 639 var x = document.getElementById("customimageryselect"); 640 if (x.files == null || x.files.length != 1) { alert("Please select image."); return; } 641 var f = x.files[0]; 642 var fname = f.name; 643 var cpnts = fname.split("_"); 644 var n = cpnts.length; 645 if (n < 5) { alert("Filename does not contain bounding box coordinates. Please select valid image file."); return; } 646 var minLat = parseFloat(cpnts[n - 4]); 647 var maxLat = parseFloat(cpnts[n - 3]); 648 var minLon = parseFloat(cpnts[n - 2]); 649 var maxLon = parseFloat(cpnts[n - 1]); 650 if (isNaN(minLat) || minLat < -90 || minLat > +90) { alert("Min Latitude must be in range -90 .. +90"); return; } 651 if (isNaN(maxLat) || maxLat < -90 || maxLat > +90) { alert("Max Latitude must be in range -90 .. +90"); return; } 652 if (isNaN(minLon) || minLon < -180 || minLon > +180) { alert("Min Longitude must be in range -180 .. +180"); return; } 653 if (isNaN(maxLon) || maxLon < -180 || maxLon > +180) { alert("Max Longitude must be in range -180 .. +180"); return; } 654 if (maxLat <= minLat) { alert("Max Latitude must be greater than Min Latitude"); return; } 655 if (maxLat >= minLat+1) { alert("Max Latitude must be less than Min Latitude + 1 degree"); return; } 656 while (maxLon > minLon + 180) maxLon -= 360; 657 while (maxLon < minLon - 180) maxLon += 360; 658 if (Math.abs(maxLon - minLon) > 1) { alert("Max Longitude must be less than Min Longitude + 1 degree"); return; } 659 document.getElementById('customimagerydiv').style.display = 'none'; 660 var reader = new FileReader(); 661 reader.onload = readerEvent => { 662 var degToRad = Math.PI / 180.0; 663 var content = readerEvent.target.result; 664 var layers = viewer.scene.imageryLayers; 665 while (layers.length > 1) layers.remove(layers.get(layers.length - 1), true); 666 satelliteProvider = 0; 667 layers.addImageryProvider(new Cesium.SingleTileImageryProvider( 668 { 669 url : content, 670 rectangle : new Cesium.Rectangle(minLon*degToRad, minLat*degToRad, maxLon*degToRad, maxLat*degToRad) 671 })); 672 } 673 reader.readAsDataURL(f); 674} 675 676function showGreenSlopePanel() 677{ 678 document.getElementById('greenspeeddiv').style.display = ''; 679 document.getElementById('greenslopediv').style.display = ''; 680 document.getElementById('greencontourdiv').style.display = ''; 681} 682 683function hideGreenSlopePanel() 684{ 685 document.getElementById('greenspeeddiv').style.display = 'none'; 686 document.getElementById('greenslopediv').style.display = 'none'; 687 document.getElementById('greencontourdiv').style.display = 'none'; 688} 689 690function terrainSelectHandler() 691{ 692 hideGreenSlopePanel(); 693 terrainModel = 1; 694 var s = document.getElementById('saveasinput').value; 695 if (s != null && s.indexOf('!!dsm') == 0) { terrainModel = 2; } 696 terrainMode = 1 + document.getElementById("terrainselect").selectedIndex; 697 if (terrainMode == 2) 698 { 699 var courseID = currentCourseID(); 700 var courseName = getCourseNameFromID(courseID); 701 if (courseName == "") courseName = cleanString(document.getElementById("saveasinput").value); 702 var minLat = getCourseMinLat(courseID); 703 var minLon = getCourseMinLon(courseID); 704 var maxLat = getCourseMaxLat(courseID); 705 var maxLon = getCourseMaxLon(courseID); 706 if (minLat == 0 && minLon == 0 && maxLat == 0 && maxLon == 0) 707 { 708 alert('Please select course.'); 709 document.getElementById("terrainselect").selectedIndex = 0; 710 terrainMode = 1; 711 return; 712 } 713 getProVisualizerTerrainData1(minLat, minLon, maxLat, maxLon, 200, null, courseName); 714 getProVisualizerTerrainData2(minLat, minLon, maxLat, maxLon, 10000, null); 715 getProVisualizerTerrainData3(minLat, minLon, maxLat, maxLon, 50000, null); 716 getNewProVisualizerTerrainProvider(); 717 } 718 else 719 { 720 terrainProvider = new Cesium.EllipsoidTerrainProvider(); 721 viewer.terrainProvider = terrainProvider; 722 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 3); 723 } 724} 725 726function getNewProVisualizerTerrainProvider() 727{ 728 if (terrainMode != 2) return; 729 if (!terrain1Ready || !terrain2Ready || !terrain3Ready) 730 { 731 window.setTimeout(getNewProVisualizerTerrainProvider, 500); 732 return; 733 } 734 terrainProvider = newProVisualizerTerrainProvider(); 735 viewer.terrainProvider = terrainProvider; 736 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 3); 737 if (terrain1AllowSlopes) 738 showGreenSlopePanel(); 739} 740 741function zoomInClickHandler() 742{ 743 setMode(0); 744 var p = viewer.camera.positionCartographic; 745 var k = 180.0/Math.PI; 746 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(p.latitude*k, p.longitude*k)); 747 var dh = p.height - h; 748 viewer.camera.moveForward(0.2*dh); 749} 750 751function zoomOutClickHandler() 752{ 753 setMode(0); 754 var p = viewer.camera.positionCartographic; 755 var k = 180.0/Math.PI; 756 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(p.latitude*k, p.longitude*k)); 757 var dh = p.height - h; 758 viewer.camera.moveBackward(0.25*dh); 759} 760 761function rotateLeftClickHandler() { rotateClickHandler( 11.25); } 762function rotateRightClickHandler() { rotateClickHandler(-11.25); } 763 764var rotateHeight = 0; 765var rotateRadius = 0; 766var rotateLat = 0; 767var rotateLon = 0; 768var rotatePrevLat = -1; 769var rotatePrevLon = 0; 770var rotatePrevH = 0; 771var rotatePrevB = 0; 772 773function rotateClickHandler(deltaBearing) 774{ 775 setMode(0); 776
777 var p = viewer.camera.positionCartographic; 778 var b = viewer.camera.heading*180.0/Math.PI; 779 var lat = Cesium.Math.toDegrees(p.latitude); 780 var lon = Cesium.Math.toDegrees(p.longitude); 781 var h0 = p.height; 782 var h1 = p.height - (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 783 if (geogDist(lat, lon, rotatePrevLat, rotatePrevLon) > 1.0 || Math.abs(h0 - rotatePrevH) > 1.0 || Math.abs(b - rotatePrevB) > 1.0) 784 { 785 // Default when looking above horizon... 786 rotateHeight = h0; 787 rotateRadius = h1*10; 788 rotateLat = moveLat(lat, lon, rotateRadius, b); 789 rotateLon = moveLon(lat, lon, rotateRadius, b); 790 var h2 = p.height - (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(rotateLat, rotateLon)); 791 792 // If looking below horizon... 793 var ray = new Cesium.Ray(viewer.camera.position, viewer.camera.direction); 794 var cartesian = viewer.scene.globe.pick(ray, viewer.scene); 795 if (cartesian) 796 { 797 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 798 var lat2 = Cesium.Math.toDegrees(cartographic.latitude); 799 var lon2 = Cesium.Math.toDegrees(cartographic.longitude); 800 var d = geogDist(lat, lon, lat2, lon2); 801 var dMax = h1*10; if (dMax < 100) dMax = 100; 802 if (d > dMax) d = dMax; 803 rotateRadius = d; 804 rotateLat = moveLat(lat, lon, rotateRadius, b); 805 rotateLon = moveLon(lat, lon, rotateRadius, b); 806 } 807 var courseID = currentCourseID(); 808 var hole = currentHole(); 809 if (rotateRadius > 10.0 && hole == 0) 810 { 811 // Snap to course centre... 812 var minLat = getCourseMinLat(courseID); 813 var minLon = getCourseMinLon(courseID); 814 var maxLat = getCourseMaxLat(courseID); 815 var maxLon = getCourseMaxLon(courseID); 816 var dLat = maxLat - minLat; 817 var dLon = maxLon - minLon; 818 if (dLat > 0 && dLat < 1 && dLon > 0 && dLon < 1) 819 { 820 var centreLat = (minLat + maxLat)/2; 821 var centreLon = (minLon + maxLon)/2; 822 var d2 = geogDist(lat, lon, centreLat, centreLon); 823 var b2 = geogBearing(lat, lon, centreLat, centreLon); 824 while (b2 < b - 180.0) b2 += 360.0; 825 while (b2 > b + 180.0) b2 -= 360.0; 826 var td = 1.0 - Math.abs(d2 - rotateRadius)/10000.0; 827 var tb = 1.0 - Math.abs(b2 - b)/30.0; 828 if (td > 0 && tb > 0) 829 { 830 var t = td*tb; 831 rotateLat = (1 - t)*rotateLat + t*centreLat; 832 rotateLon = (1 - t)*rotateLon + t*centreLon; 833 rotateRadius = geogDist(lat, lon, rotateLat, rotateLon); 834 } 835 } 836 } 837 else if (rotateRadius > 1.0 && hole > 0) 838 { 839 // Choose radius to rotate nicely around pin position... 840 var teeLat = getCourseHoleTeeLat(courseID, hole); 841 var teeLon = getCourseHoleTeeLon(courseID, hole); 842 var pinLat = getCourseHolePinLat(courseID, hole); 843 var pinLon = getCourseHolePinLon(courseID, hole); 844 if ((teeLat != 0 || teeLon != 0) && (pinLat != 0 || pinLon != 0)) 845 { 846 var d2 = geogDist(lat, lon, pinLat, pinLon); 847 var b2 = geogBearing(lat, lon, pinLat, pinLon); 848 while (b2 < b - 180.0) b2 += 360.0; 849 while (b2 > b + 180.0) b2 -= 360.0; 850 var db = b - b2; 851 if (Math.abs(db) < 45) 852 { 853 var d = (d2+50+0.7*h1)/(2*Math.cos(db*Math.PI/180.0)); 854 if (d > d2) d = d2; 855 rotateLat = moveLat(lat, lon, d, b); 856 rotateLon = moveLon(lat, lon, d, b); 857 rotateRadius = d; 858 } 859 } 860 } 861 } 862 var b2 = (rotateRadius > 1.0 ? geogBearing(lat, lon, rotateLat, rotateLon) : b); 863 b2 += deltaBearing; 864 while (b2 < 0.0) b2 += 360.0; 865 while (b2 > 360.0) b2 -= 360.0; 866 var lat2 = moveLat(rotateLat, rotateLon, rotateRadius, b2+180); 867 var lon2 = moveLon(rotateLat, rotateLon, rotateRadius, b2+180); 868 var h = rotateHeight; 869 var h2 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat2, lon2)); 870 if (h < h2 + 2) h = h2 + 2; 871 viewer.camera.setView({ 872 destination : Cesium.Cartesian3.fromDegrees(lon2, lat2, h), 873 orientation : { 874 heading : b2 * Math.PI / 180, 875 pitch : viewer.camera.pitch, 876 roll : 0 877 } 878 }); 879 rotatePrevLat = lat2; 880 rotatePrevLon = lon2; 881 rotatePrevH = h; 882 rotatePrevB = b2; 883} 884 885function setMode(newMode) 886{ 887 if (newMode == mode && mode != 9) return; 888 889 document.getElementById("setTeeButton").style.backgroundColor = ""; 890 document.getElementById("setPinButton").style.backgroundColor = ""; 891 document.getElementById("setTarget1Button").style.backgroundColor = ""; 892 document.getElementById("setTarget2Button").style.backgroundColor = ""; 893 document.getElementById("setTarget3Button").style.backgroundColor = ""; 894 document.getElementById("distanceButton").style.backgroundColor = ""; 895 document.getElementById("distanceButton2").style.backgroundColor = ""; 896 document.getElementById("clearDistanceButton").style.backgroundColor = ""; 897 898 if (mode == 9) 899 { 900 document.getElementById("symboldivA").style.backgroundColor = ""; 901 document.getElementById("symboldivB").style.backgroundColor = ""; 902 document.getElementById("symboldivC").style.backgroundColor = ""; 903 document.getElementById("symboldivD").style.backgroundColor = ""; 904 document.getElementById("symboldivE").style.backgroundColor = ""; 905 document.getElementById("symboldivF").style.backgroundColor = ""; 906 document.getElementById("symboldiv1").style.backgroundColor = ""; 907 document.getElementById("symboldiv2").style.backgroundColor = ""; 908 document.getElementById("symboldiv3").style.backgroundColor = ""; 909 document.getElementById("symboldiv4").style.backgroundColor = ""; 910 document.getElementById("symboldiv5").style.backgroundColor = ""; 911 document.getElementById("symboldiv6").style.backgroundColor = ""; 912 document.getElementById("symboldivT").style.backgroundColor = ""; 913 document.getElementById("symboldivX").style.backgroundColor = ""; 914 document.getElementById("symboldivL").style.backgroundColor = ""; 915 document.getElementById("symboldivM").style.backgroundColor = ""; 916 document.getElementById("symboldivN").style.backgroundColor = ""; 917 document.getElementById("symboldivO").style.backgroundColor = ""; 918 document.getElementById("symboldivZ").style.backgroundColor = ""; 919 } 920 921 mode = newMode; 922 923 var c = "#00FFFF"; 924 var c2 = "rgba(0,255,255,0.2)"; 925 926 if (mode == 1) document.getElementById("setTeeButton").style.backgroundColor = c; 927 else if (mode == 2) document.getElementById("setPinButton").style.backgroundColor = c; 928 else if (mode == 3) document.getElementById("setTarget1Button").style.backgroundColor = c; 929 else if (mode == 4) document.getElementById("setTarget2Button").style.backgroundColor = c; 930 else if (mode == 5) document.getElementById("setTarget3Button").style.backgroundColor = c; 931 else if (mode == 6) document.getElementById("distanceButton").style.backgroundColor = c; 932 else if (mode == 7) document.getElementById("distanceButton2").style.backgroundColor = c; 933 else if (mode == 8) document.getElementById("clearDistanceButton").style.backgroundColor = c; 934 else if (mode == 9) 935 { 936 if (activeSymbol == 'A') document.getElementById("symboldivA").style.backgroundColor = c2; 937 if (activeSymbol == 'B') document.getElementById("symboldivB").style.backgroundColor = c2; 938 if (activeSymbol == 'C') document.getElementById("symboldivC").style.backgroundColor = c2; 939 if (activeSymbol == 'D') document.getElementById("symboldivD").style.backgroundColor = c2; 940 if (activeSymbol == 'E') document.getElementById("symboldivE").style.backgroundColor = c2; 941 if (activeSymbol == 'F') document.getElementById("symboldivF").style.backgroundColor = c2; 942 if (activeSymbol == '1') document.getElementById("symboldiv1").style.backgroundColor = c2; 943 if (activeSymbol == '2') document.getElementById("symboldiv2").style.backgroundColor = c2; 944 if (activeSymbol == '3') document.getElementById("symboldiv3").style.backgroundColor = c2; 945 if (activeSymbol == '4') document.getElementById("symboldiv4").style.backgroundColor = c2; 946 if (activeSymbol == '5') document.getElementById("symboldiv5").style.backgroundColor = c2; 947 if (activeSymbol == '6') document.getElementById("symboldiv6").style.backgroundColor = c2; 948 if (activeSymbol == 'T') document.getElementById("symboldivT").style.backgroundColor = c2; 949 if (activeSymbol == 'X') document.getElementById("symboldivX").style.backgroundColor = c2; 950 if (activeSymbol == 'L') document.getElementById("symboldivL").style.backgroundColor = c2; 951 if (activeSymbol == 'M') document.getElementById("symboldivM").style.backgroundColor = c2; 952 if (activeSymbol == 'N') document.getElementById("symboldivN").style.backgroundColor = c2; 953 if (activeSymbol == 'O') document.getElementById("symboldivO").style.backgroundColor = c2; 954 if (activeSymbol == 'Z') document.getElementById("symboldivZ").style.backgroundColor = c2; 955 } 956 957 setDistDiv(''); 958 setDistDiv1(''); 959 setDistDiv2(''); 960 setDistDiv3(''); 961} 962 963function initSelectedUnits() 964{ 965 document.getElementById('unitselect').selectedIndex = (getDistanceUnit() == "m" ? 1 : 0); 966} 967 968function selectUnitHandler() 969{ 970 setMode(0); 971 setDistanceUnit(document.getElementById('unitselect').selectedIndex == 1 ? "m" : "y"); 972 refreshView(); 973 setWeatherData(); 974} 975 976function initTeeCircles() 977{ 978 document.getElementById('teecircleselect').selectedIndex = getTeeCircles(); 979} 980 981function selectTeeCirclesHandler() 982{ 983 setMode(0); 984 setTeeCircles(document.getElementById('teecircleselect').selectedIndex.toString()); 985 refreshView(); 986} 987 988function teeCirclePlusHandler() 989{ 990 setMode(0); 991 var i = getTeeCircles() - 1; 992 if (i < 0) return; 993 setTeeCircles(i.toString()); 994 document.getElementById('teecircleselect').selectedIndex = i; 995 refreshView(); 996} 997 998function teeCircleMinusHandler() 999{ 1000 setMode(0); 1001 var i = getTeeCircles() + 1; 1002 if (i > 13) return; 1003 setTeeCircles(i.toString()); 1004 document.getElementById('teecircleselect').selectedIndex = i; 1005 refreshView(); 1006} 1007 1008function initCones() 1009{ 1010 document.getElementById('conesselect').selectedIndex = 0; 1011} 1012 1013function selectConesHandler() 1014{ 1015 setMode(0); 1016 if (coneCount >= 3) 1017 refreshView(); 1018 1019 conesHandler(); 1020} 1021 1022function conesHandler() 1023{ 1024 setMode(0); 1025 if (coneCount >= 3) 1026 { 1027 refreshView(); 1028 return; 1029 } 1030 1031 var courseID = currentCourseID(); 1032 var hole = currentHole(); 1033 if (hole < 1) 1034 { 1035 alert("Please select hole."); 1036 return; 1037 } 1038 var lat2 = getCourseHolePinLat(courseID, hole); 1039 var lon2 = getCourseHolePinLon(courseID, hole); 1040 if (lat2 == 0 && lon2 == 0) 1041 { 1042 alert("Please set pin position."); 1043 return; 1044 } 1045 var lat1 = getCourseHoleTeeLat(courseID, hole); 1046 var lon1 = getCourseHoleTeeLon(courseID, hole);
1047 var par = getCourseHolePar(courseID, hole); 1048 if (par < 3) 1049 { 1050 alert("Please set tee position.") 1051 return; 1052 } 1053 for (var shot = 1; shot <= par - 2; ++shot) 1054 { 1055 lat1 = getCourseHoleTeeLat(courseID, hole); 1056 lon1 = getCourseHoleTeeLon(courseID, hole); 1057 lat2 = getCourseHolePinLat(courseID, hole); 1058 lon2 = getCourseHolePinLon(courseID, hole); 1059 if (shot > 1) 1060 { 1061 lat1 = getCourseHoleTargetLat(courseID, hole, shot-1); 1062 lon1 = getCourseHoleTargetLon(courseID, hole, shot-1); 1063 } 1064 if (shot < par - 2) 1065 { 1066 lat2 = getCourseHoleTargetLat(courseID, hole, shot); 1067 lon2 = getCourseHoleTargetLon(courseID, hole, shot); 1068 } 1069 var d = geogDist(lat1, lon1, lat2, lon2); 1070 if (d < 1) 1071 { 1072 alert("Tee/Targets/Pin too close together."); 1073 return; 1074 } 1075 var bearing = geogBearing(lat1, lon1, lat2, lon2); 1076 var brightness = 0.03 * getCircleBrightness(); 1077 var c = getCircleColor(); 1078 var color = new Cesium.Color(1,1,0,brightness); 1079 if (c == 1) color = new Cesium.Color(0.5,1.0,0.5,brightness); 1080 if (c == 2) color = new Cesium.Color(0.5,1.0,1.0,brightness); 1081 if (c == 3) color = new Cesium.Color(1.0,1.0,1.0,brightness); 1082 var conesIndex = document.getElementById('conesselect').selectedIndex; 1083 var a = 10; 1084 var b = 5; 1085 var k = 180.0/Math.PI; 1086 if (conesIndex == 0) { a = 6; b = 3; } 1087 if (conesIndex == 1) { a = 8; b = 4; } 1088 if (conesIndex == 2) { a = 10; b = 5; } 1089 if (conesIndex == 3) { a = 0.10*k; b = 0.05*k; } 1090 if (conesIndex == 4) { a = 0.12*k; b = 0.06*k; } 1091 if (conesIndex == 5) { a = 0.14*k; b = 0.07*k; } 1092 if (conesIndex == 6) { a = 0.16*k; b = 0.08*k; } 1093 if (conesIndex == 7) { a = 0.18*k; b = 0.09*k; } 1094 if (conesIndex == 8) { a = 0.20*k; b = 0.10*k; } 1095 for (var i = -a; i <= a; i += b) 1096 { 1097 if (i == 0) i += b; 1098 var lat3 = lat1; 1099 var lon3 = lon1; 1100 var lat4 = moveLat(lat1, lon1, d+10, bearing+i); 1101 var lon4 = moveLon(lat1, lon1, d+10, bearing+i); 1102 if (i == 0) 1103 { 1104 lat3 = moveLat(lat1, lon1, d, bearing); 1105 lon3 = moveLon(lat1, lon1, d, bearing); 1106 } 1107 var coords = []; 1108 var h2 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat2, lon2)); 1109 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 1110 var h4 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat4, lon4)); 1111 var dh = Math.abs(i)/5.0; 1112 if (h4 < h2 - dh) h4 = h2 - dh; 1113 if (h4 > h2 + dh) h4 = h2 + dh; 1114 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 1115 coords.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 1116 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: color } }); 1117 mapLines2.push(line); 1118 } 1119 } 1120 coneCount += 1; 1121} 1122 1123function initDispersion() 1124{ 1125 var i = getDispersionProfileID(); 1126 document.getElementById('dispersionselect').selectedIndex = i; 1127 document.getElementById('nrlselect').selectedIndex = getNRL(); 1128 if (i == 11) 1129 { 1130 document.getElementById('nrlselectdiv').style.display = 'none'; 1131 document.getElementById('customdispersionbuttondiv').style.display = ''; 1132 refreshCustomDispersion(); 1133 } 1134} 1135 1136function selectDispersionHandler() 1137{ 1138 setMode(0); 1139 var i = document.getElementById('dispersionselect').selectedIndex; 1140 setDispersionProfileID(i.toString()); 1141 if (greenSlopesCount > 0 && puttHistoryOp.length > 0) 1142 { 1143 if (showPuttDispersion == 0) 1144 showPuttDispersion = 1; 1145 selectGreenSpeedHandler(); 1146 return; 1147 } 1148 refreshView(); 1149 if (i == 11) 1150 { 1151 document.getElementById('nrlselectdiv').style.display = 'none'; 1152 document.getElementById('customdispersionbuttondiv').style.display = ''; 1153 refreshCustomDispersion(); 1154 } 1155 else 1156 { 1157 document.getElementById('customdispersiondiv').style.display = 'none'; 1158 document.getElementById('customdispersionbuttondiv').style.display = 'none'; 1159 document.getElementById('nrlselectdiv').style.display = ''; 1160 } 1161} 1162 1163function refreshCustomDispersion() 1164{ 1165 var s = "<table/>"; 1166 s += "<tr>"; 1167 s += "<td></td>"; 1168 s += "<td align='left'>Left</td>"; 1169 s += "<td align='left'>Right</td>"; 1170 s += "<td align='left'>Long</td>"; 1171 s += "<td align='left'>Short</td>"; 1172 s += "<td align='center'>Shape</td>"; 1173 s += "</tr>"; 1174 s += "<tr>"; 1175 s += "<td></td>"; 1176 s += "<td><button style='width: 50%;' onclick='increaseSpreadL(-1)'>+</button><button style='width: 50%;' onclick='decreaseSpreadL(-1)'>-</button></td>"; 1177 s += "<td><button style='width: 50%;' onclick='increaseSpreadR(-1)'>+</button><button style='width: 50%;' onclick='decreaseSpreadR(-1)'>-</button></td>"; 1178 s += "<td><button style='width: 50%;' onclick='increaseSpreadO(-1)'>+</button><button style='width: 50%;' onclick='decreaseSpreadO(-1)'>-</button></td>"; 1179 s += "<td><button style='width: 50%;' onclick='increaseSpreadS(-1)'>+</button><button style='width: 50%;' onclick='decreaseSpreadS(-1)'>-</button></td>"; 1180 s += "<td>"; 1181 s += "<button style='padding: 0px;' onclick='setShape(-1,-2)'>
1181<div style='width: 20px;'>L</div></button>"; 1182 s += "<button style='padding: 0px;' onclick='setShape(-1,-1)'><div style='width: 20px;'>-</div></button>"; 1183 s += "<button style='padding: 0px;' onclick='setShape(-1, 0)'><div style='width: 20px;'>N</div></button>"; 1184 s += "<button style='padding: 0px;' onclick='setShape(-1, 1)'><div style='width: 20px;'>-</div></button>"; 1185 s += "<button style='padding: 0px;' onclick='setShape(-1, 2)'><div style='width: 20px;'>R</div></button>"; 1186 s += "</td></tr>"; 1187 for (var i = 0; i <= 16; ++i) 1188 { 1189 if (true || getClubFlag(i)) 1190 { 1191 var id = i.toString(); 1192 var name = getClubName(i); 1193 var spreadL = getClubSpreadL(i); 1194 var spreadR = getClubSpreadR(i); 1195 var spreadO = getClubSpreadO(i); 1196 var spreadS = getClubSpreadS(i); 1197 var shape = getClubShape(i); 1198 s += "<tr>"; 1199 s += "<td>" + name + "</td>"; 1200 s += "<td><input type='textbox' readonly style='width: 50px;' value='" + spreadL.toString() + "'><button onclick='increaseSpreadL("+id+")'>+</button><button onclick='decreaseSpreadL("+id+")'>-</button></td>"; 1201 s += "<td><input type='textbox' readonly style='width: 50px;' value='" + spreadR.toString() + "'><button onclick='increaseSpreadR("+id+")'>+</button><button onclick='decreaseSpreadR("+id+")'>-</button></td>"; 1202 s += "<td><input type='textbox' readonly style='width: 50px;' value='" + spreadO.toString() + "'><button onclick='increaseSpreadO("+id+")'>+</button><button onclick='decreaseSpreadO("+id+")'>-</button></td>"; 1203 s += "<td><input type='textbox' readonly style='width: 50px;' value='" + spreadS.toString() + "'><button onclick='increaseSpreadS("+id+")'>+</button><button onclick='decreaseSpreadS("+id+")'>-</button></td>"; 1204 s += "<td>"; 1205 s += "<button style='padding: 0px;' onclick='setShape("+id+",-2)'><div style='width: 20px;"; if (shape == -2) s += " background-color: #00FFFF;"; s += "'>L</div></button>"; 1206 s += "<button style='padding: 0px;' onclick='setShape("+id+",-1)'><div style='width: 20px;"; if (shape == -1) s += " background-color: #00FFFF;"; s += "'>-</div></button>"; 1207 s += "<button style='padding: 0px;' onclick='setShape("+id+", 0)'><div style='width: 20px;"; if (shape == 0) s += " background-color: #00FFFF;"; s += "'>N</div></button>"; 1208 s += "<button style='padding: 0px;' onclick='setShape("+id+", 1)'><div style='width: 20px;"; if (shape == 1) s += " background-color: #00FFFF;"; s += "'>-</div></button>"; 1209 s += "<button style='padding: 0px;' onclick='setShape("+id+", 2)'><div style='width: 20px;"; if (shape == 2) s += " background-color: #00FFFF;"; s += "'>R</div></button>"; 1210 s += "</td></tr>"; 1211 } 1212 } 1213 s += "<tr>"; 1214 s += "<td></td>"; 1215 s += "<td align='center'><button style='width: 100%;' onclick='resetSpreadL()'>Reset</button></td>"; 1216 s += "<td align='center'><button style='width: 100%;' onclick='resetSpreadR()'>Reset</button></td>"; 1217 s += "<td align='center'><button style='width: 100%;' onclick='resetSpreadO()'>Reset</button></td>"; 1218 s += "<td align='center'><button style='width: 100%;' onclick='resetSpreadS()'>Reset</button></td>"; 1219 s += "<td align='center'><button style='width: 100%;' onclick='hideCustomDispersion()'>OK</button></td>"; 1220 s += "</tr>"; 1221 s += "</table>"; 1222 document.getElementById('customdispersiondiv').innerHTML = s; 1223} 1224 1225function setShape(club, shape) 1226{ 1227 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubShape(i, shape); } 1228 else setClubShape(club, shape); 1229 refreshCustomDispersion(); 1230} 1231 1232function increaseSpreadL(club) 1233{ 1234 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadL(i, getClubSpreadL(i)+1); } 1235 else setClubSpreadL(club, getClubSpreadL(club)+1); 1236 refreshCustomDispersion(); 1237} 1238 1239function decreaseSpreadL(club) 1240{ 1241 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadL(i, getClubSpreadL(i)-1); } 1242 else setClubSpreadL(club, getClubSpreadL(club)-1); 1243 refreshCustomDispersion(); 1244} 1245 1246function increaseSpreadR(club) 1247{ 1248 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadR(i, getClubSpreadR(i)+1); } 1249 else setClubSpreadR(club, getClubSpreadR(club)+1); 1250 refreshCustomDispersion(); 1251} 1252 1253function decreaseSpreadR(club) 1254{ 1255 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadR(i, getClubSpreadR(i)-1); } 1256 else setClubSpreadR(club, getClubSpreadR(club)-1); 1257 refreshCustomDispersion(); 1258} 1259 1260function increaseSpreadO(club) 1261{ 1262 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadO(i, getClubSpreadO(i)+1); } 1263 else setClubSpreadO(club, getClubSpreadO(club)+1); 1264 refreshCustomDispersion(); 1265} 1266 1267function decreaseSpreadO(club) 1268{ 1269 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadO(i, getClubSpreadO(i)-1); } 1270 else setClubSpreadO(club, getClubSpreadO(club)-1); 1271 refreshCustomDispersion(); 1272} 1273 1274function increaseSpreadS(club) 1275{ 1276 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadS(i, getClubSpreadS(i)+1); } 1277 else setClubSpreadS(club, getClubSpreadS(club)+1); 1278 refreshCustomDispersion(); 1279} 1280 1281function decreaseSpreadS(club) 1282{ 1283 if (club < 0) { for (var i = 0; i <= 16; ++i) setClubSpreadS(i, getClubSpreadS(i)-1); } 1284 else setClubSpreadS(club, getClubSpreadS(club)-1); 1285 refreshCustomDispersion(); 1286} 1287 1288function resetSpreadL() 1289{ 1290 if (!confirm("Reset Left Spread?")) return; 1291 for (i = 0; i <= 16; ++i) 1292 setClubSpreadL(i, getClubSpreadLDefault(i)); 1293 refreshCustomDispersion(); 1294} 1295 1296function resetSpreadR() 1297{ 1298 if (!confirm("Reset Right Spread?")) return; 1299 for (i = 0; i <= 16; ++i) 1300 setClubSpreadR(i, getClubSpreadRDefault(i)); 1301 refreshCustomDispersion(); 1302} 1303 1304function resetSpreadO() 1305{ 1306 if (!confirm("Reset Long Spread?")) return; 1307 for (i = 0; i <= 16; ++i) 1308 setClubSpreadO(i, getClubSpreadODefault(i)); 1309 refreshCustomDispersion(); 1310} 1311 1312function resetSpreadS() 1313{ 1314 if (!confirm("Reset Short Spread?")) return; 1315 for (i = 0; i <= 16; ++i) 1316 setClubSpreadS(i, getClubSpreadSDefault(i)); 1317 refreshCustomDispersion(); 1318} 1319 1320function hideCustomDispersion() 1321{ 1322 document.getElementById('customdispersiondiv').style.display = 'none'; 1323 refreshView(); 1324} 1325 1326function customDispersionEditHandler() 1327{ 1328 var x = document.getElementById('customdispersiondiv'); 1329 if (x.style.display == 'none') 1330 { 1331 x.style.display = ''; 1332 refreshCustomDispersion(); 1333 } 1334 else 1335 { 1336 x.style.display = 'none'; 1337 refreshView(); 1338 } 1339} 1340 1341function selectNRLHandler() 1342{ 1343 setMode(0); 1344 setNRL(document.getElementById('nrlselect').selectedIndex.toString()); 1345 refreshView(); 1346} 1347 1348function initLineColor() 1349{ 1350 document.getElementById('linecolorselect').selectedIndex = getLineColor(); 1351} 1352 1353function selectLineColorHandler() 1354{ 1355 setMode(0); 1356 setLineColor(document.getElementById('linecolorselect').selectedIndex.toString()); 1357 refreshView(); 1358} 1359 1360function initLineBrightness() 1361{ 1362 document.getElementById('linebrightnessselect').selectedIndex = getLineBrightness(); 1363} 1364 1365function selectLineBrightnessHandler() 1366{ 1367 setMode(0); 1368 setLineBrightness(document.getElementById('linebrightnessselect').selectedIndex.toString()); 1369 refreshView(); 1370} 1371 1372function initCircleColor() 1373{ 1374 document.getElementById('circlecolorselect').selectedIndex = getCircleColor(); 1375} 1376 1377function selectCircleColorHandler() 1378{ 1379 setMode(0);
1380 setCircleColor(document.getElementById('circlecolorselect').selectedIndex.toString()); 1381 refreshView(); 1382} 1383 1384function initCircleBrightness() 1385{ 1386 document.getElementById('circlebrightnessselect').selectedIndex = getCircleBrightness(); 1387} 1388 1389function selectCircleBrightnessHandler() 1390{ 1391 setMode(0); 1392 setCircleBrightness(document.getElementById('circlebrightnessselect').selectedIndex.toString()); 1393 refreshView(); 1394} 1395 1396function initAutoRotate() 1397{ 1398 document.getElementById('autorotateselect').selectedIndex = getAutoRotate(); 1399} 1400 1401function selectAutoRotateHandler() 1402{ 1403 setMode(0); 1404 setAutoRotate(document.getElementById('autorotateselect').selectedIndex.toString()); 1405 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 3); 1406} 1407 1408function compassClickHandler() 1409{ 1410 if (mapMode != 1) return; 1411 var i = document.getElementById('autorotateselect').selectedIndex; 1412 document.getElementById('autorotateselect').selectedIndex = (i == 0 ? 1 : 0); 1413 selectAutoRotateHandler(); 1414} 1415 1416function populateCourseSelect() 1417{ 1418 var x = document.getElementById('courseselect'); 1419 1420 while (x.length > 0) 1421 x.remove(x.length - 1); 1422 1423 x.add(new Option("<New Course>", "0"), null); 1424 1425 var n = getNumCourses(); 1426 for (var i = 0; i < n; ++i) 1427 { 1428 var name = getCourseName(i); 1429 var id = getCourseID(i).toString(); 1430 var j = 1; 1431 while (j < x.length && name > x.options[j].text) 1432 ++j; 1433 if (j < x.length) 1434 x.add(new Option(name, id), j); 1435 else 1436 x.add(new Option(name, id)); 1437 } 1438} 1439 1440function currentCourseID() 1441{ 1442 return Number(document.getElementById("courseselect").value); 1443} 1444 1445function currentHole() 1446{ 1447 return Number(document.getElementById("holeselect").value); 1448} 1449 1450function currentShot() 1451{ 1452 return Number(document.getElementById("shotselect").value); 1453} 1454 1455function setSelectedCourse(courseID) 1456{ 1457 var x = document.getElementById("courseselect"); 1458 for (var i = 0; i < x.length; ++i) 1459 { 1460 if (Number(x.options[i].value) == courseID) 1461 x.selectedIndex = i; 1462 } 1463} 1464 1465function setSelectedHole(hole) 1466{ 1467 if (hole < 0) 1468 hole = 18; 1469 if (hole > 18) 1470 hole = 0; 1471 document.getElementById("holeselect").selectedIndex = hole; 1472 setSelectedShot(0); 1473} 1474 1475function setSelectedShot(shot) 1476{ 1477 if (currentHole() <= 0) 1478 shot = 0; 1479 else 1480 { 1481 var max = getCourseHolePar(currentCourseID(), currentHole()) - 1; 1482 if (max < 0) 1483 max = 0; 1484 if (shot < 0) 1485 shot = max; 1486 if (shot > max) 1487 shot = 0; 1488 } 1489 document.getElementById("shotselect").selectedIndex = shot; 1490} 1491 1492function selectCourseHandler() 1493{ 1494 setMode(0); 1495 hideWeather(); 1496 document.getElementById("saveasinput").value = ""; 1497 document.getElementById("savebutton").style.backgroundColor = ""; 1498 initCourseData(0); 1499 setSelectedHole(0); 1500 document.getElementById("layerselect").selectedIndex = 0; 1501 layerSelectHandler(); 1502 document.getElementById("terrainselect").selectedIndex = 0; 1503 terrainSelectHandler(); 1504 if (mapMode == 2) toggle3D(); 1505 zoomToCourseHole(currentCourseID(), currentHole(), true, true, 5); 1506} 1507 1508function selectHoleHandler() 1509{ 1510 setMode(0); 1511 setSelectedShot(0); 1512 zoomToCourseHole(currentCourseID(), currentHole(), true, true, 3); 1513} 1514 1515function selectShotHandler() 1516{ 1517 setMode(0); 1518 setSelectedShot(currentShot()); 1519 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 2); 1520} 1521 1522function selectNextHoleHandler() 1523{ 1524 setMode(0); 1525 setSelectedHole(currentHole() + 1); 1526 setSelectedShot(0); 1527 zoomToCourseHole(currentCourseID(), currentHole(), true, true, 3); 1528} 1529 1530function selectPrevHoleHandler() 1531{ 1532 setMode(0); 1533 setSelectedHole(currentHole() - 1); 1534 setSelectedShot(0); 1535 zoomToCourseHole(currentCourseID(), currentHole(), true, true, 3); 1536} 1537 1538function selectNextShotHandler() 1539{ 1540 if (currentHole() < 1) 1541 { 1542 selectNextHoleHandler(); 1543 return; 1544 } 1545 setMode(0); 1546 setSelectedShot(currentShot() + 1); 1547 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 2); 1548} 1549 1550function selectPrevShotHandler() 1551{ 1552 if (currentHole() < 1) 1553 { 1554 selectPrevHoleHandler(); 1555 return; 1556 } 1557 setMode(0); 1558 setSelectedShot(currentShot() - 1); 1559 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 2); 1560} 1561 1562function toggle3D() 1563{ 1564 setMode(0); 1565 if (mapMode == 1) 1566 { 1567 mapMode = 2; 1568 document.getElementById('toggle3dbutton').innerHTML = "2D/<b>3D</b>"; 1569 document.getElementById('2dcontrolsdiv').style.display = "none"; 1570 document.getElementById('3dcontrolsdiv').style.display = ""; 1571 viewer.scene.globe.maximumScreenSpaceError = 2.0; 1572 var s = document.getElementById('saveasinput').value; 1573 if (s != null && s.indexOf('!!dsm') == 0) { viewer.scene.globe.maximumScreenSpaceError = 1.0; } 1574 } 1575 else 1576 { 1577 mapMode = 1; 1578 document.getElementById('toggle3dbutton').innerHTML = "<b>2D</b>/3D"; 1579 document.getElementById('3dcontrolsdiv').style.display = "none"; 1580 document.getElementById('2dcontrolsdiv').style.display = ""; 1581 viewer.scene.globe.maximumScreenSpaceError = 1.0; 1582 } 1583 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 3); 1584} 1585 1586var heightMultiplier = get3DHeightMultiplier(); 1587var offsetMultiplier = get3DOffsetMultiplier(); 1588var angleIncrement = get3DAngleIncrement(); 1589 1590function heightPlusHandler() 1591{ 1592 heightMultiplier *= 1.1; 1593 set3DHeightMultiplier(heightMultiplier) 1594 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1595} 1596 1597function heightMinusHandler() 1598{ 1599 if (30 * heightMultiplier > 2.0) heightMultiplier /= 1.1; 1600 set3DHeightMultiplier(heightMultiplier) 1601 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1602} 1603 1604function heightResetHandler() 1605{ 1606 heightMultiplier = 1.0; 1607 set3DHeightMultiplier(heightMultiplier) 1608 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1609} 1610 1611function offsetPlusHandler() 1612{ 1613 offsetMultiplier *= 1.1; 1614 set3DOffsetMultiplier(offsetMultiplier) 1615 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1616} 1617 1618function offsetMinusHandler() 1619{ 1620 if (60 * offsetMultiplier > 5) offsetMultiplier /= 1.1; 1621 set3DOffsetMultiplier(offsetMultiplier) 1622 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1623} 1624 1625function offsetResetHandler() 1626{ 1627 offsetMultiplier = 1.0; 1628 set3DOffsetMultiplier(offsetMultiplier) 1629 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1630} 1631 1632function anglePlusHandler() 1633{ 1634 if (angleIncrement < 5) angleIncrement += 1.0;
1635 set3DAngleIncrement(angleIncrement) 1636 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1637} 1638 1639function angleMinusHandler() 1640{ 1641 if (angleIncrement > -45) angleIncrement -= 1.0; 1642 set3DAngleIncrement(angleIncrement) 1643 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1644} 1645 1646function angleResetHandler() 1647{ 1648 angleIncrement = 0.0; 1649 set3DAngleIncrement(angleIncrement) 1650 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), true, true, 1); 1651} 1652 1653function saveCourseAsHandler() 1654{ 1655 setMode(0); 1656 var id1 = currentCourseID(); 1657 var par = getCoursePar(id1); 1658 if (par == 0) 1659 { 1660 alert("Please map at least one hole."); 1661 return; 1662 } 1663 var courseName = cleanString(document.getElementById("saveasinput").value); 1664 if (courseName == "") 1665 { 1666 alert("Please enter course name."); 1667 return; 1668 } 1669 if (getCourseIDFromName(courseName) > 0) 1670 { 1671 alert(courseName + " already exists.\nPlease enter unique name."); 1672 return; 1673 } 1674 var id2 = createCourse(courseName); 1675 copyCourseData(id1, id2); 1676 initCourseData(0); 1677 populateCourseSelect(); 1678 setSelectedCourse(id2); 1679 document.getElementById("saveasinput").value = ""; 1680 document.getElementById("savebutton").style.backgroundColor = ""; 1681} 1682 1683function setInfoDiv1(s) 1684{ 1685 var x = document.getElementById('infodiv1'); 1686 x.innerHTML = s; 1687} 1688 1689function setInfoDiv2(s) 1690{ 1691 var x = document.getElementById('infodiv2'); 1692 x.innerHTML = s; 1693} 1694 1695function setInfoDiv3(s) 1696{ 1697 var x = document.getElementById('infodiv3'); 1698 x.innerHTML = s; 1699} 1700 1701function setInfoDiv4(s) 1702{ 1703 var x = document.getElementById('infodiv4'); 1704 x.innerHTML = s; 1705} 1706 1707function clearInfo() 1708{ 1709 setInfoDiv1(" "); 1710 setInfoDiv2(" "); 1711 setInfoDiv3(" "); 1712 setInfoDiv4(" "); 1713} 1714 1715function setHelpInfo() 1716{ 1717 setInfoDiv1("Select course from drop-down list above."); 1718 setInfoDiv2("Or search pre-mapped courses <a href='features/atoz.php'>here</a>."); 1719 setInfoDiv3("Or map new course using buttons below."); 1720 setInfoDiv4(" "); 1721} 1722 1723function setInfo() 1724{ 1725 var courseID = currentCourseID(); 1726 var hole = currentHole(); 1727 var shot = currentShot(); 1728 var info = " "; 1729 1730 if (hole == 0) 1731 { 1732 1733 var par = getCoursePar(courseID); 1734 var len = getCourseLength(courseID); 1735 if (par == 0 || len == 0) 1736 { 1737 setHelpInfo(); 1738 return; 1739 } 1740 else 1741 { 1742 info = "Par " + par.toString() + ", " + len.toString() + " " + myUnitName(); 1743 } 1744 } 1745 else 1746 { 1747 var teeLat = getCourseHoleTeeLat(courseID, hole); 1748 var teeLon = getCourseHoleTeeLon(courseID, hole); 1749 var pinLat = getCourseHolePinLat(courseID, hole); 1750 var pinLon = getCourseHolePinLon(courseID, hole); 1751 if (teeLat == 0 && teeLon == 0) 1752 { 1753 info = "Set Tee Position"; 1754 } 1755 else if (pinLat == 0 && pinLon == 0) 1756 { 1757 info = "Set Pin Position"; 1758 } 1759 else 1760 { 1761 var par = getCourseHolePar(courseID, hole); 1762 var len = getCourseHoleLength(courseID, hole); 1763 info = "Par " + par.toString() + ", " + len.toString() + " " + myUnitName(); 1764 if (par >= 4) 1765 { 1766 info += " ("; 1767 for (var i = 1; i <= par - 2; ++i) 1768 { 1769 if (i > 1) 1770 info += " + "; 1771 info += getCourseHoleShotLength(courseID, hole, i).toString(); 1772 } 1773 info += ")"; 1774 } 1775 } 1776 } 1777 1778 setInfoDiv1(info); 1779} 1780 1781function setDistDiv(s) 1782{ 1783 document.getElementById('distanceDiv').innerHTML = s; 1784} 1785 1786function setDistDiv1(s) 1787{ 1788 document.getElementById('distanceDiv1').innerHTML = s; 1789} 1790 1791function setDistDiv2(s) 1792{ 1793 document.getElementById('distanceDiv2').innerHTML = s; 1794} 1795 1796function setDistDiv3(s) 1797{ 1798 document.getElementById('distanceDiv3').innerHTML = s; 1799} 1800 1801function mapLeftClickHandler(click) 1802{ 1803 mapClickHandler(click, 1); 1804} 1805 1806function mapRightClickHandler(click) 1807{ 1808 mapClickHandler(click, 2); 1809} 1810 1811function mapMouseMoveHandler(move) 1812{ 1813 if (mode == 3 || mode == 4 || mode == 5) 1814 { 1815 var mousePos = new Cesium.Cartesian2(move.endPosition.x, move.endPosition.y); 1816 var ray = viewer.camera.getPickRay(mousePos); 1817 var cartesian = viewer.scene.globe.pick(ray, viewer.scene); 1818 if (!cartesian) return; 1819 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 1820 var lat = Cesium.Math.toDegrees(cartographic.latitude); 1821 var lon = Cesium.Math.toDegrees(cartographic.longitude); 1822 var courseID = currentCourseID(); 1823 var hole = currentHole(); 1824 var lat1 = getCourseHoleTeeLat(courseID, hole); 1825 var lon1 = getCourseHoleTeeLon(courseID, hole); 1826 var lat2 = getCourseHolePinLat(courseID, hole); 1827 var lon2 = getCourseHolePinLon(courseID, hole); 1828 if (mode == 4) 1829 { 1830 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 1831 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 1832 } 1833 if (mode == 5) 1834 { 1835 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 1836 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 1837 } 1838 if (lat1 != 0 || lon1 != 0) 1839 { 1840 var d = geogDist(lat1, lon1, lat, lon); 1841 var s = Math.round(metresToMyUnits(d)).toString(); 1842 if (lat2 != 0 || lon2 != 0) 1843 { 1844 d = geogDist(lat, lon, lat2, lon2); 1845 s += " + " + Math.round(metresToMyUnits(d)).toString(); 1846 } 1847 if (mode == 3) setDistDiv1(s); 1848 else if (mode == 4) setDistDiv2(s); 1849 else if (mode == 5) setDistDiv3(s); 1850 } 1851 } 1852 if (mode == 6 || mode == 7) 1853 { 1854 if (distLat1 == 0 && distLon1 == 0) return; 1855 var mousePos = new Cesium.Cartesian2(move.endPosition.x, move.endPosition.y); 1856 var ray = viewer.camera.getPickRay(mousePos); 1857 var cartesian = viewer.scene.globe.pick(ray, viewer.scene); 1858 if (!cartesian) return; 1859 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 1860 var lat = Cesium.Math.toDegrees(cartographic.latitude); 1861 var lon = Cesium.Math.toDegrees(cartographic.longitude); 1862 distLat2 = Math.round(lat*999999)/999999; 1863 distLon2 = Math.round(lon*999999)/999999; 1864 var d = geogDist(distLat1, distLon1, distLat2, distLon2); 1865 setDistDiv(formatDist(d)); 1866 } 1867} 1868 1869function mapClickHandler(click, button) 1870{ 1871 var mousePos = new Cesium.Cartesian2(click.position.x, click.position.y); 1872 var ray = viewer.camera.getPickRay(mousePos); 1873 //var cartesian = viewer.camera.pickEllipsoid(mousePos, viewer.scene.globe.ellipsoid); 1874 var cartesian = viewer.scene.globe.pick(ray, viewer.scene); 1875 1876 if (!cartesian) 1877 return; 1878 1879 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 1880 1881 var lat = Cesium.Math.toDegrees(cartographic.latitude); 1882 var lon = Cesium.Math.toDegrees(cartographic.longitude); 1883 1884 var courseID = currentCourseID(); 1885 var hole = currentHole(); 1886 1887 document.getElementById('coordsdiv').innerHTML = ""; 1888 var s = document.getElementById('saveasinput').value; 1889 if (s != null && s.indexOf('!!stimp') == 0) 1890 { 1891 var coords = "<table>"; 1892 var r = -0.05; 1893 while (r > -0.20) 1894 { 1895 var dt = 0.05; 1896 var s = 0.0; 1897 var v = 1.83; 1898 while (v > 0.001) 1899 { 1900 s += v*dt; 1901 var dv = deltaPuttSpeed(v, 0.0, 0.0, r, dt); 1902 if (dv < 0.0) 1903 v += dv; 1904 else 1905 { 1906 dv = deltaPuttSpeed(v, 0.0, 0.0, r, dt); 1907 v = 0.0; 1908 } 1909 } 1910 var feet = s * 3.0 * 1760.0 / 1609.344; 1911 if (Math.round(feet*1000) % 1000 == 0) 1912 { 1913 coords += "<tr>"; 1914 coords += "<td>" + (Math.round(feet*1000)/1000).toString() + "</td>"; 1915 coords += "<td>" + (Math.round(r*10000)/10000).toString() + "</td>"; 1916 coords += "</tr>"; 1917 } 1918 r -= 0.000002; 1919 } 1920 coords += "</table>"; 1921 document.getElementById('coordsdiv').innerHTML = coords; 1922 if (mode == 0) 1923 return; 1924 } 1925 else if (s != null && s.indexOf('!!dispersion') == 0) 1926 { 1927 var coords = "<table>"; 1928 for (yards = 100; yards <= 300; yards += 10) 1929 { 1930 for (k = 0; k <= 10; ++k) 1931 { 1932 var dispersionMultiplier = 1.0 + k*0.1; 1933 var sumX2 = 0; 1934 var sumY2 = 0; 1935 var sumR2 = 0; 1936 var sumR = 0; 1937 var dist = yards * 1609.344 / 1760; 1938 for (var i = 0; i < 100000; ++i) 1939 { 1940 // Distance Error 1941 // -8% to +6% 1942 var x = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 1943 x = 14*x - 7; // Normal distribution -7 .. +7 1944 if (dist < 90) x *= 1 + (90 - dist)/45; // Distance error gradually increases below 90 metres 1945 x = x - 1; // Normal distribution -8 .. +6 1946 x = x*dist/100; // Normal distribution -8% .. +6% 1947 if (x < 0) x *= dispersionMultiplier; 1948 1949 // Direction Error
1950 // +/- 3/5/7 degrees at 100/200/300 yards 1951 var max = 1 + 2*dist/90; // 3/5/7 at 100/200/300 yards 1952 var y = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 1953 y = 2*max*y - max; // Normal distribution -max .. +max 1954 y *= dispersionMultiplier; 1955 y = (dist + x)*y*Math.PI/180; // Convert degrees to metres 1956 1957 // Wind and bounce 1958 // Up to 5% in random direction 1959 var z = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 1960 z = 10*z - 5; // Normal distribution -5 .. +5 1961 var dist2 = z*dist/100; // Normal distribution -5% .. +5% 1962 var theta = Math.random()*2*Math.PI; 1963 x += dist2*Math.cos(theta); 1964 y += dist2*Math.sin(theta); 1965 1966 sumX2 += x*x; 1967 sumY2 += y*y; 1968 sumR2 += x*x + y*y; 1969 sumR += Math.sqrt(x*x + y*y); 1970 } 1971 // 95% confidence = 2 standard deviations (4 for spread) 1972 // 99.7% confidence = 3 standard deviations (6 for spread) 1973 var spreadX = Math.round(6*100*(Math.sqrt(sumX2/100000))*1760/1609.344)/100; // Long/Short 1974 var spreadY = Math.round(6*100*(Math.sqrt(sumY2/100000))*1760/1609.344)/100; // Left/Right 1975 var spreadR = Math.round(6*100*(Math.sqrt(sumR2/100000))*1760/1609.344)/100; // Total 1976 var proximity = Math.round(100*(sumR/100000)*1760/1609.344)/100; 1977 if (k == 0) coords += "<tr><td>" + yards.toString() + "</td>"; 1978 coords += "<td> " + spreadY.toString() + "</td>"; 1979 } 1980 coords += "</tr>"; 1981 } 1982 coords += "</table>"; 1983 document.getElementById('coordsdiv').innerHTML = coords; 1984 if (mode == 0) 1985 return; 1986 } 1987 else if (s != null && s.indexOf('!!tileinfo') == 0) 1988 { 1989 var z = 18; 1990 if (s.length > 10) z = parseInt(s.substring(10)); 1991 var x = PV.geog.tileXFromZLon(z,lon); 1992 var y = PV.geog.tileYFromZLat(z,lat); 1993 var minLat = PV.geog.tileMinLatFromZY(z,y); 1994 var minLon = PV.geog.tileMinLonFromZX(z,x); 1995 var maxLat = PV.geog.tileMaxLatFromZY(z,y); 1996 var maxLon = PV.geog.tileMaxLonFromZX(z,x); 1997 var dx = PV.geog.dist(lat,minLon,lat,maxLon); 1998 var dy = PV.geog.dist(minLat,lon,maxLat,lon); 1999 var px = dx/256; 2000 var py = dy/256; 2001 document.getElementById('coordsdiv').innerHTML = 2002 "z="+z.toString()+", y="+y.toString()+", x="+x.toString()+"<br/>" 2003 +"dx="+dx.toFixed(2)+", dy="+dy.toFixed(2)+"<br/>" 2004 +"px="+px.toFixed(2)+", py="+py.toFixed(2)+"<br/>" 2005 +"<a href='https://tile.openstreetmap.org/"+z.toString()+"/"+x.toString()+"/"+y.toString()+".png'>OSM</a>"; 2006 if (mode == 0) 2007 return; 2008 } 2009 else if (s != null && s.indexOf('!!coords1') == 0) 2010 { 2011 document.getElementById('coordsdiv').innerHTML = 2012 (Math.round(lat*1000000)/1000000.0).toString() + "," + 2013 (Math.round(lon*1000000)/1000000.0).toString() + "," + 2014 (Math.round(getProVisualizerTerrainHeight(lat,lon)*100)/100.0); 2015 if (mode == 0) 2016 return; 2017 } 2018 else if (s != null && s.indexOf('!!coords2') == 0) 2019 { 2020 var coords = ""; 2021 coords += (Math.round(getCourseHoleTeeLat(courseID,hole)*1000000)/1000000.0).toString() + ","; 2022 coords += (Math.round(getCourseHoleTeeLon(courseID,hole)*1000000)/1000000.0).toString() + ","; 2023 coords += (Math.round(getCourseHolePinLat(courseID,hole)*1000000)/1000000.0).toString() + ","; 2024 coords += (Math.round(getCourseHolePinLon(courseID,hole)*1000000)/1000000.0).toString() + ","; 2025 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,1)*1000000)/1000000.0).toString() + ","; 2026 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,1)*1000000)/1000000.0).toString() + ","; 2027 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,2)*1000000)/1000000.0).toString() + ","; 2028 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,2)*1000000)/1000000.0).toString() + ","; 2029 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,3)*1000000)/1000000.0).toString() + ","; 2030 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,3)*1000000)/1000000.0).toString(); 2031 document.getElementById('coordsdiv').innerHTML = coords; 2032 if (mode == 0) 2033 return; 2034 } 2035 else if (s != null && s.indexOf('!!coords3') == 0) 2036 { 2037 var minLat = getCourseMinLat(courseID) - 0.001; 2038 var maxLat = getCourseMaxLat(courseID) + 0.001; 2039 var minLon = getCourseMinLon(courseID) - 0.001; 2040 var maxLon = getCourseMaxLon(courseID) + 0.001; 2041 var coords = "<small>"; 2042 coords += "function elevationOverride_XXXX(lat, lon)<br/>{<br/>"; 2043 coords += "if (lat > "+minLat.toString()+" && lat < "+maxLat.toString()+" && lon > "+minLon.toString()+" && lon < "+maxLon.toString()+")<br/>{<br/>var e;<br/>"; 2044 for (var hole = 1; hole <= 18; ++hole) 2045 {
2046 coords += "e = elevHole(lat,lon,"; 2047 coords += (Math.round(getCourseHoleTeeLat(courseID,hole)*1000000)/1000000.0).toString() + ","; 2048 coords += (Math.round(getCourseHoleTeeLon(courseID,hole)*1000000)/1000000.0).toString() + ","; 2049 coords += (Math.round(getCourseHolePinLat(courseID,hole)*1000000)/1000000.0).toString() + ","; 2050 coords += (Math.round(getCourseHolePinLon(courseID,hole)*1000000)/1000000.0).toString() + ","; 2051 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,1)*1000000)/1000000.0).toString() + ","; 2052 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,1)*1000000)/1000000.0).toString() + ","; 2053 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,2)*1000000)/1000000.0).toString() + ","; 2054 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,2)*1000000)/1000000.0).toString() + ","; 2055 coords += (Math.round(getCourseHoleTargetLat(courseID,hole,3)*1000000)/1000000.0).toString() + ","; 2056 coords += (Math.round(getCourseHoleTargetLon(courseID,hole,3)*1000000)/1000000.0).toString(); 2057 coords += ",,,,,,20,40,40,40,30); if (e > -9999) return e;<br/>"; 2058 } 2059 coords += "}<br/>return -1000000;<br/>}<br/></small>"; 2060 document.getElementById('coordsdiv').innerHTML = coords; 2061 if (mode == 0) 2062 return; 2063 } 2064 else if (s != null && s.indexOf('!!bbox') == 0) 2065 { 2066 var buffer = 200; 2067 if (s.length > 6) buffer = parseInt(s.substr(6)); 2068 var minLat = getCourseMinLat(courseID); 2069 var minLon = getCourseMinLon(courseID); 2070 var maxLat = getCourseMaxLat(courseID); 2071 var maxLon = getCourseMaxLon(courseID); 2072 minLat = Math.round(moveLat(minLat, minLon, buffer, 180)*1000000)/1000000; 2073 minLon = Math.round(moveLon(minLat, minLon, buffer, 270)*1000000)/1000000; 2074 maxLat = Math.round(moveLat(maxLat, maxLon, buffer, 0)*1000000)/1000000; 2075 maxLon = Math.round(moveLon(maxLat, maxLon, buffer, 90)*1000000)/1000000; 2076 var coords = "BBox with "; 2077 coords += buffer.toString() + "m buffer = "; 2078 coords += minLat.toString() + " "; 2079 coords += minLon.toString() + " "; 2080 coords += maxLat.toString() + " "; 2081 coords += maxLon.toString(); 2082 document.getElementById('coordsdiv').innerHTML = coords; 2083 if (mode == 0) 2084 return; 2085 } 2086 else if (s != null && s.indexOf('!!tilecounton') == 0) 2087 { 2088 showTileCount = true; 2089 } 2090 else if (s != null && s.indexOf('!!tilecountoff') == 0) 2091 { 2092 showTileCount = false; 2093 } 2094 else if (s != null && s.indexOf('!!tiles') == 0) 2095 { 2096 var courseMinLat = getCourseMinLat(courseID); 2097 var courseMinLon = getCourseMinLon(courseID); 2098 var courseMaxLat = getCourseMaxLat(courseID); 2099 var courseMaxLon = getCourseMaxLon(courseID); 2100 var courseMidLat = (courseMinLat + courseMaxLat)/2; 2101 var courseMidLon = (courseMinLon + courseMaxLon)/2; 2102 var courseDX = 200.0 + geogDist(courseMidLat, courseMinLon, courseMidLat, courseMaxLon); 2103 var courseDY = 200.0 + geogDist(courseMinLat, courseMidLon, courseMaxLat, courseMidLon); 2104 var tileStats = ""; 2105 for (var maxPixelSize = 2.0; maxPixelSize > 0.24; maxPixelSize /= 2) 2106 { 2107 var tilePixelsX = 512; 2108 var tilePixelsY = 512; 2109 var tileMinLat = -90; 2110 var tileMaxLat = 90; 2111 var tileMidLat = (tileMinLat + tileMaxLat)/2; 2112 var tileMinLon = -180; 2113 var tileMaxLon = 180; 2114 var tileMidLon = (tileMinLon + tileMaxLon)/2; 2115 var tileDX = geogDist(tileMidLat, tileMinLon, tileMidLat, tileMaxLon); 2116 var tileDY = geogDist(tileMinLat, tileMidLon, tileMaxLat, tileMidLon); 2117 var pixelSizeX = tileDX / tilePixelsX; 2118 var pixelSizeY = tileDY / tilePixelsY; 2119 var level = 0; 2120 while (pixelSizeX > maxPixelSize || pixelSizeY > maxPixelSize) 2121 { 2122 if (courseMidLat >= tileMidLat) tileMinLat = tileMidLat; else tileMaxLat = tileMidLat; 2123 if (courseMidLon >= tileMidLon) tileMinLon = tileMidLon; else tileMaxLon = tileMidLon; 2124 tileMidLat = (tileMinLat + tileMaxLat)/2; 2125 tileMidLon = (tileMinLon + tileMaxLon)/2; 2126 tileDX = geogDist(tileMidLat, tileMinLon, tileMidLat, tileMaxLon); 2127 tileDY = geogDist(tileMinLat, tileMidLon, tileMaxLat, tileMidLon); 2128 pixelSizeX = tileDX / tilePixelsX; 2129 pixelSizeY = tileDY / tilePixelsY; 2130 level += 1; 2131 } 2132 var numTilesX = Math.ceil(courseDX/tileDX);
2133 var numTilesY = Math.ceil(courseDY/tileDY); 2134 tileStats += "maxPixelSize="+maxPixelSize.toString() 2135 +", level="+level.toString() 2136 +", tileDX="+Math.round(tileDX).toString() 2137 +", tileDY="+Math.round(tileDY).toString() 2138 +", pixelSizeX="+(Math.round(1000*pixelSizeX)/1000).toString() 2139 +", pixelSizeY="+(Math.round(1000*pixelSizeY)/1000).toString() 2140 +", numTilesX="+numTilesX.toString() 2141 +", numTilesY="+numTilesY.toString() 2142 +", totalTiles="+(numTilesX*numTilesY).toString() 2143 +"<br/>"; 2144 } 2145 document.getElementById('coordsdiv').innerHTML = tileStats; 2146 if (mode == 0) 2147 return; 2148 } 2149 else if (s != null && s.indexOf('!!hdt') == 0) 2150 { 2151 document.getElementById('coordsdiv').innerHTML = ""; 2152 if (terrain1 == null || terrain1.heights == null || terrain1.heights.length != terrain1NumLat * terrain1NumLon) { alert("No terrain!"); return; } 2153 var minH = 9999; 2154 var maxH = -9999; 2155 var n = terrain1NumLat * terrain1NumLon; 2156 for (var i = 0; i < n; ++i) 2157 { 2158 var h = terrain1.heights[i]; 2159 if (h >= 0 && h < 9999) 2160 { 2161 if (h < minH) minH = h; 2162 if (h > maxH) maxH = h; 2163 } 2164 } 2165 if (maxH < 0) return; 2166 var minLat = Math.round(terrain1MinLat*1000000)/1000000.0; 2167 var maxLat = Math.round(terrain1MaxLat*1000000)/1000000.0; 2168 var minLon = Math.round(terrain1MinLon*1000000)/1000000.0; 2169 var maxLon = Math.round(terrain1MaxLon*1000000)/1000000.0; 2170 var avgLat = (minLat + maxLat)/2; 2171 var dLat = geogDist(minLat, minLon, maxLat, minLon); 2172 var dLon = geogDist(avgLat, minLon, avgLat, maxLon); 2173 var res = 1; 2174 if (s.indexOf('!!hdt2') == 0) res = 2; 2175 if (s.indexOf('!!hdt3') == 0) res = 3; 2176 if (s.indexOf('!!hdt4') == 0) res = 4; 2177 if (s.indexOf('!!hdt5') == 0) res = 5; 2178 var numLat = 2 + Math.round(dLat / res); 2179 var numLon = 2 + Math.round(dLon / res); 2180 numLon = Math.round(numLon / Math.cos(avgLat*Math.PI/180.0)); 2181 var h0 = Math.round(minH - 50); 2182 if (h0 < 0) h0 = 0; 2183 var urlPrefix = 'https://elevation.nationalmap.gov/arcgis/rest/services/3DEPElevation/ImageServer/'; 2184 var source = 1; 2185 if (minLon > -15) 2186 { 2187 urlPrefix = 'https://environment.data.gov.uk/image/rest/services/SURVEY/LIDAR_Composite_1m_DTM_2022_TSR/ImageServer/'; 2188 source = 2; 2189 var k = (numLat * numLon)/16000000.0; 2190 if (k > 1.0) { numLat /= k; numLon /= k; } 2191 } 2192 var h00 = h0; 2193 while (h0 < maxH + 50) 2194 { 2195 h1 = h0 + 50; 2196 var colorMap = "%5B"; 2197 for (var h = h0; h < h1; ++h) 2198 { 2199 var dh = h - h00; 2200 var r = 5 + 10*(Math.floor(dh)%25); 2201 var g = 5 + 10*(Math.floor(dh/25)%25); 2202 var b = 5 + 10*(Math.floor(dh/625)%25); 2203 if (h > h0) colorMap += ","; 2204 colorMap += "%5B" + h.toString() + "," + r.toString() + "," + g.toString() + "," + b.toString() + "%5D"; 2205 }
2206 colorMap += "%5D"; 2207 var url = urlPrefix + 'exportImage?f=image&bandIds=&renderingRule={"rasterFunction"%3A"Colormap",%22rasterFunctionArguments"%3A{"Colormap"%3A'+colorMap+'},"variableName"%3A"DEM"}&bbox='+minLon.toString()+'%2C'+minLat.toString()+'%2C'+maxLon.toString()+'%2C'+maxLat.toString()+'&imageSR=4326&bboxSR=4326&size='+numLon.toString()+'%2C'+numLat.toString()+'&format=png'; 2208 window.open(url, "_blank"); 2209 h0 = h1; 2210 } 2211 document.getElementById('coordsdiv').innerHTML = "h00 = " + h00.toString() + " "; 2212 document.getElementById('coordsdiv').innerHTML += "terrainList.push("; 2213 document.getElementById('coordsdiv').innerHTML += "{name:''"; 2214 document.getElementById('coordsdiv').innerHTML += ",source:" + source.toString(); 2215 document.getElementById('coordsdiv').innerHTML += ",minLat:" + minLat.toString(); 2216 document.getElementById('coordsdiv').innerHTML += ",minLon:" + minLon.toString(); 2217 document.getElementById('coordsdiv').innerHTML += ",maxLat:" + maxLat.toString(); 2218 document.getElementById('coordsdiv').innerHTML += ",maxLon:" + maxLon.toString(); 2219 document.getElementById('coordsdiv').innerHTML += ",numLat:" + numLat.toString(); 2220 document.getElementById('coordsdiv').innerHTML += ",numLon:" + numLon.toString(); 2221 document.getElementById('coordsdiv').innerHTML += "});"; 2222 if (mode == 0) 2223 return; 2224 } 2225 2226 var repeatDispersionCount = 0; 2227 if (dispersionMode == 1) 2228 repeatDispersionCount = mapMarkers2.length; 2229 2230 dispersionMode = 0; 2231 2232 if (mode == 0) 2233 { 2234 if (hole > 0) 2235 { 2236 var lat2 = getCourseHolePinLat(courseID, hole); 2237 var lon2 = getCourseHolePinLon(courseID, hole); 2238 if (geogDist(lat, lon, lat2, lon2) < 0.5) { lat = lat2; lon = lon2; } 2239 if ((greenSlopesCount > 0 || greenContoursCount > 0) && geogDist(lat, lon, lat2, lon2) < myUnitsToMetres(50)) 2240 { 2241 var opToRemove = (button == 1 ? 1 : 2); 2242 if (removePuttHistory(opToRemove, lat, lon, 0.5) > 0) selectGreenSlopesHandler(); 2243 else if (button == 1) addPuttLine(lat, lon, true, true, (s != null && s.indexOf('!!puttballs') == 0)); 2244 else setPuttTarget(lat, lon); 2245 return; 2246 } 2247 if (geogDist(lat, lon, lat2, lon2) < 10) { setPinHandler(); dispersionMode = 1; return; } 2248 lat2 = getCourseHoleTeeLat(courseID, hole); 2249 lon2 = getCourseHoleTeeLon(courseID, hole); 2250 if (geogDist(lat, lon, lat2, lon2) < 10) { setTeeHandler(); dispersionMode = 1; return; } 2251 lat2 = getCourseHoleTargetLat(courseID, hole, 1); 2252 lon2 = getCourseHoleTargetLon(courseID, hole, 1); 2253 if (geogDist(lat, lon, lat2, lon2) < 10) { setTargetHandler(1); dispersionMode = 1; return; } 2254 lat2 = getCourseHoleTargetLat(courseID, hole, 2); 2255 lon2 = getCourseHoleTargetLon(courseID, hole, 2); 2256 if (geogDist(lat, lon, lat2, lon2) < 10) { setTargetHandler(2); dispersionMode = 1; return; } 2257 lat2 = getCourseHoleTargetLat(courseID, hole, 3); 2258 lon2 = getCourseHoleTargetLon(courseID, hole, 3); 2259 if (geogDist(lat, lon, lat2, lon2) < 10) { setTargetHandler(3); dispersionMode = 1; return; } 2260 } 2261 return; 2262 } 2263 2264 if (mode == 6) 2265 { 2266 if (distLat1 == 0 && distLon1 == 0) 2267 { 2268 distLat1 = Math.round(lat*999999)/999999; 2269 distLon1 = Math.round(lon*999999)/999999; 2270 setDistDiv("Click End"); 2271 } 2272 else 2273 { 2274 distLat2 = Math.round(lat*999999)/999999; 2275 distLon2 = Math.round(lon*999999)/999999; 2276 var d = geogDist(distLat1, distLon1, distLat2, distLon2); 2277 if (d < 5) return; 2278 addDist(distLat1, distLon1, distLat2, distLon2); 2279 if (button == 2) setMode(0); 2280 setDistDiv(formatDist(d)); 2281 var i = getCourseHoleMeasures(courseID, hole); 2282 setCourseHoleMeasure(courseID, hole, i, encodeMeasure(distLat1, distLon1, distLat2, distLon2)); 2283 distLat1 = 0; 2284 distLon1 = 0; 2285 } 2286 return; 2287 } 2288 2289 if (mode == 7) 2290 { 2291 distLat2 = Math.round(lat*999999)/999999; 2292 distLon2 = Math.round(lon*999999)/999999; 2293 var d = geogDist(distLat1, distLon1, distLat2, distLon2); 2294 if (d < 5) return; 2295 addDist(distLat1, distLon1, distLat2, distLon2); 2296 if (button == 2) setMode(0); 2297 setDistDiv(formatDist(d)); 2298 var i = getCourseHoleMeasures(courseID, hole); 2299 setCourseHoleMeasure(courseID, hole, i, encodeMeasure(distLat1, distLon1, distLat2, distLon2)); 2300 return; 2301 } 2302 2303 if (mode == 8) 2304 { 2305 var n = getCourseHoleMeasures(courseID, hole); 2306 var best = -1; 2307 var bestDist = 2; 2308 for (var i = 0; i < n; ++i) 2309 { 2310 var x = decodeMeasure(getCourseHoleMeasure(courseID, hole, i)); 2311 var d = geogDist(lat, lon, x.lat1, x.lon1) + geogDist(lat, lon, x.lat2, x.lon2) - geogDist(x.lat1, x.lon1, x.lat2, x.lon2); 2312 if (d < bestDist) { best = i; bestDist = d; } 2313 } 2314 if (best >= 0) 2315 { 2316 for (var j = best; j < n - 1; ++j) 2317 setCourseHoleMeasure(courseID, hole, j, getCourseHoleMeasure(courseID, hole, j + 1)); 2318 deleteCourseHoleMeasure(courseID, hole, n - 1); 2319 refreshView(); 2320 --n; 2321 if (n == 0) setMode(0); 2322 else setDistDiv("Click Map"); 2323 if (mapMode == 2 && n > 0) 2324 { 2325 for (var k = 0; k < n; ++k) 2326 { 2327 var x = decodeMeasure(getCourseHoleMeasure(courseID, hole, k)); 2328 addDist(x.lat1, x.lon1, x.lat2, x.lon2); 2329 } 2330 } 2331 return; 2332 } 2333 setMode(0); 2334 return; 2335 } 2336 2337 if (mode == 9) 2338 { 2339 if (activeSymbol == 'Z') 2340 { 2341 var n = getCourseHoleSymbols(courseID, hole); 2342 var best = -1; 2343 var bestDist = 10; 2344 for (var i = 0; i < n; ++i) 2345 { 2346 var x = decodeSymbol(getCourseHoleSymbol(courseID, hole, i)); 2347 var d = geogDist(lat, lon, x.lat, x.lon); 2348 if (d < bestDist) { best = i; bestDist = d; } 2349 } 2350 if (best >= 0) 2351 { 2352 for (var j = best; j < n - 1; ++j) 2353 setCourseHoleSymbol(courseID, hole, j, getCourseHoleSymbol(courseID, hole, j + 1)); 2354 deleteCourseHoleSymbol(courseID, hole, n - 1); 2355 refreshView(); 2356 --n; 2357 if (n == 0) setMode(0); 2358 return; 2359 } 2360 setMode(0); 2361 return; 2362 } 2363 else 2364 { 2365 var n = getCourseHoleSymbols(courseID, hole); 2366 if (n >= 50) 2367 { 2368 alert("Max 50 symbols per hole."); 2369 setMode(0); 2370 return; 2371 } 2372 var theta = 0 - getCourseHoleBearing(courseID, hole); 2373 addSymbol(lat, lon, activeSymbol, theta); 2374 setCourseHoleSymbol(courseID, hole, n, encodeSymbol(lat, lon, activeSymbol)); 2375 if (activeSymbol != 'T' && activeSymbol != 'X') setMode(0); 2376 return; 2377 } 2378 } 2379 2380 if (hole == 0) 2381 { 2382 setMode(0); 2383 return; 2384 } 2385 2386 if (mode == 1) 2387 { 2388 setCourseHoleTeeLat(courseID, hole, lat); 2389 setCourseHoleTeeLon(courseID, hole, lon); 2390 } 2391 else if (mode == 2) 2392 { 2393 setCourseHolePinLat(courseID, hole, lat); 2394 setCourseHolePinLon(courseID, hole, lon); 2395 } 2396 else if (mode >= 3 && mode <= 5) 2397 { 2398 setCourseHoleTargetLat(courseID, hole, mode - 2, lat); 2399 setCourseHoleTargetLon(courseID, hole, mode - 2, lon); 2400 } 2401 2402 if (courseID == 0) 2403 document.getElementById("savebutton").style.backgroundColor = "#FF8888"; 2404 2405 if (button == 1) 2406 setMode(0); 2407 2408 normalizeCourseHoleTargets(courseID, hole); 2409 refreshView(); 2410 2411 while (mapMarkers2.length < repeatDispersionCount) 2412 dispersionHandler(); 2413} 2414 2415function newHandler() 2416{ 2417 setSelectedCourse(0); 2418 selectCourseHandler(); 2419} 2420 2421function searchHandler() 2422{ 2423 var searchstring = document.getElementById("saveasinput").value; 2424 if (searchstring == null) searchstring = ""; 2425 searchstring = cleanString(searchstring); 2426 if (searchstring == "") 2427 { 2428 alert("Please enter course address, town or postcode."); 2429 return; 2430 } 2431 setMode(0); 2432 initCourseData(0); 2433 setSelectedCourse(0); 2434 setSelectedHole(1); 2435 refreshView(); 2436 document.getElementById("savebutton").style.backgroundColor = ""; 2437 doXHR("findonmap", "s=" + searchstring); 2438 if (window.XMLHttpRequest) 2439 { 2440 var req = 'https://nominatim.openstreetmap.org/search?q=' + searchstring + '&format=json&polygon=0&addressdetails=0'; 2441 var xhr = new XMLHttpRequest(); 2442 xhr.onreadystatechange = function() 2443 { 2444 if (xhr.readyState == 4 && xhr.status == 200) 2445 searchResultHandler(xhr.responseText); 2446 } 2447 xhr.open("GET", req, true); 2448 xhr.send(); 2449 } 2450} 2451 2452function searchResultHandler(responseText) 2453{ 2454 var results = JSON.parse(responseText); 2455 if (results == null || results.length < 1) 2456 { 2457 alert('Address not found. Please try again.'); 2458 return; 2459 } 2460 2461 var bb = results[0].boundingbox; 2462 if (bb && bb.length == 4) 2463 { 2464 var lat1 = Number(bb[0]); 2465 var lat2 = Number(bb[1]); 2466 var lon1 = Number(bb[2]); 2467 var lon2 = Number(bb[3]); 2468 zoomToBounds(lat1,lat2,lon1,lon2); 2469 } 2470 else 2471 { 2472 alert("Search result has no bounding box. Please try again."); 2473 } 2474} 2475 2476function zoomToBounds(minLat, maxLat, minLon, maxLon) 2477{ 2478 var lat = (minLat + maxLat) / 2; 2479 var lon = (minLon + maxLon) / 2; 2480 var height = geogDist(minLat, minLon, maxLat, maxLon); 2481 if (height < 1000) height = 1000; 2482 if (height >
2482 1000000) height = 1000000; 2483 var heading = 0; 2484 var angle = -90; 2485 var durationSeconds = 3; 2486 flyToHeightAboveGround(lat, lon, lat, lon, height, heading, angle, durationSeconds); 2487} 2488 2489function setTeeHandler() 2490{ 2491 if (currentHole() == 0) 2492 { 2493 alert("Please select hole."); 2494 } 2495 else if (mode == 1) 2496 { 2497 setMode(0); 2498 } 2499 else 2500 { 2501 setMode(1); 2502 } 2503} 2504 2505function setPinHandler() 2506{ 2507 if (currentHole() == 0) 2508 { 2509 alert("Please select hole."); 2510 } 2511 else if (mode == 2) 2512 { 2513 setMode(0); 2514 } 2515 else 2516 { 2517 setMode(2); 2518 } 2519} 2520 2521function setTargetHandler(i) 2522{ 2523 if (currentHole() == 0) 2524 { 2525 alert("Please select hole."); 2526 } 2527 else if (mode == 2 + i) 2528 { 2529 setMode(0); 2530 } 2531 else 2532 { 2533 setMode(2 + i); 2534 } 2535} 2536 2537function zoomTeeHandler() 2538{ 2539 setMode(0); 2540 if (currentHole() == 0) { alert('Please select hole.'); return; } 2541 var lat = getCourseHoleTeeLat(currentCourseID(), currentHole()); 2542 var lon = getCourseHoleTeeLon(currentCourseID(), currentHole()); 2543 if (lat == 0 && lon == 0) return; 2544 var h = 90 + (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 2545 if ((mapMode == 1 && Math.abs(viewer.camera.positionCartographic.height - h) < 0.1) || (mapMode == 2 && viewer.camera.pitch < - Math.PI / 3)) 2546 { 2547 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), false, false, 2) 2548 return; 2549 } 2550 var height = 90; 2551 var heading = viewer.camera.heading * 180 / Math.PI; 2552 var angle = -90; 2553 var durationSeconds = 2; 2554 flyToHeightAboveGround(lat, lon, lat, lon, height, heading, angle, durationSeconds); 2555} 2556 2557function zoomPinHandler() 2558{ 2559 setMode(0); 2560 if (currentHole() == 0) { alert('Please select hole.'); return; } 2561 var lat = getCourseHolePinLat(currentCourseID(), currentHole()); 2562 var lon = getCourseHolePinLon(currentCourseID(), currentHole()); 2563 if (lat == 0 && lon == 0) return; 2564 var h = 60 + (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 2565 if ((mapMode == 1 && Math.abs(viewer.camera.positionCartographic.height - h) < 0.1) || (mapMode == 2 && viewer.camera.pitch < - Math.PI / 3)) 2566 { 2567 zoomToCourseHoleShot(currentCourseID(), currentHole(), currentShot(), false, false, 2) 2568 return; 2569 } 2570 var height = 60; 2571 var heading = viewer.camera.heading * 180 / Math.PI; 2572 var angle = -90; 2573 var durationSeconds = 2; 2574 flyToHeightAboveGround(lat, lon, lat, lon, height, heading, angle, durationSeconds); 2575} 2576 2577function clearTeeHandler() 2578{ 2579 setMode(0); 2580 if (currentHole() == 0) return; 2581 setCourseHoleTeeLat(currentCourseID(), currentHole(), 0); 2582 setCourseHoleTeeLon(currentCourseID(), currentHole(), 0); 2583 refreshView(); 2584} 2585 2586function clearPinHandler() 2587{ 2588 setMode(0); 2589 if (currentHole() == 0) return; 2590 setCourseHolePinLat(currentCourseID(), currentHole(), 0); 2591 setCourseHolePinLon(currentCourseID(), currentHole(), 0); 2592 refreshView(); 2593} 2594 2595function clearTargetHandler(i) 2596{ 2597 setMode(0); 2598 if (currentHole() == 0) return; 2599 setCourseHoleTargetLat(currentCourseID(), currentHole(), i, 0); 2600 setCourseHoleTargetLon(currentCourseID(), currentHole(), i, 0); 2601 normalizeCourseHoleTargets(currentCourseID(), currentHole()); 2602 refreshView(); 2603} 2604 2605function setTargetMaxHandler(i) 2606{ 2607 setMode(0); 2608 if (currentHole() == 0) return; 2609 setCourseHoleTargetMax(currentCourseID(), currentHole(), i); 2610 normalizeCourseHoleTargets(currentCourseID(), currentHole()); 2611 refreshView(); 2612} 2613 2614function setTargetDeltaHandler(i, delta) 2615{ 2616 setMode(0); 2617 if (currentHole() == 0) return; 2618 setCourseHoleTargetDelta(currentCourseID(), currentHole(), i, delta); 2619 normalizeCourseHoleTargets(currentCourseID(), currentHole()); 2620 refreshView(); 2621} 2622 2623function setTargetPlusHandler(i) 2624{ 2625 setMode(0); 2626 if (currentHole() == 0) return; 2627 setCourseHoleTargetDelta(currentCourseID(), currentHole(), i, 5); 2628 normalizeCourseHoleTargets(currentCourseID(), currentHole()); 2629 refreshView(); 2630} 2631 2632function setTargetMinusHandler(i) 2633{ 2634 setMode(0); 2635 if (currentHole() == 0) return; 2636 setCourseHoleTargetDelta(currentCourseID(), currentHole(), i, -5); 2637 normalizeCourseHoleTargets(currentCourseID(), currentHole()); 2638 refreshView(); 2639} 2640 2641function teeGridHandler() 2642{ 2643 if (teeGridCount >= 1) 2644 { 2645 refreshView(); 2646 return; 2647 } 2648 2649 var courseID = currentCourseID(); 2650 var hole = currentHole(); 2651 if (hole < 1) 2652 { 2653 alert("Please select hole."); 2654 return; 2655 } 2656 var lat2 = getCourseHolePinLat(courseID, hole); 2657 var lon2 = getCourseHolePinLon(courseID, hole); 2658 if (lat2 == 0 && lon2 == 0) 2659 { 2660 alert("Please set pin position."); 2661 return; 2662 }
2663 var par = getCourseHolePar(courseID, hole); 2664 if (par < 3) 2665 { 2666 alert("Please set tee position.") 2667 return; 2668 } 2669 var lat1 = getCourseHoleTeeLat(courseID, hole); 2670 var lon1 = getCourseHoleTeeLon(courseID, hole); 2671 if (par >= 4) 2672 { 2673 lat2 = getCourseHoleTargetLat(courseID, hole, 1); 2674 lon2 = getCourseHoleTargetLon(courseID, hole, 1); 2675 } 2676 var d = geogDist(lat1, lon1, lat2, lon2); 2677 if (d < 1) 2678 { 2679 alert("Tee/Targets/Pin too close together."); 2680 return; 2681 } 2682 var bearingY = geogBearing(lat1, lon1, lat2, lon2); 2683 var dLatY = moveLat(lat2, lon2, myUnitsToMetres(1), bearingY) - lat2; 2684 var dLonY = moveLon(lat2, lon2, myUnitsToMetres(1), bearingY) - lon2; 2685 var bearingX = bearingY + 90; 2686 var dLatX = moveLat(lat2, lon2, myUnitsToMetres(1), bearingX) - lat2; 2687 var dLonX = moveLon(lat2, lon2, myUnitsToMetres(1), bearingX) - lon2; 2688 var circleBrightness = document.getElementById('circlebrightnessselect').selectedIndex; 2689 var gridBrightness1 = 0.040 * circleBrightness; 2690 var gridBrightness2 = 0.015 * circleBrightness; 2691 var gridColor1 = new Cesium.Color(1,1,1,gridBrightness1); 2692 var gridColor2 = new Cesium.Color(1,1,1,gridBrightness2); 2693 for (var y = -50; y <= 100; y += 10) 2694 { 2695 var coords = []; 2696 for (var x = -50; x <= 50; x += 2) 2697 { 2698 var lat = lat1 + y*dLatY +x*dLatX; 2699 var lon = lon1 + y*dLonY +x*dLonX; 2700 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 2701 coords.push(Cesium.Cartesian3.fromDegrees(lon, lat, h)); 2702 } 2703 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: (y % 50 == 0 ? gridColor1 : gridColor2) } }); 2704 mapLines2.push(line); 2705 } 2706 teeGridCount += 1; 2707} 2708 2709function greenGridHandler() 2710{ 2711 setMode(0); 2712 var courseID = currentCourseID(); 2713 var hole = currentHole(); 2714 if (hole < 1) 2715 { 2716 alert("Please select hole."); 2717 return; 2718 } 2719 var lat2 = getCourseHolePinLat(courseID, hole); 2720 var lon2 = getCourseHolePinLon(courseID, hole); 2721 if (lat2 == 0 && lon2 == 0) 2722 { 2723 alert("Please set pin position."); 2724 return; 2725 } 2726 var par = getCourseHolePar(courseID, hole); 2727 if (par < 3) 2728 { 2729 alert("Please set tee position.") 2730 return; 2731 } 2732 if (greenGridCount == 6) 2733 { 2734 refreshView(); 2735 return; 2736 } 2737 else if (greenGridCount == 3) 2738 { 2739 refreshView(); 2740 if (par < 5) 2741 return; 2742 greenGridCount = 3; 2743 } 2744 2745 var lat1 = 0; 2746 var lon1 = 0; 2747 if (par == 3) 2748 { 2749 lat1 = getCourseHoleTeeLat(courseID, hole); 2750 lon1 = getCourseHoleTeeLon(courseID, hole); 2751 } 2752 else if (par == 4 || (par == 5 && greenGridCount >= 3)) 2753 { 2754 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 2755 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 2756 } 2757 else if (par == 5 || (par == 6 && greenGridCount >= 3)) 2758 { 2759 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 2760 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 2761 } 2762 else if (par == 6) 2763 { 2764 lat1 = getCourseHoleTargetLat(courseID, hole, 3); 2765 lon1 = getCourseHoleTargetLon(courseID, hole, 3); 2766 } 2767 var d = geogDist(lat1, lon1, lat2, lon2); 2768 if (d < 1) 2769 { 2770 alert("Tee/Targets/Pin too close together."); 2771 return; 2772 } 2773 var bearingY = geogBearing(lat1, lon1, lat2, lon2); 2774 var dLatY = moveLat(lat2, lon2, myUnitsToMetres(1), bearingY) - lat2; 2775 var dLonY = moveLon(lat2, lon2, myUnitsToMetres(1), bearingY) - lon2; 2776 var bearingX = bearingY + 90; 2777 var dLatX = moveLat(lat2, lon2, myUnitsToMetres(1), bearingX) - lat2; 2778 var dLonX = moveLon(lat2, lon2, myUnitsToMetres(1), bearingX) - lon2; 2779 var gridBrightness1 = (greenGridCount % 3 == 0 ? 0.02 : 0.01) * getCircleBrightness(); 2780 var gridBrightness2 = 0.01 * getCircleBrightness(); 2781 var gridColor1 = new Cesium.Color(1,1,1,gridBrightness1); 2782 var gridColor2 = new Cesium.Color(1,1,1,gridBrightness2); 2783 var step = 10; 2784 if (greenGridCount % 3 == 1) step = 5; 2785 if (greenGridCount % 3 == 2) step = 1; 2786 for (var y = -30; y <= 30; y += step) 2787 { 2788 var coords = []; 2789 for (var x = -30; x <= 30; x += 1) 2790 { 2791 var lat = lat2 + y*dLatY +x*dLatX; 2792 var lon = lon2 + y*dLonY +x*dLonX; 2793 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 2794 coords.push(Cesium.Cartesian3.fromDegrees(lon, lat, h)); 2795 } 2796 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: (y % 10 == 0 ? gridColor1 : gridColor2) } }); 2797 mapLines2.push(line); 2798 } 2799 for (var x = -30; x <= 30; x += step) 2800 { 2801 var coords = []; 2802 for (var y = -30; y <= 30; y += 1) 2803 { 2804 var lat = lat2 + y*dLatY +x*dLatX; 2805 var lon = lon2 + y*dLonY +x*dLonX; 2806 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 2807 coords.push(Cesium.Cartesian3.fromDegrees(lon, lat, h)); 2808 } 2809 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: (x % 10 == 0 ? gridColor1 : gridColor2) } }); 2810 mapLines2.push(line); 2811 } 2812 if (greenGridCount >= 3) 2813 { 2814 var coords = []; 2815 var lat3 = lat2 - 30*dLatY; 2816 var lon3 = lon2 - 30*dLonY; 2817 var h1 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat1, lon1)); 2818 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 2819 coords.push(Cesium.Cartesian3.fromDegrees(lon1, lat1, h1)); 2820 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 2821 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: (greenGridCount == 3 ? gridColor1 : gridColor2) } }); 2822 mapLines2.push(line); 2823 } 2824 greenGridCount += 1; 2825} 2826 2827function selectGreenSpeedHandler() 2828{ 2829 if (greenSlopesCount == 0) { greenSlopesHandler(); return; } 2830 var n1 = greenGridCount; 2831 var n2 = greenSlopesCount; 2832 var n3 = greenContoursCount; 2833 if (n2 < 1) n2 = 1; 2834 refreshView(); 2835 var i = 0; 2836 for (i = 0; i < n1; ++i) greenGridHandler(); 2837 for (i = 0; i < n2; ++i) greenSlopesHandler(false); 2838 for (i = 0; i < n3; ++i) greenContoursHandler(); 2839 if (n2 > 0) replayPuttHistory(); 2840} 2841 2842function selectGreenSlopesHandler() 2843{ 2844 if (greenSlopesCount == 0) { greenSlopesHandler(); return; } 2845 var n1 = greenGridCount; 2846 var n2 = greenSlopesCount; 2847 var n3 = greenContoursCount; 2848 if (n2 < 1) n2 = 1; 2849 refreshView(); 2850 var i = 0; 2851 for (i = 0; i < n1; ++i) greenGridHandler(); 2852 for (i = 0; i < n2; ++i) greenSlopesHandler(false); 2853 for (i = 0; i < n3; ++i) greenContoursHandler(); 2854 if (n2 > 0) replayPuttHistory(); 2855} 2856 2857function selectGreenContoursHandler() 2858{ 2859 if (greenContoursCount == 0) { greenContoursHandler(); return; } 2860 var n1 = greenGridCount; 2861 var n2 = greenSlopesCount; 2862 var n3 = greenContoursCount; 2863 if (n3 < 1) n3 = 1; 2864 refreshView(); 2865 var i = 0; 2866 for (i = 0; i < n1; ++i) greenGridHandler(); 2867 for (i = 0; i < n2; ++i) greenSlopesHandler(false); 2868 for (i = 0; i < n3; ++i) greenContoursHandler(); 2869 if (n2 > 0) replayPuttHistory(); 2870} 2871 2872function greenSlopesHandler(clearHistory = true) 2873{ 2874 setMode(0); 2875 if (clearHistory) clearPuttHistory(); 2876 var courseID = currentCourseID(); 2877 var hole = currentHole(); 2878 if (hole < 1) 2879 { 2880 alert("Please select hole."); 2881 return; 2882 } 2883 var lat2 = getCourseHolePinLat(courseID, hole); 2884 var lon2 = getCourseHolePinLon(courseID, hole); 2885 if (lat2 == 0 && lon2 == 0) 2886 { 2887 alert("Please set pin position."); 2888 return; 2889 }
2890 var par = getCourseHolePar(courseID, hole); 2891 if (par < 3) 2892 { 2893 alert("Please set tee position.") 2894 return; 2895 } 2896 if (greenSlopesCount != 0) 2897 { 2898 refreshView(); 2899 return; 2900 } 2901 var lat1 = 0; 2902 var lon1 = 0; 2903 if (par == 3) 2904 { 2905 lat1 = getCourseHoleTeeLat(courseID, hole); 2906 lon1 = getCourseHoleTeeLon(courseID, hole); 2907 } 2908 else if (par == 4 || (par == 5 && greenGridCount > 3)) 2909 { 2910 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 2911 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 2912 } 2913 else if (par == 5 || (par == 6 && greenGridCount > 3)) 2914 { 2915 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 2916 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 2917 } 2918 else if (par == 6) 2919 { 2920 lat1 = getCourseHoleTargetLat(courseID, hole, 3); 2921 lon1 = getCourseHoleTargetLon(courseID, hole, 3); 2922 } 2923 var d = geogDist(lat1, lon1, lat2, lon2); 2924 if (d < 1) 2925 { 2926 alert("Tee/Targets/Pin too close together."); 2927 return; 2928 } 2929 var bearingY = geogBearing(lat1, lon1, lat2, lon2); 2930 var dLatY = moveLat(lat2, lon2, myUnitsToMetres(1), bearingY) - lat2; 2931 var dLonY = moveLon(lat2, lon2, myUnitsToMetres(1), bearingY) - lon2; 2932 var bearingX = bearingY + 90; 2933 var dLatX = moveLat(lat2, lon2, myUnitsToMetres(1), bearingX) - lat2; 2934 var dLonX = moveLon(lat2, lon2, myUnitsToMetres(1), bearingX) - lon2; 2935 var gridBrightness = 0.05*document.getElementById("greenslopebrightnessselect").value; 2936 var gridColor = []; 2937 gridColor[0] = new Cesium.Color(1.00, 1.00, 1.00, gridBrightness);//White 2938 gridColor[1] = new Cesium.Color(1.00, 1.00, 0.80, gridBrightness); 2939 gridColor[2] = new Cesium.Color(1.00, 1.00, 0.60, gridBrightness); 2940 gridColor[3] = new Cesium.Color(1.00, 1.00, 0.40, gridBrightness); 2941 gridColor[4] = new Cesium.Color(1.00, 1.00, 0.20, gridBrightness); 2942 gridColor[5] = new Cesium.Color(1.00, 1.00, 0.00, gridBrightness);//Yellow 2943 gridColor[6] = new Cesium.Color(0.95, 0.80, 0.00, gridBrightness); 2944 gridColor[7] = new Cesium.Color(0.90, 0.60, 0.00, gridBrightness); 2945 gridColor[8] = new Cesium.Color(0.85, 0.40, 0.00, gridBrightness); 2946 gridColor[9] = new Cesium.Color(0.80, 0.20, 0.00, gridBrightness); 2947 gridColor[10] = new Cesium.Color(0.75, 0.00, 0.00, gridBrightness);//Red 2948 gridColor[11] = new Cesium.Color(0.60, 0.00, 0.00, gridBrightness); 2949 gridColor[12] = new Cesium.Color(0.45, 0.00, 0.00, gridBrightness); 2950 gridColor[13] = new Cesium.Color(0.30, 0.00, 0.00, gridBrightness); 2951 gridColor[14] = new Cesium.Color(0.15, 0.00, 0.00, gridBrightness); 2952 gridColor[15] = new Cesium.Color(0.00, 0.00, 0.00, gridBrightness);//Black 2953 var colorFactor = 1000.0 / getRedSlope(); 2954 var sizeIndex = document.getElementById("greenslopesizeselect").selectedIndex; 2955 var addNumbers = false; 2956 if (sizeIndex >= 4) { addNumbers = true; sizeIndex -= 4; } 2957 var lengthFactor = 1.0 + 0.5*sizeIndex; 2958 for (var x = -30; x <= 30; x += 1) 2959 { 2960 for (var y = -30; y <= 30; y += 1) 2961 { 2962 var lat3 = lat2 + y*dLatY + x*dLatX; 2963 var lon3 = lon2 + y*dLonY + x*dLonX; 2964 var hg = getProVisualizerTerrainHeightAndGradient(lat3, lon3, 0.5, bearingX, bearingY); 2965 var gx = hg.g1; 2966 var gy = hg.g2; 2967 var absgx = Math.abs(gx); 2968 var absgy = Math.abs(gy); 2969 var absgsum = absgx + absgy; 2970 if (absgsum > 0.001) 2971 { 2972 var k = 1.0; 2973 if (absgx > absgy) { var yx = absgy/absgx; k = Math.sqrt(1 + yx*yx); } 2974 else { var xy = absgx/absgy; k = Math.sqrt(1 + xy*xy); } 2975 var g = (absgx + absgy)/k; 2976 var colorIndex = Math.floor(g*colorFactor); 2977 if (colorIndex > 15) colorIndex = 15; 2978 if (addNumbers && ((x+30) % 2 == 1) && ((y+30) % 2 == 1)) 2979 { 2980 var lat4 = lat3 - (gx*dLatX + gy*dLatY); 2981 var lon4 = lon3 - (gx*dLonX + gy*dLonY); 2982 var lat5 = lat3 + (gx*dLatX + gy*dLatY); 2983 var lon5 = lon3 + (gx*dLonX + gy*dLonY); 2984 var theta = 180.0 - geogBearing(lat4, lon4, lat5, lon5); 2985 var k2 = (3.0 + sizeIndex)/100.0; 2986 var text = Math.round(g*100).toString(); 2987 addText(text, lat3, lon3, theta, k2, k2, gridColor[colorIndex], hg.h, gx, bearingX, gy, bearingY); 2988 } 2989 else 2990 { 2991 var k2 = 2.0*lengthFactor*Math.sqrt(10*g)/(10*g); 2992 var g1 = k2*(gx/k); 2993 var g2 = k2*(gy/k); 2994 var lat4 = lat3 - (g1*dLatX + g2*dLatY); 2995 var lon4 = lon3 - (g1*dLonX + g2*dLonY);
2996 var lat5 = lat3 + (g1*dLatX + g2*dLatY); 2997 var lon5 = lon3 + (g1*dLonX + g2*dLonY); 2998 var d45 = geogDist(lat4, lon4, lat5, lon5); 2999 var b45 = geogBearing(lat4, lon4, lat5, lon5); 3000 var lat6 = moveLat(lat4, lon4, 0.2*d45, b45-45.0); 3001 var lon6 = moveLon(lat4, lon4, 0.2*d45, b45-45.0); 3002 var lat7 = moveLat(lat4, lon4, 0.2*d45, b45+45.0); 3003 var lon7 = moveLon(lat4, lon4, 0.2*d45, b45+45.0); 3004 var h4 = getProVisualizerTerrainHeight(lat4, lon4); 3005 var h5 = getProVisualizerTerrainHeight(lat5, lon5); 3006 var h6 = getProVisualizerTerrainHeight(lat6, lon6); 3007 var h7 = getProVisualizerTerrainHeight(lat7, lon7); 3008 var coords = []; 3009 coords.push(Cesium.Cartesian3.fromDegrees(lon5, lat5, h5)); 3010 coords.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 3011 coords.push(Cesium.Cartesian3.fromDegrees(lon6, lat6, h6)); 3012 coords.push(Cesium.Cartesian3.fromDegrees(lon7, lat7, h7)); 3013 coords.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 3014 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: gridColor[colorIndex] } }); 3015 mapLines2.push(line); 3016 } 3017 } 3018 } 3019 } 3020 greenSlopesCount += 1; 3021 greenSlopesBearing = bearingY; 3022} 3023 3024function addGradient(lat, lon) 3025{ 3026 setMode(0); 3027 var courseID = currentCourseID(); 3028 var hole = currentHole(); 3029 if (hole < 1) { return; } 3030 var lat2 = getCourseHolePinLat(courseID, hole); 3031 var lon2 = getCourseHolePinLon(courseID, hole); 3032 if (lat2 == 0 && lon2 == 0) { return; } 3033 var par = getCourseHolePar(courseID, hole); 3034 if (par < 3) { return; } 3035 var lat1 = 0; 3036 var lon1 = 0; 3037 if (par == 3) 3038 { 3039 lat1 = getCourseHoleTeeLat(courseID, hole); 3040 lon1 = getCourseHoleTeeLon(courseID, hole); 3041 } 3042 else if (par == 4 || (par == 5 && greenGridCount > 3)) 3043 { 3044 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 3045 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 3046 } 3047 else if (par == 5 || (par == 6 && greenGridCount > 3)) 3048 { 3049 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 3050 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 3051 } 3052 else if (par == 6) 3053 { 3054 lat1 = getCourseHoleTargetLat(courseID, hole, 3); 3055 lon1 = getCourseHoleTargetLon(courseID, hole, 3); 3056 } 3057 var d = geogDist(lat1, lon1, lat2, lon2); 3058 if (d < 1) { return; } 3059 var bearingY = geogBearing(lat1, lon1, lat2, lon2); 3060 var bearingX = bearingY + 90; 3061 var hg = getProVisualizerTerrainHeightAndGradient(lat, lon, 0.5, bearingX, bearingY); 3062 var gx = hg.g1; 3063 var gy = hg.g2; 3064 var absgx = Math.abs(gx); 3065 var absgy = Math.abs(gy); 3066 var absgsum = absgx + absgy; 3067 if (absgsum > 0.001) 3068 { 3069 var k = 1.0; 3070 if (absgx > absgy) { var yx = absgy/absgx; k = Math.sqrt(1 + yx*yx); } 3071 else { var xy = absgx/absgy; k = Math.sqrt(1 + xy*xy); } 3072 var g = (absgx + absgy)/k; 3073 var dLatY = moveLat(lat, lon, myUnitsToMetres(1), bearingY) - lat; 3074 var dLonY = moveLon(lat, lon, myUnitsToMetres(1), bearingY) - lon; 3075 var dLatX = moveLat(lat, lon, myUnitsToMetres(1), bearingX) - lat; 3076 var dLonX = moveLon(lat, lon, myUnitsToMetres(1), bearingX) - lon; 3077 var lat4 = lat - (gx*dLatX + gy*dLatY); 3078 var lon4 = lon - (gx*dLonX + gy*dLonY); 3079 var lat5 = lat + (gx*dLatX + gy*dLatY); 3080 var lon5 = lon + (gx*dLonX + gy*dLonY); 3081 var text = Math.round(g*100).toString(); 3082 var theta = 180.0 - geogBearing(lat4, lon4, lat5, lon5); 3083 var hgrad1 = gx; 3084 var htheta1 = bearingX; 3085 var hgrad2 = gy; 3086 var htheta2 = bearingY; 3087 var gridBrightness = 0.07 * getCircleBrightness(); 3088 var gridColor = []; 3089 gridColor[0] = new Cesium.Color(1.00, 1.00, 1.00, gridBrightness);//White 3090 gridColor[1] = new Cesium.Color(1.00, 1.00, 0.80, gridBrightness); 3091 gridColor[2] = new Cesium.Color(1.00, 1.00, 0.60, gridBrightness); 3092 gridColor[3] = new Cesium.Color(1.00, 1.00, 0.40, gridBrightness); 3093 gridColor[4] = new Cesium.Color(1.00, 1.00, 0.20, gridBrightness); 3094 gridColor[5] = new Cesium.Color(1.00, 1.00, 0.00, gridBrightness);//Yellow 3095 gridColor[6] = new Cesium.Color(0.95, 0.80, 0.00, gridBrightness); 3096 gridColor[7] = new Cesium.Color(0.90, 0.60, 0.00, gridBrightness); 3097 gridColor[8] = new Cesium.Color(0.85, 0.40, 0.00, gridBrightness); 3098 gridColor[9] = new Cesium.Color(0.80, 0.20, 0.00, gridBrightness); 3099 gridColor[10] = new Cesium.Color(0.75, 0.00, 0.00, gridBrightness);//Red
3100 gridColor[11] = new Cesium.Color(0.60, 0.00, 0.00, gridBrightness); 3101 gridColor[12] = new Cesium.Color(0.45, 0.00, 0.00, gridBrightness); 3102 gridColor[13] = new Cesium.Color(0.30, 0.00, 0.00, gridBrightness); 3103 gridColor[14] = new Cesium.Color(0.15, 0.00, 0.00, gridBrightness); 3104 gridColor[15] = new Cesium.Color(0.00, 0.00, 0.00, gridBrightness);//Black 3105 var colorFactor = 1000.0 / getRedSlope(); 3106 var colorIndex = Math.floor(g*colorFactor); 3107 if (colorIndex > 15) colorIndex = 15; 3108 var sizeIndex = document.getElementById("greenslopesizeselect").selectedIndex; 3109 if (sizeIndex >= 4) { sizeIndex -= 4; } 3110 var k2 = (3.0 + sizeIndex)/100.0; 3111 addText(text, lat, lon, theta, k2, k2, gridColor[colorIndex], hg.h, hgrad1, htheta1, hgrad2, htheta2); 3112 } 3113} 3114 3115function deltaPuttSpeed(speed, gradientF, gradientL, rollingGradient, dt) 3116{ 3117 var maxSpeed = 30; 3118 if (maxSpeed < speed) maxSpeed = speed; 3119 var rollingGradientAtSpeed = rollingGradient/(1.0 - 0.99*speed/maxSpeed) + rollingGradient*0.1*speed; 3120 var dgF = gradientF - rollingGradientAtSpeed; 3121 var deltaSpeedF = -4.0*dt*dgF - 1.0*speed*dt*dgF - 2.0*speed*dt*gradientF; 3122 var deltaSpeedL = -4.0*dt*gradientL*(speed/(speed+0.02))*(1.0 + 0.3*speed); 3123 var vF = speed + deltaSpeedF; 3124 if (vF < 0.0001) return -speed; 3125 var vL = deltaSpeedL; 3126 var vv = vF*vF + vL*vL; 3127 var newSpeed = (vv < 0.0001 ? 0.0 : Math.sqrt(vv)); 3128 return newSpeed - speed; 3129} 3130 3131function deltaPuttBearing(speed, gradientF, gradientL, rollingGradient, dt) 3132{ 3133 var maxSpeed = 30; 3134 if (maxSpeed < speed) maxSpeed = speed; 3135 var rollingGradientAtSpeed = rollingGradient/(1.0 - 0.99*speed/maxSpeed) + rollingGradient*0.1*speed; 3136 var dgF = gradientF - rollingGradientAtSpeed; 3137 var deltaSpeedF = -4.0*dt*dgF - 1.0*speed*dt*dgF - 2.0*speed*dt*gradientF; 3138 var deltaSpeedL = -4.0*dt*gradientL*(speed/(speed+0.02))*(1.0 + 0.3*speed); 3139 var vF = speed + deltaSpeedF; 3140 if (vF < 0.0001) return 0.0; 3141 var vL = deltaSpeedL; 3142 var db = Math.atan2(-vL, vF)*180.0/Math.PI; 3143 return db; 3144} 3145 3146var puttTargetLat = 0; 3147var puttTargetLon = 0; 3148var puttHistoryOp = []; 3149var puttHistoryLat = []; 3150var puttHistoryLon = []; 3151 3152function clearPuttHistory() 3153{ 3154 puttTargetLat = 0; 3155 puttTargetLon = 0; 3156 puttHistoryOp = []; 3157 puttHistoryLat = []; 3158 puttHistoryLon = []; 3159} 3160 3161function appendPuttHistory(op, lat, lon) 3162{ 3163 var n = puttHistoryOp.length; 3164 puttHistoryOp[n] = op; 3165 puttHistoryLat[n] = lat; 3166 puttHistoryLon[n] = lon; 3167} 3168 3169function removePuttHistory(opToRemove, lat, lon, d) 3170{ 3171 var numRemoved = 0; 3172 var n = puttHistoryOp.length; 3173 var i = 0; 3174 while (i < n) 3175 { 3176 if (puttHistoryOp[i] == opToRemove && geogDist(lat, lon, puttHistoryLat[i], puttHistoryLon[i]) < d) 3177 { 3178 var k = 1; 3179 if (opToRemove == 1 ) { while (i+k < n && puttHistoryOp[i+k] == 2) ++k; } 3180 for (var j = i+k; j < n; ++j) 3181 { 3182 puttHistoryOp[j-k] = puttHistoryOp[j]; 3183 puttHistoryLat[j-k] = puttHistoryLat[j]; 3184 puttHistoryLon[j-k] = puttHistoryLon[j]; 3185 } 3186 numRemoved += k; 3187 n -= k; 3188 } 3189 else 3190 { 3191 i += 1; 3192 } 3193 } 3194 puttHistoryOp.length -= numRemoved; 3195 puttHistoryLat.length -= numRemoved; 3196 puttHistoryLon.length -= numRemoved; 3197 return numRemoved; 3198} 3199 3200function replayPuttHistory() 3201{ 3202 var copyOp = []; 3203 var copyLat = []; 3204 var copyLon = []; 3205 var n = puttHistoryOp.length; 3206 for (var i = 0; i < n; ++i) 3207 { 3208 copyOp[i] = puttHistoryOp[i]; 3209 copyLat[i] = puttHistoryLat[i]; 3210 copyLon[i] = puttHistoryLon[i]; 3211 } 3212 clearPuttHistory(); 3213 var n = copyOp.length; 3214 for (var i = 0; i < n; ++i) 3215 { 3216 if (copyOp[i] == 1) setPuttTarget(copyLat[i], copyLon[i]); 3217 else if (copyOp[i] == 2) addPuttLine(copyLat[i], copyLon[i]); 3218 } 3219} 3220 3221function setPuttTarget(lat, lon) 3222{ 3223 var snapLat = 0; 3224 var snapLon = 0; 3225 var snapDist = 0.5; 3226 var snapIndex = -1; 3227 var n = puttHistoryOp.length; 3228 for (var i = 0; i < n; ++i) 3229 { 3230 if (puttHistoryOp[i] == 1) 3231 { 3232 var d = geogDist(lat, lon, puttHistoryLat[i], puttHistoryLon[i]); 3233 if (d < snapDist) 3234 { 3235 snapLat = puttHistoryLat[i]; 3236 snapLon = puttHistoryLon[i]; 3237 snapDist = d; 3238 snapIndex = i; 3239 } 3240 } 3241 } 3242 if (snapIndex >= 0) 3243 { 3244 lat = snapLat; 3245 lon = snapLon; 3246 } 3247 appendPuttHistory(1, lat, lon); 3248 puttTargetLat = lat; 3249 puttTargetLon = lon; 3250 var repeatCount = 0; 3251 var j = n - 1; 3252 while (j >= 0 && puttHistoryOp[j] == 1 && puttHistoryLat[j] == puttTargetLat && puttHistoryLon[j] == puttTargetLon) 3253 { 3254 repeatCount += 1; 3255 --j; 3256 } 3257 if (repeatCount == 1) 3258 { 3259 var tenFeet = 10.0 * 1609.344 / (3.0 * 1760.0); 3260 for (var i = 0; i < 8; ++i) 3261 { 3262 var bearing = greenSlopesBearing + i*45; 3263 var lat2 = moveLat(lat, lon, tenFeet, bearing); 3264 var lon2 = moveLon(lat, lon, tenFeet, bearing); 3265 addPuttLine(lat2, lon2, false, true); 3266 } 3267 } 3268 else if (repeatCount == 2) 3269 { 3270 var tenFeet = 10.0 * 1609.344 / (3.0 * 1760.0); 3271 for (var i = 0; i < 8; ++i) 3272 { 3273 var bearing = greenSlopesBearing + i*45 + 22.5; 3274 var lat2 = moveLat(lat, lon, tenFeet, bearing); 3275 var lon2 = moveLon(lat, lon, tenFeet, bearing); 3276 addPuttLine(lat2, lon2, false, true); 3277 } 3278 } 3279 else 3280 { 3281 addHoleMarker(lat, lon); 3282 } 3283} 3284 3285function getRollingGradient() 3286{ 3287 var greenSpeed = 8 + document.getElementById("greenspeedselect").selectedIndex; 3288 var rollingGradient = -0.0707; 3289 switch (greenSpeed) 3290 { 3291 case 8: rollingGradient = -0.1162; break; 3292 case 9: rollingGradient = -0.1031; break; 3293 case 10: rollingGradient = -0.0927; break; 3294 case 11: rollingGradient = -0.0842; break; 3295 case 12: rollingGradient = -0.0771; break; 3296 case 13: rollingGradient = -0.0711; break; 3297 case 14: rollingGradient = -0.0660; break; 3298 case 15: rollingGradient = -0.0616; break; 3299 default: alert("Unexpected green speed."); 3300 } 3301 return rollingGradient; 3302} 3303 3304function getRedSlope() 3305{ 3306 var i = document.getElementById("greenslopecolorselect").selectedIndex; 3307 if (i > 0) return 4+i; 3308 var rollingGradient = getRollingGradient(); 3309 return -100.0*rollingGradient; 3310} 3311 3312function addPuttLine(lat, lon, addToHistory = true, animate = true, addBallMarkers = false) 3313{ 3314 var courseID = currentCourseID(); 3315 var hole = currentHole(); 3316 if (hole < 1) { alert("Please select hole."); return; } 3317 if (puttTargetLat == 0 && puttTargetLon == 0) 3318 { 3319 puttTargetLat = getCourseHolePinLat(courseID, hole); 3320 puttTargetLon = getCourseHolePinLon(courseID, hole); 3321 } 3322 if (puttTargetLat == 0 && puttTargetLon == 0) { alert("Please set pin position."); return; } 3323 var d = geogDist(lat, lon, puttTargetLat, puttTargetLon); 3324 if (d < 0.3) { return; } 3325 if (d > 50) { alert("Max 50 metres."); return; }
3326 if (addToHistory) appendPuttHistory(2, lat, lon); 3327 var rollingGradient = getRollingGradient(); 3328 var brightness = 0.07 * getCircleBrightness(); 3329 var color1 = new Cesium.Color(1.00, 1.00, 1.00, brightness); 3330 var color2 = new Cesium.Color(0.00, 1.00, 1.00, brightness*0.5); 3331 var color3 = new Cesium.Color(0.00, 1.00, 1.00, brightness); 3332 var dt = 0.05; 3333 var iMax = 10; 3334 var puttingSpeed = document.getElementById("puttingspeedselect").value; 3335 var attempt = 0; 3336 var maxAttempts = (puttingSpeed <= 6 ? 10 : 3); 3337 var finished = false; 3338 while (!finished && attempt < maxAttempts) 3339 { 3340 attempt += 1; 3341 var bearing0 = geogBearing(lat, lon, puttTargetLat, puttTargetLon); 3342 var distMax = d + 10.0; 3343 var tol = d * 10.0 * Math.PI/180.0; // 10 degrees 3344 var bearingDelta = 2.0; // 2 degrees 3345 for (var i = 0; i <= iMax; ++i) 3346 { 3347 tol *= 0.5; 3348 bearingDelta *= 0.5; 3349 finished = false; 3350 var n = 0; 3351 while (n < 360 && !finished) 3352 { 3353 var lat2 = puttTargetLat; 3354 var lon2 = puttTargetLon; 3355 var dist = 0.0; 3356 var bearingF = bearing0; 3357 if (n % 2 == 0) bearingF += bearingDelta*n/2; 3358 else bearingF -= bearingDelta*(n+1)/2; 3359 var bearing1 = bearingF; 3360 var bearingL = bearingF - 90; 3361 var hg = getProVisualizerTerrainHeightAndGradient(lat2, lon2, 0.5, bearingF, bearingL); 3362 var h2 = hg.h; 3363 var speed = 0.05; 3364 if (puttingSpeed < 1) 3365 { 3366 speed = 0.25+0.05*attempt; 3367 var minRolloutDist = 0.30; 3368 var actualRolloutDist = puttRollDist(puttTargetLat, puttTargetLon, speed, bearing1, rollingGradient); 3369 if (actualRolloutDist < minRolloutDist) 3370 { 3371 var n2 = 0; 3372 while (n2 < 10 && Math.abs(actualRolloutDist - minRolloutDist) >= 0.005) 3373 { 3374 n2 += 1; 3375 if (actualRolloutDist < 0.01) speed += 0.1; 3376 else speed *= Math.sqrt(minRolloutDist / actualRolloutDist); 3377 actualRolloutDist = puttRollDist(puttTargetLat, puttTargetLon, speed, bearing1, rollingGradient); 3378 } 3379 if (speed > 0.60) speed = 0.60; 3380 if (speed < 0.30) speed = 0.30; 3381 } 3382 if (attempt > 1) speed += 0.0005*(attempt-1); 3383 } 3384 else if (puttingSpeed <= 6) 3385 { 3386 speed = 0.1*(puttingSpeed-0); 3387 if (attempt > 1) speed += 0.0005*(attempt-1); 3388 } 3389 else 3390 { 3391 var desiredRolloutDist = 0.1*(puttingSpeed-6); 3392 var actualRolloutDist = puttRollDist(puttTargetLat, puttTargetLon, speed, bearing1, rollingGradient); 3393 var n2 = 0; 3394 while (n2 < 10 && Math.abs(actualRolloutDist - desiredRolloutDist) >= 0.005) 3395 { 3396 n2 += 1; 3397 if (actualRolloutDist < 0.01) speed += 0.1; 3398 else speed *= Math.sqrt(desiredRolloutDist / actualRolloutDist); 3399 actualRolloutDist = puttRollDist(puttTargetLat, puttTargetLon, speed, bearing1, rollingGradient); 3400 } 3401 if (speed > 1.00) speed = 1.00; 3402 if (speed < 0.01) speed = 0.01; 3403 if (attempt > 1) speed += 0.001*(attempt-1); 3404 } 3405 var speedAtHole = speed; 3406 var speedAtHoleEntry = speed; 3407 var speedAtPutter = speed; 3408 var speed0 = speed; 3409 var timeAtPutter = 0.0; 3410 var holeRadius = 0.054; 3411 var coords1 = []; 3412 var lats = []; 3413 var lons = []; 3414 var hs = []; 3415 var dt0 = dt; 3416 if (i == iMax) { coords1.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); lats.push(lat2); lons.push(lon2); hs.push(h2); } 3417 while (dist < distMax && speed > 0.0001 && !finished) 3418 { 3419 var ds = speed*dt; 3420 var lat3 = moveLat(lat2, lon2, ds, bearingF + 180.0); 3421 var lon3 = moveLon(lat2, lon2, ds, bearingF + 180.0); 3422 var hg = getProVisualizerTerrainHeightAndGradient(lat3, lon3, 0.5, bearingF, bearingL); 3423 var gradientF = hg.g1; 3424 var gradientL = hg.g2; 3425 var deltaSpeed = deltaPuttSpeed (speed, gradientF, gradientL, rollingGradient, dt); 3426 var deltaBearing = deltaPuttBearing(speed, gradientF, gradientL, rollingGradient, dt); 3427 var d2 = geogDist(lat, lon, lat2, lon2); 3428 var d3 = geogDist(lat, lon, lat3, lon3); 3429 timeAtPutter += dt; 3430 if (d2 + d3 < ds + tol) 3431 { 3432 finished = true; 3433 var t = d2/ds; 3434 if (i == iMax) 3435 { 3436 var dLat = lat3 - lat2; 3437 var dLon = lon3 - lon2; 3438 lat3 = lat2 + t*dLat; 3439 lon3 = lon2 + t*dLon; 3440 } 3441 speed0 = speed; 3442 speedAtPutter = speed - t*deltaSpeed; 3443 timeAtPutter -= (1.0 - t)*dt; 3444 dt0 = t*dt; 3445 } 3446 if (dist < holeRadius) 3447 { 3448 var t = (holeRadius - dist)/ds; 3449 speedAtHoleEntry = speed - t*deltaSpeed; 3450 //deltaBearing *= 0.75; 3451 } 3452 lat2 = lat3; 3453 lon2 = lon3; 3454 dist += ds; 3455 speed -= deltaSpeed; 3456 bearingF -= deltaBearing; 3457 bearingL = bearingF - 90; 3458 h2 = hg.h;
3459 if (i == iMax) { coords1.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); lats.push(lat2); lons.push(lon2); hs.push(h2); } 3460 } 3461 if (finished) 3462 { 3463 if (i < iMax) 3464 { 3465 bearing0 = bearing1; 3466 } 3467 else 3468 { 3469 var lat0 = lat2; 3470 var lon0 = lon2; 3471 if (animate) 3472 { 3473 var simStartTime = Date.now() + 1000; 3474 var line = viewer.entities.add({ 3475 polyline : { positions: new Cesium.CallbackProperty(function(time, result) { 3476 var elapsed = Date.now() - simStartTime; 3477 var maxIndexToShow = Math.round(elapsed / (1000*dt)); 3478 if (maxIndexToShow < 1) return []; 3479 if (maxIndexToShow >= coords1.length - 1) return coords1; 3480 return coords1.slice(coords1.length - 1 - maxIndexToShow, coords1.length); 3481 }, false), 3482 width: 1.0, 3483 material: color1 } }); 3484 fastRender(1.0 + dt*lats.length); 3485 } 3486 else 3487 { 3488 var line = viewer.entities.add({ polyline : { positions: coords1, width: 1.0, material: color1 } }); 3489 mapLines2.push(line); 3490 } 3491 if (addBallMarkers) 3492 { 3493 for (var j = 1; j <= lats.length; j += 20) 3494 addBallMarker(lats[lats.length - j], lons[lons.length - j]); 3495 } 3496 var coords2 = []; 3497 coords2.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 3498 if (speed < 0.01) speed = 0.01; 3499 var d1 = geogDist(lat2, lon2, puttTargetLat, puttTargetLon); 3500 var d2 = 0.0; 3501 while (speed > 0.001) 3502 { 3503 d2 += speed*dt; 3504 var lat3 = moveLat(lat2, lon2, speed*dt, bearingF); 3505 var lon3 = moveLon(lat2, lon2, speed*dt, bearingF); 3506 var deltaSpeed = deltaPuttSpeed(speed, 0.0, 0.0, rollingGradient, dt); 3507 lat2 = lat3; 3508 lon2 = lon3; 3509 speed += deltaSpeed; 3510 if (d2 <= d1) 3511 { 3512 h2 = getProVisualizerTerrainHeight(lat2, lon2); 3513 coords2.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 3514 } 3515 } 3516 if (d2 <= d1) 3517 { 3518 var lat3 = moveLat(lat2, lon2, 0.05, bearingF+90.0); 3519 var lon3 = moveLon(lat2, lon2, 0.05, bearingF+90.0); 3520 var h3 = getProVisualizerTerrainHeight(lat3, lon3); 3521 coords2.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 3522 var lat3 = moveLat(lat2, lon2, 0.05, bearingF-90.0); 3523 var lon3 = moveLon(lat2, lon2, 0.05, bearingF-90.0); 3524 var h3 = getProVisualizerTerrainHeight(lat3, lon3); 3525 coords2.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 3526 coords2.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 3527 } 3528 var line = viewer.entities.add({ polyline : { positions: coords2, width: 1.0, material: color2 } }); 3529 mapLines2.push(line); 3530 var k = 0.02; 3531 var theta = (bearingF - geogBearing(lat0, lon0, puttTargetLat, puttTargetLon))*Math.PI/180.0; 3532 while (theta < -Math.PI) theta += 2*Math.PI; 3533 while (theta > Math.PI) theta -= 2*Math.PI; 3534 var d3 = Math.abs(d1*theta); 3535 var text1 = Math.round(100*d1).toString(); 3536 var text2 = Math.round(100*d2).toString(); 3537 var text3 = Math.round(100*d3).toString(); 3538 if (getDistanceUnit() == "y") 3539 { 3540 var inches = Math.round(metresToYards(d1)*36); var feet = Math.floor(inches/12); inches -= 12*feet; 3541 text1 = feet.toString() + "'" + inches.toString() + '"'; 3542 var inches = Math.round(metresToYards(d2)*36); var feet = Math.floor(inches/12); inches -= 12*feet; 3543 text2 = feet.toString() + "'" + inches.toString() + '"'; 3544 var inches = Math.round(metresToYards(d3)*36); var feet = Math.floor(inches/12); inches -= 12*feet; 3545 text3 = feet.toString() + "'" + inches.toString() + '"'; 3546 } 3547 var textLat = moveLat(lat0, lon0, -0.2, bearingF); 3548 var textLon = moveLon(lat0, lon0, -0.2, bearingF); 3549 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3550 addText(text1, textLat, textLon, -bearingF, k, k, color1, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3551 var textLat = moveLat(lat0, lon0, -0.4, bearingF); 3552 var textLon = moveLon(lat0, lon0, -0.4, bearingF); 3553 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3554 addText(text2, textLat, textLon, -bearingF, k, k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3555 var textLat = moveLat(lat0, lon0, -0.6, bearingF); 3556 var textLon = moveLon(lat0, lon0, -0.6, bearingF); 3557 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3558 addText(text3, textLat, textLon, -bearingF, k, k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3559 var text4 = Math.round(100*speedAtHole).toString() + "cm/s"; 3560 var textLat = moveLat(lat0, lon0, -0.8, bearingF); 3561 var textLon = moveLon(lat0, lon0, -0.8, bearingF); 3562 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3563 addText(text4, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3564 var distPastHole = puttRollDist(puttTargetLat, puttTargetLon, speedAtHole, bearing1, rollingGradient); 3565 var text5 = "+" + (distPastHole < 9.99 ? Math.round(100*distPastHole).toString() + "cm" : "999cm"); 3566 var textLat = moveLat(lat0, lon0, -1.0, bearingF); 3567 var textLon = moveLon(lat0, lon0, -1.0, bearingF); 3568 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3569 addText(text5, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3570 var s = document.getElementById('saveasinput').value; 3571 if (showPuttDispersion > 0) 3572 { 3573 var percentHoled = addPuttDispersion(puttTargetLat, puttTargetLon, lat0, lon0, speed0, bearingF, rollingGradient, dt0, dt, (addToHistory ? viewer : null)); 3574 var text6 = Math.floor(percentHoled).toString()+"%"; 3575 var textLat = moveLat(lat0, lon0, -1.2, bearingF); 3576 var textLon = moveLon(lat0, lon0, -1.2, bearingF); 3577 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3578 addText(text6, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3579 } 3580 else if (s != null && s.indexOf('!!puttstats') == 0) 3581 { 3582 var text6 = Math.round(100*speedAtHoleEntry).toString() + "cm/s"; 3583 var textLat = moveLat(lat0, lon0, -1.2, bearingF); 3584 var textLon = moveLon(lat0, lon0, -1.2, bearingF); 3585 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3586 addText(text6, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3587 var text7 = Math.round(100*speedAtPutter).toString() + "cm/s"; 3588 var textLat = moveLat(lat0, lon0, -1.4, bearingF); 3589 var textLon = moveLon(lat0, lon0, -1.4, bearingF); 3590 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3591 addText(text7, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3592 var seconds2dp = (Math.floor(100*timeAtPutter)/100).toString(); 3593 if (seconds2dp.indexOf(".") < 0) seconds2dp += "."; 3594 while (seconds2dp.indexOf(".") > seconds2dp.length - 3) seconds2dp += "0"; 3595 var text8 = seconds2dp.toString()+"s"; 3596 var textLat = moveLat(lat0, lon0, -1.6, bearingF); 3597 var textLon = moveLon(lat0, lon0, -1.6, bearingF); 3598 var hg = getProVisualizerTerrainHeightAndGradient(textLat, textLon, 0.5, bearingF+90, bearingF); 3599 addText(text8, textLat, textLon, -bearingF, 0.8*k, 0.8*k, color3, hg.h, hg.g1, bearingF+90, hg.g2, bearingF); 3600 } 3601 } 3602 } 3603 n += 1; 3604 } 3605 } 3606 } 3607 if (!finished) 3608 { 3609 var color = new Cesium.Color(1.00, 1.00, 1.00, brightness); 3610 var bearing = geogBearing(lat, lon, puttTargetLat, puttTargetLon); 3611 var lat1 = moveLat(lat, lon, 0.15, bearing+ 45); var lon1 = moveLon(lat, lon, 0.15, bearing+ 45); var h1 = getProVisualizerTerrainHeight(lat1, lon1); 3612 var lat2 = moveLat(lat, lon, 0.15, bearing- 45); var lon2 = moveLon(lat, lon, 0.15, bearing- 45); var h2 = getProVisualizerTerrainHeight(lat2, lon2); 3613 var lat3 = moveLat(lat, lon, 0.15, bearing+135); var lon3 = moveLon(lat, lon, 0.15, bearing+135); var h3 = getProVisualizerTerrainHeight(lat3, lon3); 3614 var lat4 = moveLat(lat, lon, 0.15, bearing-135); var lon4 = moveLon(lat, lon, 0.15, bearing-135); var h4 = getProVisualizerTerrainHeight(lat4, lon4); 3615 var coords1 = []; 3616 coords1.push(Cesium.Cartesian3.fromDegrees(lon1, lat1, h1)); 3617 coords1.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 3618 var line1 = viewer.entities.add({ polyline : { positions: coords1, width: 1.0, material: color } }); 3619 mapLines2.push(line1); 3620 var coords2 = []; 3621 coords2.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 3622 coords2.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 3623 var line2 = viewer.entities.add({ polyline : { positions: coords2, width: 1.0, material: color } }); 3624 mapLines2.push(line2); 3625 } 3626} 3627 3628function puttRollDist(lat, lon, speed, bearing, rollingGradient, flat = false) 3629{ 3630 var d = 0.0; 3631 var dt = 0.05; 3632 var lat2 = lat; 3633 var lon2 = lon; 3634 var speed2 = speed; 3635 var bearing2 = bearing; 3636 while (speed2 > 0.0001) 3637 { 3638 var ds = speed2*dt; 3639 d += ds; 3640 var hg2 = getProVisualizerTerrainHeightAndGradient(lat2, lon2, 0.5, bearing2, bearing2-90); 3641 if (flat) { hg2.g1 = 0; hg2.g2 = 0; } 3642 var deltaSpeed = deltaPuttSpeed (speed2, hg2.g1, hg2.g2, rollingGradient, dt); 3643 var deltaBearing = deltaPuttBearing(speed2, hg2.g1, hg2.g2, rollingGradient, dt); 3644 var lat3 = moveLat(lat2, lon2, ds, bearing2); 3645 var lon3 = moveLon(lat2, lon2, ds, bearing2); 3646 lat2 = lat3; 3647 lon2 = lon3; 3648 speed2 += deltaSpeed; 3649 bearing2 += deltaBearing; 3650 } 3651 return d; 3652} 3653
3654function addPuttDispersion(targetLat, targetLon, startLat, startLon, startSpeed, startBearing, rollingGradient, dt0, dt1, viewer) 3655{ 3656 var targetDist = PV.geog.dist(startLat, startLon, targetLat, targetLon); 3657 if (targetDist < 0.1) return 100; 3658 var targetBearing = PV.geog.bearing(startLat, startLon, targetLat, targetLon); 3659 while (targetBearing < startBearing - 180) targetBearingF += 360; 3660 while (targetBearing > startBearing + 180) targetBearingF -= 360; 3661 var maxSpeedErrorFraction = 0.04*(targetDist+0.5)/targetDist; 3662 var maxBearingError = 1 + 0.2*Math.abs(targetBearing - startBearing); 3663 var playsLikeDist = PV.sim.puttRollDistFlat(startSpeed, rollingGradient); 3664 var distRatio = targetDist / playsLikeDist; 3665 if (distRatio > 1) 3666 { 3667 if (distRatio > 10) distRatio = 10; 3668 maxSpeedErrorFraction *= distRatio; 3669 maxBearingError *= distRatio; 3670 } 3671 if (maxSpeedErrorFraction > 0.5) maxSpeedErrorFraction = 0.5; 3672 if (maxBearingError > 10) maxBearingError = 10; 3673 var rollingGradientAtStimp12 = PV.sim.getRollingGradientAtStimp(12); 3674 var bearingErrorFactor = Math.abs(rollingGradientAtStimp12/rollingGradient); 3675 if (bearingErrorFactor > 1.0) maxBearingError *= (0.5 + 0.5*bearingErrorFactor*bearingErrorFactor); 3676 var dispersionProfileID = getDispersionProfileID(); 3677 if (dispersionProfileID <= 10) 3678 { 3679 var k = 1.0 + 0.1*dispersionProfileID; 3680 maxSpeedErrorFraction *= k; 3681 maxBearingError *= k; 3682 } 3683 var holeRadius = PV.sim.holeRadius(); 3684 var holeRadiusDLat = PV.geog.distToDeltaLat(holeRadius, targetLat, targetLon); 3685 var holeRadiusDLon = PV.geog.distToDeltaLon(holeRadius, targetLat, targetLon); 3686 var maxHoleSpeed = PV.sim.maxHoleSpeed(); 3687 var opacity = 0.05*targetDist; if (opacity > 0.2) opacity = 0.2; 3688 var missColor = new Cesium.Color(1.00, 1.00, 1.00, opacity); 3689 var holeColor = new Cesium.Color(0.50, 1.00, 0.50, 2*opacity); 3690 var holedPercent = 0; 3691 for (var i = 1; i <= showPuttDispersion; ++i) 3692 { 3693 var numHoled = 0; 3694 var numPutts = 0; 3695 for (var speedErrorIndex = -20; speedErrorIndex <= 20; ++speedErrorIndex) 3696 { 3697 for (var bearingErrorIndex = -20; bearingErrorIndex <= 20; ++bearingErrorIndex) 3698 { 3699 if (i == 2 || speedErrorIndex*speedErrorIndex + bearingErrorIndex*bearingErrorIndex < 400) 3700 { 3701 numPutts += 1; 3702 var coords = []; 3703 var lat = startLat; 3704 var lon = startLon; 3705 var speedError = startSpeed*maxSpeedErrorFraction*speedErrorIndex*(Math.abs(speedErrorIndex)+20)/800; 3706 var bearingError = maxBearingError*bearingErrorIndex/20; 3707 if (i == 2) 3708 { 3709 speedError = startSpeed*maxSpeedErrorFraction*speedErrorIndex*Math.abs(speedErrorIndex)/400; 3710 bearingError = maxBearingError*bearingErrorIndex*Math.abs(bearingErrorIndex)/400; 3711 } 3712 var speed = startSpeed + speedError; 3713 var bearing = startBearing + bearingError; 3714 var holed = false; 3715 var dt = dt0; 3716 var d = 0.0; 3717 var dMax = targetDist + 10.0; 3718 while (speed > 0.0001 && d < dMax) 3719 { 3720 var ds = speed*dt; 3721 d += ds; 3722 var lat2 = moveLat(lat, lon, ds, bearing); 3723 var lon2 = moveLon(lat, lon, ds, bearing); 3724 if (!holed && speed < maxHoleSpeed) 3725 { 3726 var effectiveHoleRadius = PV.sim.effectiveHoleRadius(speed); 3727 var k = effectiveHoleRadius / holeRadius; 3728 var holeMinLat = targetLat - k*holeRadiusDLat; 3729 var holeMinLon = targetLon - k*holeRadiusDLon; 3730 var holeMaxLat = targetLat + k*holeRadiusDLat; 3731 var holeMaxLon = targetLon + k*holeRadiusDLon; 3732 var minLat = lat; var maxLat = lat2; var minLon = lon; var maxLon = lon2; 3733 if (minLat > maxLat) { minLat = lat2; maxLat = lat; } 3734 if (minLon > maxLon) { minLon = lon2; maxLon = lon; } 3735 if (minLat < holeMaxLat && maxLat > holeMinLat && minLon < holeMaxLon && maxLon > holeMinLon) 3736 { 3737 var m = 1 + Math.round(100*ds); 3738 var dLat = lat2 - lat; 3739 var dLon = lon2 - lon; 3740 for (var j = 0; j <= m; ++j) 3741 { 3742 var t = j/m 3743 var lat3 = lat + t*dLat; 3744 var lon3 = lon + t*dLon; 3745 var d3 = PV.geog.dist(targetLat, targetLon, lat3, lon3); 3746 if (d3 < effectiveHoleRadius) 3747 { 3748 holed = true; 3749 break; 3750 } 3751 } 3752 } 3753 } 3754 lat = lat2; 3755 lon = lon2; 3756 dt = dt1; 3757 var hg = getProVisualizerTerrainHeightAndGradient(lat, lon, 0.5, bearing, bearing-90); 3758 var deltaSpeed = deltaPuttSpeed (speed, hg.g1, hg.g2, rollingGradient, dt); 3759 var deltaBearing = deltaPuttBearing(speed, hg.g1, hg.g2, rollingGradient, dt); 3760 if (speed + deltaSpeed > 0.001) 3761 { 3762 hg = getProVisualizerTerrainHeightAndGradient(lat, lon, 0.5, bearing+deltaBearing, bearing+deltaBearing-90); 3763 deltaSpeed = deltaPuttSpeed (speed+deltaSpeed, hg.g1, hg.g2, rollingGradient, dt); 3764 deltaBearing = deltaPuttBearing(speed+deltaSpeed, hg.g1, hg.g2, rollingGradient, dt); 3765 if (speed + deltaSpeed > 0.001) 3766 { 3767 hg = getProVisualizerTerrainHeightAndGradient(lat, lon, 0.5, bearing+deltaBearing, bearing+deltaBearing-90); 3768 deltaSpeed = deltaPuttSpeed (speed+deltaSpeed, hg.g1, hg.g2, rollingGradient, dt); 3769 deltaBearing = deltaPuttBearing(speed+deltaSpeed, hg.g1, hg.g2, rollingGradient, dt); 3770 } 3771 } 3772 speed += deltaSpeed; 3773 bearing += deltaBearing; 3774 coords.push(Cesium.Cartesian3.fromDegrees(lon, lat, hg.h)); 3775 } 3776 if (holed) numHoled += 1; 3777 if (viewer != null && d < dMax && i == showPuttDispersion) 3778 { 3779 var coords2 = []; 3780 var j0 = coords.length - 10; if (j0 < 0) j0 = 0; 3781 for (var j = j0; j < coords.length; ++j) coords2.push(coords[j]); 3782 var line = viewer.entities.add({ polyline : { positions: coords2, width: 1.0, material: (holed ? holeColor : missColor) } }); 3783 mapLines2.push(line); 3784 } 3785 } 3786 } 3787 } 3788 if (i == 1) 3789 holedPercent = Math.floor(100*numHoled/numPutts); 3790 } 3791 return holedPercent; 3792} 3793 3794function greenContoursHandler() 3795{ 3796 setMode(0); 3797 var courseID = currentCourseID(); 3798 var hole = currentHole(); 3799 if (hole < 1) 3800 { 3801 alert("Please select hole."); 3802 return; 3803 } 3804 var lat2 = getCourseHolePinLat(courseID, hole); 3805 var lon2 = getCourseHolePinLon(courseID, hole); 3806 if (lat2 == 0 && lon2 == 0) 3807 { 3808 alert("Please set pin position."); 3809 return; 3810 }
3811 var par = getCourseHolePar(courseID, hole); 3812 if (par < 3) 3813 { 3814 alert("Please set tee position.") 3815 return; 3816 } 3817 if (greenContoursCount != 0) 3818 { 3819 refreshView(); 3820 return; 3821 } 3822 var lat1 = 0; 3823 var lon1 = 0; 3824 if (par == 3) 3825 { 3826 lat1 = getCourseHoleTeeLat(courseID, hole); 3827 lon1 = getCourseHoleTeeLon(courseID, hole); 3828 } 3829 else if (par == 4 || (par == 5 && greenGridCount > 3)) 3830 { 3831 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 3832 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 3833 } 3834 else if (par == 5 || (par == 6 && greenGridCount > 3)) 3835 { 3836 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 3837 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 3838 } 3839 else if (par == 6) 3840 { 3841 lat1 = getCourseHoleTargetLat(courseID, hole, 3); 3842 lon1 = getCourseHoleTargetLon(courseID, hole, 3); 3843 } 3844 var d = geogDist(lat1, lon1, lat2, lon2); 3845 if (d < 1) 3846 { 3847 alert("Tee/Targets/Pin too close together."); 3848 return; 3849 } 3850 var bearingY = geogBearing(lat1, lon1, lat2, lon2); 3851 var dLatY = moveLat(lat2, lon2, myUnitsToMetres(1), bearingY) - lat2; 3852 var dLonY = moveLon(lat2, lon2, myUnitsToMetres(1), bearingY) - lon2; 3853 var bearingX = bearingY + 90; 3854 var dLatX = moveLat(lat2, lon2, myUnitsToMetres(1), bearingX) - lat2; 3855 var dLonX = moveLon(lat2, lon2, myUnitsToMetres(1), bearingX) - lon2; 3856 var gridBrightness = 0.05*document.getElementById("greencontourbrightnessselect").value; 3857 var gridColor = []; 3858 for (var j = 1; j <= 10; ++j) { for (var i = 0; i <= 255; ++i) gridColor[j*256 + i] = new Cesium.Color(i/255.0, 1.0, (255-i)/255.0, gridBrightness*(0.3+0.07*j)); } 3859 var h2 = getProVisualizerTerrainHeight(lat2, lon2); 3860 var con1 = 10*(1 + document.getElementById("greencontourselect").selectedIndex); 3861 var con2 = 2*con1; 3862 var minG = 0.01*document.getElementById("greencontourminselect").value; 3863 for (var x = -300; x <= 300; x += 1) 3864 { 3865 for (var y = -300; y <= 300; y += 1) 3866 { 3867 var lat3 = lat2 + 0.1*y*dLatY + 0.1*x*dLatX; 3868 var lon3 = lon2 + 0.1*y*dLonY + 0.1*x*dLonX; 3869 var h3 = getProVisualizerTerrainHeight(lat3, lon3); 3870 var dh = h3 - h2; 3871 var cm = con1 + Math.round(200*dh); 3872 if (cm % con2 == 0) 3873 { 3874 var hg = getProVisualizerTerrainHeightAndGradient(lat3, lon3, 0.5, bearingX, bearingY); 3875 var gx = hg.g1; 3876 var gy = hg.g2; 3877 var absgx = Math.abs(gx); 3878 var absgy = Math.abs(gy); 3879 var absgsum = absgx + absgy; 3880 if (absgsum > 0.001) 3881 { 3882 var k = 1.0; 3883 if (absgx > absgy) { var yx = absgy/absgx; k = Math.sqrt(1 + yx*yx); } 3884 else { var xy = absgx/absgy; k = Math.sqrt(1 + xy*xy); } 3885 var g = (absgx + absgy)/k; 3886 var j = Math.floor(g*200); 3887 if (j > 10) j = 10; 3888 if (j > 0 && g > minG) 3889 { 3890 var k = 0.05/g; //0.2*(g + 0.2*(1.0-g))/g; 3891 var g1 = k * hg.g1; 3892 var g2 = k * hg.g2; 3893 var lat4 = lat3 - (g1*dLatX + g2*dLatY); 3894 var lon4 = lon3 - (g1*dLonX + g2*dLonY); 3895 var lat5 = lat3 + (g1*dLatX + g2*dLatY); 3896 var lon5 = lon3 + (g1*dLonX + g2*dLonY); 3897 var h4 = getProVisualizerTerrainHeight(lat4, lon4); 3898 var h5 = getProVisualizerTerrainHeight(lat5, lon5); 3899 var coords = []; 3900 coords.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 3901 coords.push(Cesium.Cartesian3.fromDegrees(lon5, lat5, h5)); 3902 var colorIndex = 128 + Math.round(cm/2); 3903 if (colorIndex < 0) colorIndex = 0; 3904 if (colorIndex > 255) colorIndex = 255; 3905 colorIndex += j*256; 3906 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: gridColor[colorIndex] } }); 3907 mapLines2.push(line); 3908 } 3909 } 3910 } 3911 } 3912 } 3913 greenContoursCount += 1; 3914 greenSlopesBearing = bearingY; 3915} 3916 3917var col1 = new Cesium.Color(1,1,1,0.3); 3918var col2 = new Cesium.Color(1,1,1,0.5); 3919var colW = new Cesium.Color(1.0,1.0,1.0,1.0); 3920var colY = new Cesium.Color(1.0,1.0,0.0,1.0); 3921var colG = new Cesium.Color(0.2,1.0,0.2,1.0); 3922var colB = new Cesium.Color(0.0,1.0,1.0,1.0); 3923var colR = new Cesium.Color(1.0,0.2,0.2,1.0); 3924function addDist(lat1, lon1, lat2, lon2) 3925{ 3926 distCount += 1; 3927 var d = geogDist(lat1, lon1, lat2, lon2); 3928 var d2 = Math.round(metresToMyUnits(d)); 3929 var t = 0.5; 3930 if (mapMode == 1 && d > 150) 3931 { 3932 var hole = currentHole(); 3933 if (hole > 0) 3934 { 3935 var courseID = currentCourseID(); 3936 var teeLat = getCourseHoleTeeLat(courseID, hole); 3937 var teeLon = getCourseHoleTeeLon(courseID, hole); 3938 if (teeLat != 0 || teeLon != 0) 3939 { 3940 var d3 = geogDist(teeLat, teeLon, lat1, lon1); 3941 var d4 = geogDist(teeLat, teeLon, lat2, lon2);
3942 if (d3 < 100 && d4 > d3 + 100) t = (d-75)/d; 3943 } 3944 } 3945 } 3946 var text = d2.toString(); 3947 var k = 0.5 + d/1000.0; 3948 var coords1 = []; 3949 var h1 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat1, lon1)); 3950 var h2 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat2, lon2)); 3951 var lon3 = lon1 + ((t*d - (3*text.length+1)*k)/d)*(lon2-lon1); 3952 var lat3 = lat1 + ((t*d - (3*text.length+1)*k)/d)*(lat2-lat1); 3953 var h3 = h1 + (h2-h1)*geogDist(lat1,lon1,lat3,lon3)/d; 3954 coords1.push(Cesium.Cartesian3.fromDegrees(lon1, lat1, h1)); 3955 coords1.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 3956 var line1 = viewer.entities.add({ polyline : { positions: coords1, width: 1.0, material: col1 } }); 3957 mapLines2.push(line1); 3958 var coords2 = []; 3959 var lon4 = lon1 + ((t*d + (3*text.length+1)*k)/d)*(lon2-lon1); 3960 var lat4 = lat1 + ((t*d + (3*text.length+1)*k)/d)*(lat2-lat1); 3961 var h4 = h1 + (h2-h1)*geogDist(lat1,lon1,lat4,lon4)/d; 3962 coords2.push(Cesium.Cartesian3.fromDegrees(lon4, lat4, h4)); 3963 coords2.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 3964 var line2 = viewer.entities.add({ polyline : { positions: coords2, width: 1.0, material: col1 } }); 3965 mapLines2.push(line2); 3966 var lat = lat1 + t*(lat2-lat1); 3967 var lon = lon1 + t*(lon2-lon1); 3968 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 3969 var theta = 90 - geogBearing(lat1, lon1, lat2, lon2); 3970 var kx = k; 3971 var ky = k; 3972 if (mapMode == 2 && d2 >= 100) 3973 { 3974 theta -= 90; 3975 kx *= 0.5; 3976 ky *= 2.0; 3977 } 3978 var hh = 0.5*(h1+h2); 3979 var hgrad1 = (h2 - h1)/geogDist(lat1, lon1, lat2, lon2); 3980 var htheta1 = geogBearing(lat1, lon1, lat2, lon2); 3981 var hgrad2 = 0.0; 3982 var htheta2 = htheta1 + 90.0; 3983 if (hh < h + 2.0) 3984 { 3985 var lat3 = moveLat(lat, lon, -1.0, htheta2); 3986 var lon3 = moveLon(lat, lon, -1.0, htheta2); 3987 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 3988 var lat4 = moveLat(lat, lon, 1.0, htheta2); 3989 var lon4 = moveLon(lat, lon, 1.0, htheta2); 3990 var h4 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat4, lon4)); 3991 hgrad2 = (h4 - h3)/2.0; 3992 if (hh > h) hgrad2 *= 1.0 - (hh - h)/2.0; 3993 } 3994 addText(text, lat, lon, theta, kx, ky, col2, hh, hgrad1, htheta1, hgrad2, htheta2); 3995} 3996 3997function addSymbol(lat, lon, symbol, theta) 3998{ 3999 var h = 0; 4000 var a = 0; 4001 var b = 0; 4002 var c = 0; 4003 var d = 90.0; 4004 if (terrainMode == 2) 4005 { 4006 h = getProVisualizerTerrainHeight(lat, lon); 4007 var lat1 = moveLat(lat, lon, -1.0, b); 4008 var lon1 = moveLon(lat, lon, -1.0, b); 4009 var h1 = getProVisualizerTerrainHeight(lat1, lon1); 4010 var lat2 = moveLat(lat, lon, 1.0, b); 4011 var lon2 = moveLon(lat, lon, 1.0, b); 4012 var h2 = getProVisualizerTerrainHeight(lat2, lon2); 4013 a = (h2 - h1)/2.0; 4014 var lat3 = moveLat(lat, lon, -1.0, d); 4015 var lon3 = moveLon(lat, lon, -1.0, d); 4016 var h3 = getProVisualizerTerrainHeight(lat3, lon3); 4017 var lat4 = moveLat(lat, lon, 1.0, d); 4018 var lon4 = moveLon(lat, lon, 1.0, d); 4019 var h4 = getProVisualizerTerrainHeight(lat4, lon4); 4020 c = (h4 - h3)/2.0; 4021 } 4022 if (symbol == 'A') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4023 if (symbol == 'B') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4024 if (symbol == 'C') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4025 if (symbol == 'D') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4026 if (symbol == 'E') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4027 if (symbol == 'F') addText(symbol, lat, lon, theta, 1, 1, col2,h,a,b,c,d); 4028 if (symbol == '1') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4029 if (symbol == '2') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4030 if (symbol == '3') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4031 if (symbol == '4') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4032 if (symbol == '5') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4033 if (symbol == '6') addText(symbol, lat, lon, theta, 0.4, 0.4, col2,h,a,b,c,d); 4034 if (symbol == 'T') addText(symbol, lat, lon, theta, 1, 1, colG,h,a,b,c,d); 4035 if (symbol == 'X') addText(symbol, lat, lon, theta, 1, 1, colR,h,a,b,c,d); 4036 4037 if (symbol == 'L') addText(symbol, lat, lon, theta, 0.10, 0.10, colW,h,a,b,c,d); 4038 if (symbol == 'L') addText(symbol, lat, lon, theta, 0.08, 0.08, colW,h,a,b,c,d); 4039 if (symbol == 'L') addText(symbol, lat, lon, theta, 0.06, 0.06, c
4039olW,h,a,b,c,d); 4040 if (symbol == 'L') addText(symbol, lat, lon, theta, 0.04, 0.04, colW,h,a,b,c,d); 4041 if (symbol == 'L') addText(symbol, lat, lon, theta, 0.02, 0.02, colW,h,a,b,c,d); 4042 4043 if (symbol == 'M') addText(symbol, lat, lon, theta, 0.10, 0.10, colY,h,a,b,c,d); 4044 if (symbol == 'M') addText(symbol, lat, lon, theta, 0.08, 0.08, colY,h,a,b,c,d); 4045 if (symbol == 'M') addText(symbol, lat, lon, theta, 0.06, 0.06, colY,h,a,b,c,d); 4046 if (symbol == 'M') addText(symbol, lat, lon, theta, 0.04, 0.04, colY,h,a,b,c,d); 4047 if (symbol == 'M') addText(symbol, lat, lon, theta, 0.02, 0.02, colY,h,a,b,c,d); 4048 4049 if (symbol == 'N') addText(symbol, lat, lon, theta, 0.10, 0.10, colG,h,a,b,c,d); 4050 if (symbol == 'N') addText(symbol, lat, lon, theta, 0.08, 0.08, colG,h,a,b,c,d); 4051 if (symbol == 'N') addText(symbol, lat, lon, theta, 0.06, 0.06, colG,h,a,b,c,d); 4052 if (symbol == 'N') addText(symbol, lat, lon, theta, 0.04, 0.04, colG,h,a,b,c,d); 4053 if (symbol == 'N') addText(symbol, lat, lon, theta, 0.02, 0.02, colG,h,a,b,c,d); 4054 4055 if (symbol == 'O') addText(symbol, lat, lon, theta, 0.10, 0.10, colB,h,a,b,c,d); 4056 if (symbol == 'O') addText(symbol, lat, lon, theta, 0.08, 0.08, colB,h,a,b,c,d); 4057 if (symbol == 'O') addText(symbol, lat, lon, theta, 0.06, 0.06, colB,h,a,b,c,d); 4058 if (symbol == 'O') addText(symbol, lat, lon, theta, 0.04, 0.04, colB,h,a,b,c,d); 4059 if (symbol == 'O') addText(symbol, lat, lon, theta, 0.02, 0.02, colB,h,a,b,c,d); 4060 4061} 4062 4063function adjustHeight(lat, lon, h, lat2, lon2, hgrad1, htheta1, hgrad2, htheta2) 4064{ 4065 var h2 = h; 4066 var d2 = geogDist(lat, lon, lat2, lon2); 4067 var theta2 = geogBearing(lat, lon, lat2, lon2); 4068 while (theta2 > htheta1 + 180.0) theta2 -= 360.0; 4069 while (theta2 < htheta1 - 180.0) theta2 += 360.0; 4070 h2 += d2*hgrad1*Math.cos((theta2 - htheta1)*Math.PI/180.0); 4071 while (theta2 > htheta2 + 180.0) theta2 -= 360.0; 4072 while (theta2 < htheta2 - 180.0) theta2 += 360.0; 4073 h2 += d2*hgrad2*Math.cos((theta2 - htheta2)*Math.PI/180.0); 4074 return h2; 4075} 4076 4077function addText(text, lat, lon, theta, kx, ky, col, h = 0.0, hgrad1 = 0.0, htheta1 = 0.0, hgrad2 = 0.0, htheta2 = 0.0) 4078{ 4079 theta *= Math.PI/180.0; 4080 var deltaLat = distToDeltaLat(1, lat, lon); 4081 var deltaLon = distToDeltaLon(1, lat, lon); 4082 var cosTheta = Math.cos(theta); 4083 var sinTheta = Math.sin(theta); 4084 var dLatX = deltaLat*sinTheta*kx; 4085 var dLatY = deltaLat*cosTheta*ky; 4086 var dLonX = deltaLon*cosTheta*kx; 4087 var dLonY = -deltaLon*sinTheta*ky; 4088 var totalWidth = 0; 4089 for (var i = 0; i < text.length; ++i) 4090 { 4091 var c = text.charAt(i); 4092 if (c == "'") totalWidth += 2; 4093 else if (c == '"') totalWidth += 3; 4094 else if (c == '/') totalWidth += 4; 4095 else if (c == '.') totalWidth += 2; 4096 else totalWidth += 6; 4097 } 4098 widthSoFar = 0; 4099 for (var i = 0; i < text.length; ++i) 4100 { 4101 var c = text.charAt(i); 4102 var xyCoords1 = []; 4103 var xyCoords2 = []; 4104 var xyCoords3 = []; 4105 if (c == '1') 4106 { 4107 xyCoords1.push(1); xyCoords1.push(0); 4108 xyCoords1.push(4); xyCoords1.push(0); 4109 xyCoords2.push(3); xyCoords2.push(0); 4110 xyCoords2.push(3); xyCoords2.push(6); 4111 xyCoords2.push(1); xyCoords2.push(4); 4112 } 4113 else if (c == '2') 4114 { 4115 xyCoords1.push(4); xyCoords1.push(1); 4116 xyCoords1.push(4); xyCoords1.push(0); 4117 xyCoords1.push(0); xyCoords1.push(0); 4118 xyCoords1.push(4); xyCoords1.push(4); 4119 xyCoords1.push(4); xyCoords1.push(5); 4120 xyCoords1.push(3); xyCoords1.push(6); 4121 xyCoords1.push(1); xyCoords1.push(6); 4122 xyCoords1.push(0); xyCoords1.push(5); 4123 } 4124 else if (c == '3') 4125 { 4126 xyCoords1.push(0); xyCoords1.push(1); 4127 xyCoords1.push(1); xyCoords1.push(0); 4128 xyCoords1.push(3); xyCoords1.push(0); 4129 xyCoords1.push(4); xyCoords1.push(1); 4130 xyCoords1.push(4); xyCoords1.push(2); 4131 xyCoords1.push(3); xyCoords1.push(3); 4132 xyCoords1.push(1); xyCoords1.push(3); 4133 xyCoords2.push(3); xyCoords2.push(3); 4134 xyCoords2.push(4); xyCoords2.push(4); 4135 xyCoords2.push(4); xyCoords2.push(5); 4136 xyCoords2.push(3); xyCoords2.push(6); 4137 xyCoords2.push(1); xyCoords2.push(6); 4138 xyCoords2.push(0); xyCoords2.push(5); 4139 } 4140 else if (c == '4') 4141 { 4142 xyCoords1.push(3); xyCoords1.push(0); 4143 xyCoords1.push(3); xyCoords1.push(6); 4144 xyCoords1.push(0); xyCoords1.push(2); 4145 xyCoords1.push(4); xyCoords1.push(2); 4146 } 4147 else if (c == '5') 4148 { 4149 xyCoords1.push(4); xyCoords1.push(6); 4150 xyCoords1.push(0); xyCoords1.push(6); 4151 xyCoords1.push(0); xyCoords1.push(4); 4152 xyCoords1.push(3); xyCoords1.push(4); 4153 xyCoords1.push(4); xyCoords1.push(3); 4154 xyCoords1.push(4); xyCoords1.push(1); 4155 xyCoords1.push(3); xyCoords1.push(0); 4156 xyCoords1.push(0); xyCoords1.push(0); 4157 xyCoords1.push(0); xyCoords1.push(1); 4158 } 4159 else if (c == '6') 4160 { 4161 xyCoords1.push(4); xyCoords1.push(6); 4162 xyCoords1.push(1); xyCoords1.push(6); 4163 xyCoords1.push(0); xyCoords1.push(5); 4164 xyCoords1.push(0); xyCoords1.push(1); 4165 xyCoords1.push(1); xyCoords1.push(0); 4166 xyCoords1.push(3); xyCoords1.push(0); 4167 xyCoords1.push(4); xyCoords1.push(1); 4168 xyCoords1.push(4); xyCoords1.push(3); 4169 xyCoords1.push(3); xyCoords1.push(4); 4170 xyCoords1.push(1); xyCoords1.push(4); 4171 xyCoords1.push(0); xyCoords1.push(3); 4172 } 4173 else if (c == '7') 4174 { 4175 xyCoords1.push(2); xyCoords1.push(0); 4176 xyCoords1.push(2); xyCoords1.push(2); 4177 xyCoords1.push(4); xyCoords1.push(6); 4178 xyCoords1.push(0); xyCoords1.push(6); 4179 xyCoords1.push(0); xyCoords1.push(5); 4180 } 4181 else if (c == '8') 4182 { 4183 xyCoords1.push(1); xyCoords1.push(3); 4184 xyCoords1.push(3); xyCoords1.push(3); 4185 xyCoords1.push(4); xyCoords1.push(4); 4186 xyCoords1.push(4); xyCoords1.push(5); 4187 xyCoords1.push(3); xyCoords1.push(6); 4188 xyCoords1.push(1); xyCoords1.push(6); 4189 xyCoords1.push(0); xyCoords1.push(5); 4190 xyCoords1.push(0); xyCoords1.push(4); 4191 xyCoords1.push(1); xyCoords1.push(3); 4192 xyCoords1.push(0); xyCoords1.push(2); 4193 xyCoords1.push(0); xyCoords1.push(1); 4194 xyCoords1.push(1); xyCoords1.push(0); 4195 xyCoords1.push(3); xyCoords1.push(0); 4196 xyCoords1.push(4); xyCoords1.push(1); 4197 xyCoords1.push(4); xyCoords1.push(2); 4198 xyCoords1.push(3); xyCoords1.push(3); 4199 } 4200 else if (c == '9') 4201 { 4202 xyCoords1.push(0); xyCoords1.push(0); 4203 xyCoords1.push(3); xyCoords1.push(0); 4204 xyCoords1.push(4); xyCoords1.push(1); 4205 xyCoords1.push(4); xyCoords1.push(5); 4206 xyCoords1.push(3); xyCoords1.push(6); 4207 xyCoords1.push(1); xyCoords1.push(6); 4208 xyCoords1.push(0); xyCoords1.push(5); 4209 xyCoords1.push(0); xyCoords1.push(3); 4210 xyCoords1.push(1); xyCoords1.push(2); 4211 xyCoords1.push(3); xyCoords1.push(2); 4212 xyCoords1.push(4); xyCoords1.push(3); 4213 } 4214 else if (c == '0') 4215 { 4216 xyCoords1.push(1); xyCoords1.push(0); 4217 xyCoords1.push(3); xyCoords1.push(0); 4218 xyCoords1.push(4); xyCoords1.push(1); 4219 xyCoords1.push(4); xyCoords1.push(5); 4220 xyCoords1.push(3); xyCoords1.push(6); 4221 xyCoords1.push(1); xyCoords1.push(6); 4222 xyCoords1.push(0); xyCoords1.push(5); 4223 xyCoords1.push(0); xyCoords1.push(1); 4224 xyCoords1.push(1); xyCoords1.push(0); 4225 } 4226 else if (c == 'A') 4227 { 4228 xyCoords1.push(-0.5); xyCoords1.push(0); 4229 xyCoords1.push(2); xyCoords1.push(6); 4230 xyCoords1.push(4.5); xyCoords1.push(0); 4231 xyCoords2.push(0.5); xyCoords2.push(2.5); 4232 xyCoords2.push(3.5); xyCoords2.push(2.5); 4233 } 4234 else if (c == 'B') 4235 { 4236 xyCoords1.push(0); xyCoords1.push(0); 4237 xyCoords1.push(0); xyCoords1.push(6); 4238 xyCoords1.push(3); xyCoords1.push(6); 4239 xyCoords1.push(4); xyCoords1.push(5); 4240 xyCoords1.push(4); xyCoords1.push(4); 4241 xyCoords1.push(3); xyCoords1.push(3); 4242 xyCoords1.push(4); xyCoords1.push(2); 4243 xyCoords1.push(4); xyCoords1.push(1); 4244 xyCoords1.push(3); xyCoords1.push(0); 4245 xyCoords1.push(0); xyCoords1.push(0); 4246 xyCoords2.push(0); xyCoords2.push(3); 4247 xyCoords2.push(3); xyCoords2.push(3); 4248 } 4249 else if (c == 'C') 4250 { 4251 xyCoords1.push(4); xyCoords1.push(1); 4252 xyCoords1.push(3); xyCoords1.push(0); 4253 xyCoords1.push(1); xyCoords1.push(0); 4254 xyCoords1.push(0); xyCoords1.push(1); 4255 xyCoords1.push(0); xyCoords1.push(5); 4256 xyCoords1.push(1); xyCoords1.push(6); 4257 xyCoords1.push(3); xyCoords1.push(6); 4258 xyCoords1.push(4); xyCoords1.push(5); 4259 } 4260 else if (c == 'c') 4261 { 4262 xyCoords1.push(4); xyCoords1.push(1); 4263 xyCoords1.push(3); xyCoords1.push(0); 4264 xyCoords1.push(1); xyCoords1.push(0); 4265 xyCoords1.push(0); xyCoords1.push(1); 4266 xyCoords1.push(0); xyCoords1.push(3); 4267 xyCoords1.push(1); xyCoords1.push(4); 4268 xyCoords1.push(3); xyCoords1.push(4); 4269 xyCoords1.push(4); xyCoords1.push(3); 4270 } 4271 else if (c == 'D') 4272 { 4273 xyCoords1.push(0); xyCoords1.push(0); 4274 xyCoords1.push(0); xyCoords1.push(6); 4275 xyCoords1.push(3); xyCoords1.push(6); 4276 xyCoords1.push(4); xyCoords1.push(5); 4277 xyCoords1.push(4); xyCoords1.push(1); 4278 xyCoords1.push(3); xyCoords1.push(0); 4279 xyCoords1.push(0); xyCoords1.push(0); 4280 } 4281 else if (c == 'E') 4282 { 4283 xyCoords1.push(4); xyCoords1.push(0); 4284 xyCoords1.push(0); xyCoords1.push(0); 4285 xyCoords1.push(0); xyCoords1.push(6); 4286 xyCoords1.push(4); xyCoords1.push(6); 4287 xyCoords2.push(0); xyCoords2.push(3); 4288 xyCoords2.push(4); xyCoords2.push(3); 4289 } 4290 else if (c == 'F') 4291 { 4292 xyCoords1.push(0); xyCoords1.push(0); 4293 xyCoords1.push(0); xyCoords1.push(6); 4294 xyCoords1.push(4); xyCoords1.push(6); 4295 xyCoords2.push(0); xyCoords2.push(3); 4296 xyCoords2.push(3.5); xyCoords2.push(3); 4297 } 4298 else if (c == 'm') 4299 { 4300 xyCoords1.push(0); xyCoords1.push(0); 4301 xyCoords1.push(0); xyCoords1.push(4); 4302 xyCoords1.push(2); xyCoords1.push(4); 4303 xyCoords1.push(2); xyCoords1.push(0); 4304 xyCoords2.push(2); xyCoords2.push(4); 4305 xyCoords2.push(4); xyCoords2.push(4); 4306 xyCoords2.push(4); xyCoords2.push(0); 4307 } 4308 else if (c == 's') 4309 { 4310 xyCoords1.push(0); xyCoords1.push(0); 4311 xyCoords1.push(4); xyCoords1.push(0); 4312 xyCoords1.push(4); xyCoords1.push(2); 4313 xyCoords1.push(0); xyCoords1.push(2); 4314 xyCoords1.push(0); xyCoords1.push(4); 4315 xyCoords1.push(4); xyCoords1.push(4); 4316 } 4317 else if (c == 'T') 4318 { 4319 xyCoords1.push(0); xyCoords1.push(2.5); 4320 xyCoords1.push(1.2); xyCoords1.push(1); 4321 xyCoords1.push(4); xyCoords1.push(5); 4322 } 4323 else if (c == 'X') 4324 { 4325 xyCoords1.push(0); xyCoords1.push(1); 4326 xyCoords1.push(4); xyCoords1.push(5); 4327 xyCoords2.push(4); xyCoords2.push(1); 4328 xyCoords2.push(0); xyCoords2.push(5); 4329 } 4330 else if (c == '/') 4331 { 4332 xyCoords1.push(0); xyCoords1.push(0); 4333 xyCoords1.push(3); xyCoords1.push(6); 4334 } 4335 else if (c == '%') 4336 { 4337 xyCoords1.push(0); xyCoords1.push(0); 4338 xyCoords1.push(4); xyCoords1.push(6); 4339 xyCoords2.push(0); xyCoords2.push(6); 4340 xyCoords2.push(0); xyCoords2.push(4); 4341 xyCoords2.push(2); xyCoords2.push(4); 4342 xyCoords2.push(2); xyCoords2.push(6); 4343 xyCoords2.push(0); xyCoords2.push(6); 4344 xyCoords3.push(2); xyCoords3.push(2); 4345 xyCoords3.push(2); xyCoords3.push(0); 4346 xyCoords3.push(4); xyCoords3.push(0); 4347 xyCoords3.push(4); xyCoords3.push(2); 4348 xyCoords3.push(2); xyCoords3.push(2); 4349 } 4350 else if (c == '+') 4351 { 4352 xyCoords1.push(0); xyCoords1.push(3); 4353 xyCoords1.push(4); xyCoords1.push(3); 4354 xyCoords2.push(2); xyCoords2.push(1); 4355 xyCoords2.push(2); xyCoords2.push(5); 4356 } 4357 else if (c == '"') 4358 { 4359 xyCoords1.push(0); xyCoords1.push(5); 4360 xyCoords1.push(0); xyCoords1.push(6); 4361 xyCoords2.push(2); xyCoords2.push(5); 4362 xyCoords2.push(2); xyCoords2.push(6); 4363 } 4364 else if (c == "'") 4365 { 4366 xyCoords1.push(0); xyCoords1.push(5); 4367 xyCoords1.push(0); xyCoords1.push(6); 4368 } 4369 else if (c == '.') 4370 { 4371 xyCoords1.push(0); xyCoords1.push(0); 4372 xyCoords1.push(0); xyCoords1.push(1); 4373 } 4374 else 4375 { 4376 xyCoords1.push(1); xyCoords1.push(1); 4377 xyCoords1.push(3); xyCoords1.push(1); 4378 xyCoords1.push(4); xyCoords1.push(2); 4379 xyCoords1.push(4); xyCoords1.push(4); 4380 xyCoords1.push(3); xyCoords1.push(5); 4381 xyCoords1.push(1); xyCoords1.push(5); 4382 xyCoords1.push(0); xyCoords1.push(4); 4383 xyCoords1.push(0); xyCoords1.push(2); 4384 xyCoords1.push(1); xyCoords1.push(1); 4385 } 4386 if (xyCoords1.length > 0) 4387 { 4388 var coords = []; 4389 for (var j = 0; j < xyCoords1.length; j += 2) 4390 { 4391 var x = xyCoords1[j] - totalWidth/2 + widthSoFar + 1; 4392 var y = xyCoords1[j+1] - 3;
4393 var lat2 = lat + x*dLatX + y*dLatY; 4394 var lon2 = lon + x*dLonX + y*dLonY; 4395 var h2 = adjustHeight(lat, lon, h, lat2, lon2, hgrad1, htheta1, hgrad2, htheta2); 4396 coords.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 4397 } 4398 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: col } }); 4399 mapLines2.push(line); 4400 } 4401 if (xyCoords2.length > 0) 4402 { 4403 var coords = []; 4404 for (var j = 0; j < xyCoords2.length; j += 2) 4405 { 4406 var x = xyCoords2[j] - totalWidth/2 + widthSoFar + 1; 4407 var y = xyCoords2[j+1] - 3; 4408 var lat2 = lat + x*dLatX + y*dLatY; 4409 var lon2 = lon + x*dLonX + y*dLonY; 4410 var h2 = adjustHeight(lat, lon, h, lat2, lon2, hgrad1, htheta1, hgrad2, htheta2); 4411 coords.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 4412 } 4413 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: col } }); 4414 mapLines2.push(line); 4415 } 4416 if (xyCoords3.length > 0) 4417 { 4418 var coords = []; 4419 for (var j = 0; j < xyCoords3.length; j += 2) 4420 { 4421 var x = xyCoords3[j] - totalWidth/2 + widthSoFar + 1; 4422 var y = xyCoords3[j+1] - 3; 4423 var lat2 = lat + x*dLatX + y*dLatY; 4424 var lon2 = lon + x*dLonX + y*dLonY; 4425 var h2 = adjustHeight(lat, lon, h, lat2, lon2, hgrad1, htheta1, hgrad2, htheta2); 4426 coords.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 4427 } 4428 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: col } }); 4429 mapLines2.push(line); 4430 } 4431 if (c == "'") widthSoFar += 2; 4432 else if (c == '"') widthSoFar += 3; 4433 else if (c == '/') widthSoFar += 4; 4434 else if (c == '.') widthSoFar += 2; 4435 else widthSoFar += 6; 4436 } 4437} 4438 4439function distanceHandler() 4440{ 4441 if (mode == 6) 4442 { 4443 setMode(0); 4444 } 4445 else 4446 { 4447 distLat1 = 0; 4448 distLon1 = 0; 4449 distLat2 = 0; 4450 distLon2 = 0; 4451 if (mapMode == 2 && distCount == 0) 4452 { 4453 var courseID = currentCourseID(); 4454 var hole = currentHole(); 4455 var n = getCourseHoleMeasures(courseID, hole); 4456 if (n > 0) 4457 { 4458 setMode(0); 4459 for (var i = 0; i < n; ++i) 4460 { 4461 var x = decodeMeasure(getCourseHoleMeasure(courseID, hole, i)); 4462 addDist(x.lat1, x.lon1, x.lat2, x.lon2); 4463 } 4464 return; 4465 } 4466 } 4467 setMode(6); 4468 setDistDiv("Click Start"); 4469 } 4470} 4471 4472function distanceHandler2() 4473{ 4474 if (mode == 7) 4475 { 4476 setMode(0); 4477 } 4478 else 4479 { 4480 var courseID = currentCourseID(); 4481 var hole = currentHole(); 4482 if (hole < 1) 4483 { 4484 alert("Please select hole."); 4485 setMode(0); 4486 return; 4487 } 4488 distLat1 = getCourseHoleTeeLat(courseID, hole); 4489 distLon1 = getCourseHoleTeeLon(courseID, hole); 4490 if (distLat1 == 0 && distLon1 == 0) 4491 { 4492 alert("Please set tee position."); 4493 setMode(0); 4494 return; 4495 } 4496 distLat2 = 0; 4497 distLon2 = 0; 4498 if (mapMode == 2 && distCount == 0) 4499 { 4500 var n = getCourseHoleMeasures(courseID, hole); 4501 if (n > 0) 4502 { 4503 setMode(0); 4504 for (var i = 0; i < n; ++i) 4505 { 4506 var x = decodeMeasure(getCourseHoleMeasure(courseID, hole, i)); 4507 addDist(x.lat1, x.lon1, x.lat2, x.lon2); 4508 } 4509 return; 4510 } 4511 } 4512 setMode(7); 4513 setDistDiv("Click Map"); 4514 } 4515} 4516 4517function clearDistanceHandler() 4518{ 4519 if (mode == 8 || distCount == 0) 4520 { 4521 setMode(0); 4522 } 4523 else 4524 { 4525 setMode(8); 4526 setDistDiv("Click Map"); 4527 } 4528} 4529 4530function clearDispersionHandler() 4531{ 4532 setMode(0); 4533 if (greenSlopesCount > 0 && puttHistoryOp.length > 0) 4534 { 4535 if (showPuttDispersion != 0) 4536 { 4537 showPuttDispersion = 0; 4538 selectGreenSpeedHandler(); 4539 } 4540 return; 4541 } 4542 removeDispersionFeatures(); 4543} 4544 4545function clearSpreadHandler() 4546{ 4547 setMode(0); 4548 if (greenSlopesCount > 0 && puttHistoryOp.length > 0) 4549 { 4550 if (showPuttDispersion != 0) 4551 { 4552 showPuttDispersion = 0; 4553 selectGreenSpeedHandler(); 4554 } 4555 return; 4556 } 4557 removeDispersionFeatures(); 4558} 4559 4560function zoomToCourseHole(courseID, hole, clear, refresh, durationSeconds) 4561{ 4562 if (clear) 4563 removeAllFeatures(); 4564 4565 var x = document.getElementById('mapdiv'); 4566 var mw = x.clientWidth; 4567 var mh = x.clientHeight; 4568 if (mw < 100) mw = 100; 4569 if (mh < 100) mh = 100; 4570 4571 var t = mw/mh - 1; 4572 var height = (50 - t*10) * heightMultiplier; 4573 var offset = (45 + (t > 0 ? t*35 : t*10)) * offsetMultiplier; 4574 var angle = (-25 + t*10) + angleIncrement; 4575 4576 if (height < 2.0) height = 2.0; 4577 if (offset < 5.0) offset = 5.0; 4578 if (angle > -5.0) angle = -5.0; 4579 if (angle < -90.0) angle = -90.0; 4580 4581 var lat = 0; 4582 var lon = 0; 4583 var hlat = 0; 4584 var hlon = 0; 4585 var heading = 0; 4586 4587 if (hole == 0) 4588 { 4589 var lat1 = getCourseMinLat(courseID); 4590 var lon1 = getCourseMinLon(courseID); 4591 var lat2 = getCourseMaxLat(courseID); 4592 var lon2 = getCourseMaxLon(courseID); 4593 if (lat1 != 0 || lon1 != 0) 4594 { 4595 if (lat2 <= lat1) lat2 = lat1 + 0.0001; 4596 while (lon2 < lon1 - 180) lon2 += 360; 4597 while (lon2 > lon1 + 180) lon2 -= 360; 4598 lat = (lat1 + lat2)/2; 4599 lon = (lon1 + lon2)/2; 4600 hlat = lat; 4601 hlon = lon; 4602 var d1 = geogDist(lat1, lon1, lat2, lon2); 4603 var d2 = geogDist(lat, lon, lat + 1, lon); 4604 if (mapMode == 2) 4605 { 4606 lat -= 3000 * (d1/4000) / d2; 4607 height = 1000 * (d1/4000); 4608 angle = -22; 4609 } 4610 else 4611 {
4612 height = 1.0*d1; 4613 heading = 0; 4614 angle = -90; 4615 } 4616 } 4617 } 4618 else if (hole > 0) 4619 { 4620 var par = getCourseHolePar(courseID, hole); 4621 if (par >= 3) 4622 { 4623 if (mapMode == 1) 4624 { 4625 var lat1 = getCourseHoleTeeLat(courseID, hole); 4626 var lon1 = getCourseHoleTeeLon(courseID, hole); 4627 var lat2 = getCourseHolePinLat(courseID, hole); 4628 var lon2 = getCourseHolePinLon(courseID, hole); 4629 heading = geogBearing(lat1, lon1, lat2, lon2); 4630 d = geogDist(lat1, lon1, lat2, lon2); 4631 var t = 0.5; 4632 if (d > 1) t = 0.5 * (d + 20) / d; 4633 lat = lat1 + t * (lat2 - lat1); 4634 lon = lon1 + t * (lon2 - lon1); 4635 hlat = lat; 4636 hlon = lon; 4637 height = d + 70; 4638 angle = -90; 4639 } 4640 else 4641 { 4642 lat = getCourseHoleShotLat(courseID, hole, 1, offset, offset + 30); 4643 lon = getCourseHoleShotLon(courseID, hole, 1, offset, offset + 30); 4644 hlat = getCourseHoleTeeLat(courseID, hole); 4645 hlon = getCourseHoleTeeLon(courseID, hole); 4646 heading = getCourseHoleShotBearing(courseID, hole, 1); 4647 } 4648 } 4649 else 4650 { 4651 var lat1 = getCourseHoleMinLat(courseID, hole); 4652 var lon1 = getCourseHoleMinLon(courseID, hole); 4653 var lat2 = getCourseHoleMaxLat(courseID, hole); 4654 var lon2 = getCourseHoleMaxLon(courseID, hole); 4655 if (lat1 != 0 || lon1 != 0) 4656 { 4657 if (lat2 <= lat1) lat2 = lat1 + 0.0001; 4658 while (lon2 < lon1 - 180) lon2 += 360; 4659 while (lon2 > lon1 + 180) lon2 -= 360; 4660 lat = (lat1 + lat2)/2; 4661 lon = (lon1 + lon2)/2; 4662 hlat = lat; 4663 hlon = lon; 4664 var d1 = geogDist(lat1, lon1, lat2, lon2); 4665 height = 1.0*d1; 4666 heading = 0; 4667 angle = -90; 4668 if (height < 200) 4669 height = 200; 4670 } 4671 } 4672 } 4673 4674 if (mapMode == 1) 4675 { 4676 var x = document.getElementById('mapdiv'); 4677 var w = window.innerWidth; 4678 var h = window.innerHeight; 4679 w = x.clientWidth; 4680 h = x.clientHeight; 4681 if (w < 100) w = 100; 4682 if (h < 100) h = 100; 4683 height *= 1.05 * Math.sqrt(w / h); 4684 if (getAutoRotate() != 0) 4685 heading = 0; 4686 } 4687 4688 if (lat != 0 || lon != 0) 4689 { 4690 flyToHeightAboveGround(lat, lon, hlat, hlon, height, heading, angle, durationSeconds) 4691 } 4692 4693 if (refresh) 4694 refreshView(); 4695} 4696 4697function zoomToCourseHoleShot(courseID, hole, shot, clear, refresh, durationSeconds) 4698{ 4699 if (hole == 0 || shot == 0) 4700 { 4701 zoomToCourseHole(courseID, hole, clear, refresh, durationSeconds); 4702 return; 4703 } 4704 if (clear) 4705 removeAllFeatures(); 4706 4707 var x = document.getElementById('mapdiv'); 4708 var mw = x.clientWidth; 4709 var mh = x.clientHeight; 4710 if (mw < 100) mw = 100; 4711 if (mh < 100) mh = 100; 4712 4713 var t = mw/mh - 1; 4714 var height = (50 - t*10) * heightMultiplier; 4715 var offset = ((shot <= 1 ? 45 : 50) + (t > 0 ? t*35 : t*10)) * offsetMultiplier; 4716 var angle = (-25 + t*10) + angleIncrement; 4717 4718 if (shot == 1) offset -= 5.0; 4719 if (height < 2.0) height = 2.0; 4720 if (offset < 5.0) offset = 5.0; 4721 if (angle > -5.0) angle = -5.0; 4722 if (angle < -90.0) angle = -90.0; 4723 4724 height *= 0.50; 4725 offset *= 0.65; 4726 angle *= 0.90; 4727 4728 var lat = 0; 4729 var lon = 0; 4730 var hlat = 0; 4731 var hlon = 0; 4732 var heading = 0; 4733 4734 var par = getCourseHolePar(courseID, hole); 4735 4736 if (par >= 3) 4737 { 4738 if (mapMode == 1) 4739 { 4740 var lat1 = getCourseHoleTeeLat(courseID, hole); 4741 var lon1 = getCourseHoleTeeLon(courseID, hole); 4742 var lat2 = getCourseHolePinLat(courseID, hole); 4743 var lon2 = getCourseHolePinLon(courseID, hole); 4744 heading = geogBearing(lat1, lon1, lat2, lon2); 4745 if (shot > 0) 4746 { 4747 if (shot == 1 && par > 3) 4748 { 4749 lat2 = getCourseHoleTargetLat(courseID, hole, shot); 4750 lon2 = getCourseHoleTargetLon(courseID, hole, shot); 4751 } 4752 else if (shot >= 2 && shot <= par - 2) 4753 { 4754 lat1 = getCourseHoleTargetLat(courseID, hole, shot - 1); 4755 lon1 = getCourseHoleTargetLon(courseID, hole, shot - 1); 4756 } 4757 else if (shot > par - 2) 4758 { 4759 lat1 = getCourseHolePinLat(courseID, hole); 4760 lon1 = getCourseHolePinLon(courseID, hole); 4761 } 4762 } 4763 var d = geogDist(lat1, lon1, lat2, lon2); 4764 var t = 0.5; 4765 if (shot <= 1 && d > 1) t = 0.5 * (d + 20) / d; 4766 lat = lat1 + t * (lat2 - lat1); 4767 lon = lon1 + t * (lon2 - lon1); 4768 hlat = lat; 4769 hlon = lon; 4770 height = d + 70; 4771 angle = -90; 4772 } 4773 else 4774 { 4775 lat = getCourseHoleShotLat(courseID, hole, shot, offset, offset + 30); 4776 lon = getCourseHoleShotLon(courseID, hole, shot, offset, offset + 30); 4777 heading = getCourseHoleShotBearing(courseID, hole, shot); 4778 if (shot <= 1) 4779 { 4780 hlat = getCourseHoleTeeLat(courseID, hole); 4781 hlon = getCourseHoleTeeLon(courseID, hole); 4782 } 4783 else if (shot <= par - 2) 4784 { 4785 hlat = getCourseHoleTargetLat(courseID, hole, shot - 1); 4786 hlon = getCourseHoleTargetLon(courseID, hole, shot - 1); 4787 } 4788 else 4789 {
4790 hlat = getCourseHolePinLat(courseID, hole); 4791 hlon = getCourseHolePinLon(courseID, hole); 4792 } 4793 } 4794 } 4795 4796 if (mapMode == 1) 4797 { 4798 var x = document.getElementById('mapdiv'); 4799 var w = window.innerWidth; 4800 var h = window.innerHeight; 4801 w = x.clientWidth; 4802 h = x.clientHeight; 4803 if (w < 100) w = 100; 4804 if (h < 100) h = 100; 4805 height *= 1.05 * Math.sqrt(w / h); 4806 if (getAutoRotate() != 0) 4807 heading = 0; 4808 } 4809 4810 if (lat != 0 || lon != 0) 4811 { 4812 flyToHeightAboveGround(lat, lon, hlat, hlon, height, heading, angle, durationSeconds) 4813 } 4814 4815 if (refresh) 4816 refreshView(); 4817} 4818 4819function addHoleMarker(lat, lon) 4820{ 4821 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 4822 var marker = viewer.entities.add({ 4823 position : Cesium.Cartesian3.fromDegrees(lon, lat, h + 0.005), 4824 cylinder : { 4825 length: 0.01, 4826 topRadius: 0.05, 4827 bottomRadius: 0.05, 4828 material: Cesium.Color.WHITE, 4829 outline: true, 4830 outlineColor: Cesium.Color.WHITE 4831 } 4832 }); 4833 mapMarkers2.push(marker); 4834} 4835 4836function addBallMarker(lat, lon) 4837{ 4838 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 4839 var marker = viewer.entities.add({ 4840 position : Cesium.Cartesian3.fromDegrees(lon, lat, h + 0.005), 4841 cylinder : { 4842 length: 0.04, 4843 topRadius: 0.02, 4844 bottomRadius: 0.02, 4845 material: Cesium.Color.WHITE, 4846 outline: true, 4847 outlineColor: Cesium.Color.WHITE 4848 } 4849 }); 4850 mapMarkers2.push(marker); 4851} 4852 4853function addDispersionMarker(lat, lon) 4854{ 4855 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 4856 var marker = viewer.entities.add({ 4857 position : Cesium.Cartesian3.fromDegrees(lon, lat, h + 0.05), 4858 cylinder : { 4859 length: 0.1, 4860 topRadius: 0.05, 4861 bottomRadius: 0.05, 4862 material: Cesium.Color.WHITE, 4863 outline: true, 4864 outlineColor: Cesium.Color.WHITE 4865 } 4866 }); 4867 mapMarkers2.push(marker); 4868} 4869 4870function addMarker(lat, lon, addYellowCircles, addWhiteCircles, addTargetCircles, numTargetCircles, bearing) 4871{ 4872 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 4873 var marker = viewer.entities.add({ 4874 position : Cesium.Cartesian3.fromDegrees(lon, lat, h + 0.9), 4875 cylinder : { 4876 length: 1.8, 4877 topRadius: 0.1, 4878 bottomRadius: 0.1, 4879 material: Cesium.Color.WHITE, 4880 outline: true, 4881 outlineColor: Cesium.Color.WHITE 4882 } 4883 }); 4884 mapMarkers.push(marker); 4885 if (mapMode == 1) 4886 { 4887 var x = myUnitsToMetres(1); 4888 var c = getCircleColor(); 4889 if (addYellowCircles) 4890 { 4891 var y = getCircleBrightness() / 10.0; 4892 var y0 = (y > 0 ? y*0.75+0.25 : 0.0); 4893 var c0 = new Cesium.Color(1, 1, 0, y0); 4894 var c1 = new Cesium.Color(1, 1, 0, y*0.5); 4895 var c2 = new Cesium.Color(1, 1, 0, y*0.2); 4896 var c3 = new Cesium.Color(1, 1, 0, y*0.15); 4897 var c4 = new Cesium.Color(1, 1, 0, y*0.12); 4898 if (c == 1) 4899 { 4900 c0 = new Cesium.Color(0.5, 1, 0.5, y0); 4901 c1 = new Cesium.Color(0.5, 1, 0.5, y*0.5); 4902 c2 = new Cesium.Color(0.5, 1, 0.5, y*0.2); 4903 c3 = new Cesium.Color(0.5, 1, 0.5, y*0.15); 4904 c4 = new Cesium.Color(0.5, 1, 0.5, y*0.12); 4905 } 4906 if (c == 2) 4907 { 4908 c0 = new Cesium.Color(0.5, 1, 1, y0); 4909 c1 = new Cesium.Color(0.5, 1, 1, y*0.5); 4910 c2 = new Cesium.Color(0.5, 1, 1, y*0.2); 4911 c3 = new Cesium.Color(0.5, 1, 1, y*0.15); 4912 c4 = new Cesium.Color(0.5, 1, 1, y*0.12); 4913 } 4914 if (c == 3) 4915 { 4916 c0 = new Cesium.Color(1, 1, 1, y0); 4917 c1 = new Cesium.Color(1, 1, 1, y*0.5); 4918 c2 = new Cesium.Color(1, 1, 1, y*0.2); 4919 c3 = new Cesium.Color(1, 1, 1, y*0.15); 4920 c4 = new Cesium.Color(1, 1, 1, y*0.12); 4921 } 4922 var i = getTeeCircles(); 4923 if (i <= 6) 4924 { 4925 addCircle(lat, lon, x*200, c1); 4926 addCircle(lat, lon, x*(250-5*i), c2); 4927 addCircle(lat, lon, x*(300-10*i), c1); 4928 } 4929 else if (i <= 10) 4930 { 4931 addCircle(lat, lon, x*(260-10*i), c1); 4932 addCircle(lat, lon, x*(280-10*i), c2); 4933 addCircle(lat, lon, x*(300-10*i), c1); 4934 } 4935 else 4936 { 4937 var j = 0; 4938 var n = 0; 4939 var m = 3 + i - 11; 4940 while (n < m && j <= 16) 4941 { 4942 if (getClubFlag(j)) 4943 { 4944 n += 1; 4945 var d = getClubTotal(j); 4946 addCircle(lat, lon, x*d, (n == 1 ? c1 : (n == 2 ? c2 : (n == 3 ? c3 : c4)))); 4947 if (bearing > -9999) 4948 { 4949 var lat2 = moveLat(lat, lon, x*d - 3, bearing + 10); 4950 var lon2 = moveLon(lat, lon, x*d - 3, bearing + 10); 4951 var hg2 = getProVisualizerTerrainHeightAndGradient(lat2, lon2, 2.0, 0.0, 90.0); 4952 addText(Math.round(d).toString(), lat2, lon2, -bearing - 10, 0.4, 0.4, c0, hg2.h, hg2.g1, 0.0, hg2.g2, 90.0); 4953 var lat3 = moveLat(lat, lon, x*d - 3, bearing - 10); 4954 var lon3 = moveLon(lat, lon, x*d - 3, bearing - 10); 4955 var hg3 = getProVisualizerTerrainHeightAndGradient(lat3, lon3, 2.0, 0.0, 90.0); 4956 addText(Math.round(d).toString(), lat3, lon3, -bearing + 10, 0.4, 0.4, c0, hg3.h, hg3.g1, 0.0, hg3.g2, 90.0); 4957 } 4958 } 4959 j += 1; 4960 } 4961 } 4962 } 4963 if (addWhiteCircles) 4964 { 4965 var y = getCircleBrightness() / 15.0; 4966 var c1 = new Cesium.Color(1, 1, 1, y*0.5); 4967 var c2 = new Cesium.Color(1, 1, 1, y*0.2); 4968 var c3 = new Cesium.Color(1, 1, 1, y*0.1); 4969 addCircle(lat, lon, x*10, c1); 4970 addCircle(lat, lon, x*50, c2); 4971 addCircle(lat, lon, x*100, c1); 4972 addCircle(lat, lon, x*150, c2); 4973 addCircle(lat, lon, x*200, c1); 4974 addCircle(lat, lon, x*250, c2); 4975 4976 if (currentShot() >= 0) 4977 { 4978 addCircle(lat, lon, x*20, c3); 4979 addCircle(lat, lon, x*30, c3); 4980 addCircle(lat, lon, x*40, c3); 4981 4982 addCircle(lat, lon, x*60, c3); 4983 addCircle(lat, lon, x*70, c3); 4984 addCircle(lat, lon, x*80, c3); 4985 addCircle(lat, lon, x*90, c3); 4986 4987 addCircle(lat, lon, x*110, c3); 4988 addCircle(lat, lon, x*120, c3); 4989 addCircle(lat, lon, x*130, c3); 4990 addCircle(lat, lon, x*140, c3); 4991 4992 addCircle(lat, lon, x*160, c3); 4993 addCircle(lat, lon, x*170, c3); 4994 addCircle(lat, lon, x*180, c3); 4995 addCircle(lat, lon, x*190, c3); 4996 4997 addCircle(lat, lon, x*210, c3); 4998 addCircle(lat, lon, x*220, c3); 4999 addCircle(lat, lon, x*230, c3); 5000 addCircle(lat, lon, x*240, c3); 5001 } 5002 } 5003 if (addTargetCircles) 5004 { 5005 var y = getCircleBrightness() / 15.0; 5006 var c1 = new Cesium.Color(1, 1, 1, y*0.25); 5007 var c2 = new Cesium.Color(1, 1, 1, y*0.15); 5008 var c3 = new Cesium.Color(1, 1, 1, y*0.10); 5009 if (numTargetCircles >= 1) addCircle(lat, lon, x*10, c1); 5010 if (numTargetCircles >= 2) addCircle(lat, lon, x*20, c2); 5011 if (numTargetCircles >= 3) addCircle(lat, lon, x*30, c3); 5012 } 5013 } 5014} 5015 5016function addCircle(lat, lon, radius, color) 5017{ 5018 var rLat = moveLat(lat, lon, radius, 0) - lat; 5019 var rLon = moveLon(lat, lon, radius, 90) - lon; 5020 var coords = []; 5021 var theta = 0.0; 5022 var n = 60 + Math.round(radius); 5023 var dTheta = 2*Math.PI/n; 5024 for (var i = 0; i <= n; ++i) 5025 { 5026 if (i == n) theta = 0.0; 5027 var lat2 = lat + rLat*Math.cos(theta); 5028 var lon2 = lon + rLon*Math.sin(theta); 5029 var h2 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat2, lon2)); 5030 coords.push(Cesium.Cartesian3.fromDegrees(lon2, lat2, h2)); 5031 theta += dTheta; 5032 } 5033 for (var i = 0; i < 2; ++i) 5034 { 5035 var circle = viewer.entities.add({ polyline : { positions: coords, width: 1, material: color } }); 5036 mapCircles.push(circle); 5037 } 5038} 5039 5040function addCenterLines() 5041{ 5042 var courseID = currentCourseID(); 5043 for (var i = 1; i <= 18; ++i) 5044 addCourseHoleCenterLine(courseID, i, 1, 1, 0, 1, true); 5045} 5046 5047function addCenterLine() 5048{ 5049 var courseID = currentCourseID(); 5050 var hole = currentHole(); 5051 var a = getLineBrightness() * 0.1; 5052 var c = getLineColor(); 5053 var r = 0; 5054 var g = 0; 5055 var b = 0; 5056 if (c == 1) { r = 1.0; g = 1.0; b = 1.0; } 5057 else if (c == 2) { r = 1.0; g = 1.0; b = 0.0; } 5058 else if (c == 3) { r = 0.5; g = 1.0; b = 0.5; } 5059 else if (c == 4) { r = 0.5; g = 1.0; b = 1.0; } 5060 addCourseHoleCenterLine(courseID, hole, r, g, b, a, false); 5061} 5062 5063function addCourseHoleCenterLine(courseID, hole, r, g, b, a, isOverview) 5064{ 5065 if (hole < 1) 5066 return; 5067
5068 var par = getCourseHolePar(courseID, hole); 5069 5070 if (par < 3) 5071 return; 5072 5073 var positions = []; 5074 var i = 0; 5075 5076 var lat = getCourseHoleTeeLat(courseID, hole); 5077 var lon = getCourseHoleTeeLon(courseID, hole); 5078 var h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 5079 positions[i++] = Cesium.Cartesian3.fromDegrees(lon, lat, h); 5080 5081 if (par >= 4) 5082 { 5083 lat = getCourseHoleTargetLat(courseID, hole, 1); 5084 lon = getCourseHoleTargetLon(courseID, hole, 1); 5085 h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 5086 positions[i++] = Cesium.Cartesian3.fromDegrees(lon, lat, h); 5087 } 5088 if (par >= 5) 5089 { 5090 lat = getCourseHoleTargetLat(courseID, hole, 2); 5091 lon = getCourseHoleTargetLon(courseID, hole, 2); 5092 h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 5093 positions[i++] = Cesium.Cartesian3.fromDegrees(lon, lat, h); 5094 } 5095 if (par >= 6) 5096 { 5097 lat = getCourseHoleTargetLat(courseID, hole, 3); 5098 lon = getCourseHoleTargetLon(courseID, hole, 3); 5099 h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 5100 positions[i++] = Cesium.Cartesian3.fromDegrees(lon, lat, h); 5101 } 5102 5103 lat = getCourseHolePinLat(courseID, hole); 5104 lon = getCourseHolePinLon(courseID, hole); 5105 h = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat, lon)); 5106 positions[i++] = Cesium.Cartesian3.fromDegrees(lon, lat, h); 5107 5108 if (isOverview) 5109 { 5110 var x = (positions[0].x + positions[1].x)/2; 5111 var y = (positions[0].y + positions[1].y)/2; 5112 var z = (positions[0].z + positions[1].z)/2; 5113 viewer.entities.add({ 5114 position: new Cesium.Cartesian3(x,y,z), 5115 billboard : { image: '/images/' + hole.toString() + 'y.png', eyeOffset: new Cesium.Cartesian3(0,0,-50) } 5116 }); 5117 5118 var line = viewer.entities.add({ 5119 polyline : { 5120 positions: positions, 5121 width: 7, 5122 material: new Cesium.PolylineArrowMaterialProperty(new Cesium.Color(r, g, b, a)) 5123 } 5124 }); 5125 5126 mapLines.push(line); 5127 } 5128 else 5129 { 5130 var line = viewer.entities.add({ 5131 polyline : { 5132 positions: positions, 5133 width: 1.0, 5134 material: new Cesium.Color(r, g, b, a) 5135 } 5136 }); 5137 5138 mapLines.push(line); 5139 } 5140} 5141 5142function removeAllFeatures() 5143{ 5144 viewer.entities.removeAll(); 5145 mapMarkers = []; 5146 mapMarkers2 = []; 5147 mapCircles = []; 5148 mapLines = []; 5149 mapLines2 = []; 5150 teeGridCount = 0; 5151 greenGridCount = 0; 5152 greenSlopesCount = 0; 5153 greenSlopesBearing = 0; 5154 greenContoursCount = 0; 5155 distCount = 0; 5156 coneCount = 0; 5157} 5158 5159function removeDispersionFeatures() 5160{ 5161 refreshView(); 5162} 5163 5164function clearNotes() 5165{ 5166 var x = document.getElementById('notesdiv'); 5167 x.innerHTML = ''; 5168} 5169 5170function setNotes(courseID, hole) 5171{ 5172 var s = getCourseHoleNotes(courseID, hole); 5173 s = s.replace(/z1z/g, "&"); 5174 s = s.replace(/z2z/g, "'"); 5175 s = s.replace(/z3z/g, '"'); 5176 s = s.replace(/z4z/g, '|'); 5177 var x = document.getElementById('notesdiv'); 5178 x.innerHTML = s; 5179} 5180 5181function saveNotes() 5182{ 5183 var courseID = currentCourseID(); 5184 var hole = currentHole(); 5185 var x = document.getElementById('notesdiv'); 5186 var s = x.innerHTML; 5187 s = s.replace(/&/g, "z1z"); 5188 s = s.replace(/'/g, "z2z"); 5189 s = s.replace(/"/g, "z3z"); 5190 s = s.replace(/\|/g, "z4z"); 5191 setCourseHoleNotes(courseID, hole, s); 5192} 5193 5194function refreshView() 5195{ 5196 var courseID = currentCourseID(); 5197 var hole = currentHole(); 5198 var shot = currentShot(); 5199 var par = getCourseHolePar(courseID, hole); 5200 5201 removeAllFeatures(); 5202 setDistDiv(''); 5203 clearInfo(); 5204 clearNotes(); 5205 setInfo(); 5206 setNotes(courseID, hole); 5207 5208 if (hole == 0) 5209 { 5210 addCenterLines(); 5211 } 5212 else 5213 { 5214 var teeLat = getCourseHoleTeeLat(courseID, hole); 5215 var teeLon = getCourseHoleTeeLon(courseID, hole); 5216 5217 var pinLat = getCourseHolePinLat(courseID, hole); 5218 var pinLon = getCourseHolePinLon(courseID, hole); 5219 5220 var target1Lat = getCourseHoleTargetLat(courseID, hole, 1); 5221 var target1Lon = getCourseHoleTargetLon(courseID, hole, 1); 5222 5223 var target2Lat = getCourseHoleTargetLat(courseID, hole, 2); 5224 var target2Lon = getCourseHoleTargetLon(courseID, hole, 2); 5225 5226 var target3Lat = getCourseHoleTargetLat(courseID, hole, 3); 5227 var target3Lon = getCourseHoleTargetLon(courseID, hole, 3); 5228 5229 if (mapMode == 1) 5230 addCenterLine(); 5231 5232 if (teeLat != 0 || teeLon != 0) 5233 { 5234 var bearing = -9999; 5235 if (target1Lat != 0 || target1Lon != 0) bearing = geogBearing(teeLat, teeLon, target1Lat, target1Lon); 5236 else if (pinLat != 0 || pinLon != 0) bearing = geogBearing(teeLat, teeLon, pinLat, pinLon); 5237 addMarker(teeLat, teeLon, true, false, false, 0, bearing); 5238 } 5239 5240 if (pinLat != 0 || pinLon != 0) 5241 addMarker(pinLat, pinLon, false, true, false, 0, 0); 5242 5243 if (target1Lat != 0 || target1Lon != 0) 5244 addMarker(target1Lat, target1Lon, false, false, shot >= 1, 3, 0); 5245 5246 if (target2Lat != 0 || target2Lon != 0) 5247 addMarker(target2Lat, target2Lon, false, false, shot >= 1, 1, 0); 5248 5249 if (target3Lat != 0 || target3Lon != 0) 5250 addMarker(target3Lat, target3Lon, false, false, shot >= 1, 1, 0); 5251 } 5252 if (mapMode == 1) 5253 { 5254 var n = getCourseHoleMeasures(courseID, hole); 5255 var i; 5256 for (i = 0; i < n; ++i) 5257 { 5258 var x = decodeMeasure(getCourseHoleMeasure(courseID, hole, i)); 5259 addDist(x.lat1, x.lon1, x.lat2, x.lon2); 5260 } 5261 } 5262 var n = getCourseHoleSymbols(courseID, hole); 5263 if (n > 0) 5264 { 5265 var theta = 0 - getCourseHoleBearing(courseID, hole); 5266 for (i = 0; i < n; ++i) 5267 { 5268 var x = decodeSymbol(getCourseHoleSymbol(courseID, hole, i));
5269 addSymbol(x.lat, x.lon, x.symbol, theta); 5270 } 5271 } 5272 viewTimestamp = Date.now(); 5273} 5274 5275function formatElevDiff(diff) 5276{ 5277 if (diff < 0) 5278 return "− " + Math.round(-diff).toString(); 5279 5280 return "+ " + Math.round(diff).toString(); 5281} 5282 5283function playsLikeDist(distMetres, elev1Metres, elev2Metres) 5284{ 5285 // dist + height gain - 3% for each 1000m above sea level 5286 return (distMetres + elev2Metres - elev1Metres) * (1.0 - 0.00003 * elev1Metres); 5287} 5288 5289var elevationSource = 0; 5290var elevationSourceRef = ""; 5291 5292function elevationRef() 5293{ 5294 if (elevationSource == 1) 5295 return "(<a href='https://apps.nationalmap.gov/epqs/'>EPQS</a>)"; 5296 else if (elevationSource == 2) 5297 return "(<a href='https://www.usgs.gov/centers/eros/science/usgs-eros-archive-digital-elevation-shuttle-radar-topography-mission-srtm-1-arc?qt-science_center_objects=0#qt-science_center_objects'>SRTM</a>)"; 5298 else if (elevationSource == 3) 5299 return "(<a href='https://asterweb.jpl.nasa.gov/gdem.asp'>GDEM</a>)"; 5300 else if (elevationSource == 99) 5301 return "(Mixed)"; 5302 else if (elevationSource == 100) 5303 return elevationSourceRef; 5304 return ""; 5305} 5306 5307function refreshElevation() 5308{ 5309 if (elevationTimestamp == viewTimestamp) 5310 { 5311 window.setTimeout(refreshElevation, 1000); 5312 return; 5313 } 5314 5315 var courseID = currentCourseID(); 5316 var hole = currentHole(); 5317 bestClubID[1] = -1; 5318 bestClubID[2] = -1; 5319 bestClubID[3] = -1; 5320 bestClubID[4] = -1; 5321 5322 if (hole == 0) 5323 { 5324 var par = getCoursePar(courseID); 5325 var len = getCourseLength(courseID); 5326 if (par == 0 || len == 0) 5327 { 5328 setHelpInfo(); 5329 } 5330 else 5331 { 5332 var frontNineDesc = ""; 5333 var frontNinePar = 0; 5334 var frontNineLen = 0; 5335 var backNineDesc = ""; 5336 var backNinePar = 0; 5337 var backNineLen = 0; 5338 for (var hole = 1; hole <= 18; ++hole) 5339 { 5340 var par = getCourseHolePar(courseID, hole); 5341 var len = getCourseHoleLength(courseID, hole); 5342 var desc = ""; 5343 if (par >= 3) desc += par.toString(); 5344 else desc += "−"; 5345 desc += " "; 5346 if (hole <= 9) 5347 { 5348 frontNineDesc += desc; 5349 frontNinePar += par; 5350 frontNineLen += len; 5351 } 5352 else 5353 { 5354 backNineDesc += desc; 5355 backNinePar += par; 5356 backNineLen += len; 5357 } 5358 } 5359 if (frontNinePar > 0) 5360 { 5361 frontNineDesc += "= " + frontNinePar.toString() + " = " + Math.round(frontNineLen).toString(); 5362 setInfoDiv2(frontNineDesc); 5363 } 5364 else 5365 { 5366 setInfoDiv2(" "); 5367 } 5368 if (backNinePar > 0) 5369 { 5370 backNineDesc += "= " + backNinePar.toString() + " = " + Math.round(backNineLen).toString(); 5371 setInfoDiv3(backNineDesc); 5372 } 5373 else 5374 { 5375 setInfoDiv3(" "); 5376 } 5377 setInfoDiv4(" "); 5378 } 5379 elevationTimestamp = viewTimestamp; 5380 window.setTimeout(refreshElevation, 1000); 5381 return; 5382 } 5383 5384 var par = getCourseHolePar(courseID, hole); 5385 5386 if (par < 3) 5387 { 5388 elevationTimestamp = viewTimestamp; 5389 window.setTimeout(refreshElevation, 1000); 5390 return; 5391 } 5392 5393 if (par == 3) 5394 { 5395 var lat1 = getCourseHoleTeeLat(courseID, hole); 5396 var lon1 = getCourseHoleTeeLon(courseID, hole); 5397 var elev1 = lookupElevation(lat1, lon1); 5398 5399 var lat2 = getCourseHolePinLat(courseID, hole); 5400 var lon2 = getCourseHolePinLon(courseID, hole); 5401 var elev2 = lookupElevation(lat2, lon2); 5402 5403 if (elev1 == -1000000 || elev2 == -1000000) 5404 { 5405 var dist1 = geogDist(lat1, lon1, lat2, lon2); 5406 setInfoDiv2("Elevation: Not available"); 5407 setInfoDiv3("Plays Like: Not available"); 5408 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5409 setInfoDiv4("" 5410 + name1 + " " + perc1.toString() + "%"); 5411 elevationTimestamp = viewTimestamp; 5412 bestClubID[1] = club1; 5413 } 5414 else if (elev1 == -999999 || elev2 == -999999) 5415 { 5416 setInfoDiv2(" ");//"Elevation: Fetching..."); 5417 setInfoDiv3(" ");//Plays Like:"); 5418 } 5419 else 5420 { 5421 if (elev1 < elev2 - 6) elev1 += 3; else if (elev1 < elev2) elev1 += 0.5 * (elev2 - elev1); 5422 if (elev2 < elev1 - 4) elev2 += 2; else if (elev2 < elev1) elev2 += 0.5 * (elev1 - elev2); 5423 if (elev2 > elev1 + 4) elev2 -= 2; else if (elev2 > elev1) elev2 += 0.5 * (elev1 - elev2); 5424 var dist1 = playsLikeDist(geogDist(lat1, lon1, lat2, lon2), elev1, elev2); 5425 setInfoDiv2("Elevation: " 5426 + Math.round(elev1).toString() + " " 5427 + formatElevDiff(elev2-elev1) + " metres " 5428 + elevationRef()); 5429 setInfoDiv3("Plays Like: " 5430 + Math.round(metresToMyUnits(dist1)).toString() + " " + myUnitName()); 5431 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5432 setInfoDiv4("" 5433 + name1 + " " + perc1.toString() + "%"); 5434 elevationTimestamp = viewTimestamp; 5435 bestClubID[1] = club1; 5436 } 5437 } 5438 else if (par == 4) 5439 { 5440 var lat1 = getCourseHoleTeeLat(courseID, hole); 5441 var lon1 = getCourseHoleTeeLon(courseID, hole); 5442 var elev1 = lookupElevation(lat1, lon1); 5443 5444 var lat2 = getCourseHoleTargetLat(courseID, hole, 1); 5445 var lon2 = getCourseHoleTargetLon(courseID, hole, 1); 5446 var elev2 = lookupElevation(lat2, lon2); 5447 5448 var lat3 = getCourseHolePinLat(courseID, hole); 5449 var lon3 = getCourseHolePinLon(courseID, hole); 5450 var elev3 = lookupElevation(lat3, lon3); 5451 5452 if (elev1 == -1000000 || elev2 == -1000000 || elev3 == -1000000) 5453 { 5454 var dist1 = geogDist(lat1, lon1, lat2, lon2); 5455 var dist2 = geogDist(lat2, lon2, lat3, lon3); 5456 setInfoDiv2("Elevation: Not available"); 5457 setInfoDiv3("Plays Like: Not available"); 5458 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1);
5459 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5460 setInfoDiv4("" 5461 + name1 + " " + perc1.toString() + "% + " 5462 + name2 + " " + perc2.toString() + "%"); 5463 elevationTimestamp = viewTimestamp; 5464 bestClubID[1] = club1; 5465 bestClubID[2] = club2; 5466 } 5467 else if (elev1 == -999999 || elev2 == -999999 || elev3 == -999999) 5468 { 5469 setInfoDiv2(" ");//"Elevation: Fetching..."); 5470 setInfoDiv3(" ");//Plays Like:"); 5471 } 5472 else 5473 { 5474 if (elev1 < elev2 - 6) elev1 += 3; else if (elev1 < elev2) elev1 += 0.5 * (elev2 - elev1); 5475 if (elev2 < elev1 - 4) elev2 += 2; else if (elev2 < elev1) elev2 += 0.5 * (elev1 - elev2); 5476 if (elev2 > elev1 + 4) elev2 -= 2; else if (elev2 > elev1) elev2 += 0.5 * (elev1 - elev2); 5477 if (elev3 < elev1 - 4) elev3 += 2; else if (elev3 < elev1) elev3 += 0.5 * (elev1 - elev3); 5478 if (elev3 > elev1 + 4) elev3 -= 2; else if (elev3 > elev1) elev3 += 0.5 * (elev1 - elev3); 5479 var dist1 = playsLikeDist(geogDist(lat1, lon1, lat2, lon2), elev1, elev2); 5480 var dist2 = playsLikeDist(geogDist(lat2, lon2, lat3, lon3), elev2, elev3); 5481 setInfoDiv2("Elevation: " 5482 + Math.round(elev1).toString() + " " 5483 + formatElevDiff(elev2-elev1) + " " 5484 + formatElevDiff(elev3-elev2) + " metres " 5485 + elevationRef()); 5486 setInfoDiv3("Plays Like: " 5487 + Math.round(metresToMyUnits(dist1 + dist2)).toString() + " " + myUnitName() + " (" 5488 + Math.round(metresToMyUnits(dist1)).toString() + " + " 5489 + Math.round(metresToMyUnits(dist2)).toString() + ")"); 5490 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5491 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5492 setInfoDiv4("" 5493 + name1 + " " + perc1.toString() + "% + " 5494 + name2 + " " + perc2.toString() + "%"); 5495 elevationTimestamp = viewTimestamp; 5496 bestClubID[1] = club1; 5497 bestClubID[2] = club2; 5498 } 5499 } 5500 else if (par == 5) 5501 { 5502 var lat1 = getCourseHoleTeeLat(courseID, hole); 5503 var lon1 = getCourseHoleTeeLon(courseID, hole); 5504 var elev1 = lookupElevation(lat1, lon1); 5505 5506 var lat2 = getCourseHoleTargetLat(courseID, hole, 1); 5507 var lon2 = getCourseHoleTargetLon(courseID, hole, 1); 5508 var elev2 = lookupElevation(lat2, lon2); 5509 5510 var lat3 = getCourseHoleTargetLat(courseID, hole, 2); 5511 var lon3 = getCourseHoleTargetLon(courseID, hole, 2); 5512 var elev3 = lookupElevation(lat3, lon3); 5513 5514 var lat4 = getCourseHolePinLat(courseID, hole); 5515 var lon4 = getCourseHolePinLon(courseID, hole); 5516 var elev4 = lookupElevation(lat4, lon4); 5517 5518 if (elev1 == -1000000 || elev2 == -1000000 || elev3 == -1000000 || elev4 == -1000000) 5519 { 5520 var dist1 = geogDist(lat1, lon1, lat2, lon2); 5521 var dist2 = geogDist(lat2, lon2, lat3, lon3); 5522 var dist3 = geogDist(lat3, lon3, lat4, lon4); 5523 setInfoDiv2("Elevation: Not available"); 5524 setInfoDiv3("Plays Like: Not available"); 5525 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5526 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5527 var club3 = getBestClubForDist(dist3); var name3 = getClubShortName(club3); var perc3 = getClubDistPercentage(club3, dist3); 5528 setInfoDiv4("" 5529 + name1 + " " + perc1.toString() + "% + " 5530 + name2 + " " + perc2.toString() + "% + " 5531 + name3 + " " + perc3.toString() + "%"); 5532 elevationTimestamp = viewTimestamp; 5533 bestClubID[1] = club1; 5534 bestClubID[2] = club2; 5535 bestClubID[3] = club3; 5536 } 5537 else if (elev1 == -999999 || elev2 == -999999 || elev3 == -999999 || elev4 == -999999) 5538 { 5539 setInfoDiv2(" ");//"Elevation: Fetching..."); 5540 setInfoDiv3(" ");//Plays Like:"); 5541 } 5542 else 5543 { 5544 if (elev1 < elev2 - 6) elev1 += 3; else if (elev1 < elev2) elev1 += 0.5 * (elev2 - elev1); 5545 if (elev2 < elev1 - 4) elev2 += 2; else if (elev2 < elev1) elev2 += 0.5 * (elev1 - elev2); 5546 if (elev2 > elev1 + 4) elev2 -= 2; else if (elev2 > elev1) elev2 += 0.5 * (elev1 - elev2); 5547 if (elev3 < elev1 - 4) elev3 += 2;
5547 else if (elev3 < elev1) elev3 += 0.5 * (elev1 - elev3); 5548 if (elev3 > elev1 + 4) elev3 -= 2; else if (elev3 > elev1) elev3 += 0.5 * (elev1 - elev3); 5549 if (elev4 < elev1 - 4) elev4 += 2; else if (elev4 < elev1) elev4 += 0.5 * (elev1 - elev4); 5550 if (elev4 > elev1 + 4) elev4 -= 2; else if (elev4 > elev1) elev4 += 0.5 * (elev1 - elev4); 5551 var dist1 = playsLikeDist(geogDist(lat1, lon1, lat2, lon2), elev1, elev2); 5552 var dist2 = playsLikeDist(geogDist(lat2, lon2, lat3, lon3), elev2, elev3); 5553 var dist3 = playsLikeDist(geogDist(lat3, lon3, lat4, lon4), elev3, elev4); 5554 setInfoDiv2("Elevation: " 5555 + Math.round(elev1).toString() + " " 5556 + formatElevDiff(elev2-elev1) + " " 5557 + formatElevDiff(elev3-elev2) + " " 5558 + formatElevDiff(elev4-elev3) + " metres " 5559 + elevationRef()); 5560 setInfoDiv3("Plays Like: " 5561 + Math.round(metresToMyUnits(dist1 + dist2 + dist3)).toString() + " " + myUnitName() + " (" 5562 + Math.round(metresToMyUnits(dist1)).toString() + " + " 5563 + Math.round(metresToMyUnits(dist2)).toString() + " + " 5564 + Math.round(metresToMyUnits(dist3)).toString() + ")"); 5565 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5566 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5567 var club3 = getBestClubForDist(dist3); var name3 = getClubShortName(club3); var perc3 = getClubDistPercentage(club3, dist3); 5568 setInfoDiv4("" 5569 + name1 + " " + perc1.toString() + "% + " 5570 + name2 + " " + perc2.toString() + "% + " 5571 + name3 + " " + perc3.toString() + "%"); 5572 elevationTimestamp = viewTimestamp; 5573 bestClubID[1] = club1; 5574 bestClubID[2] = club2; 5575 bestClubID[3] = club3; 5576 } 5577 } 5578 else if (par == 6) 5579 { 5580 var lat1 = getCourseHoleTeeLat(courseID, hole); 5581 var lon1 = getCourseHoleTeeLon(courseID, hole); 5582 var elev1 = lookupElevation(lat1, lon1); 5583 5584 var lat2 = getCourseHoleTargetLat(courseID, hole, 1); 5585 var lon2 = getCourseHoleTargetLon(courseID, hole, 1); 5586 var elev2 = lookupElevation(lat2, lon2); 5587 5588 var lat3 = getCourseHoleTargetLat(courseID, hole, 2); 5589 var lon3 = getCourseHoleTargetLon(courseID, hole, 2); 5590 var elev3 = lookupElevation(lat3, lon3); 5591 5592 var lat4 = getCourseHoleTargetLat(courseID, hole, 3); 5593 var lon4 = getCourseHoleTargetLon(courseID, hole, 3); 5594 var elev4 = lookupElevation(lat4, lon4); 5595 5596 var lat5 = getCourseHolePinLat(courseID, hole); 5597 var lon5 = getCourseHolePinLon(courseID, hole); 5598 var elev5 = lookupElevation(lat5, lon5); 5599 5600 if (elev1 == -1000000 || elev2 == -1000000 || elev3 == -1000000 || elev4 == -1000000 || elev5 == -1000000) 5601 { 5602 var dist1 = geogDist(lat1, lon1, lat2, lon2); 5603 var dist2 = geogDist(lat2, lon2, lat3, lon3); 5604 var dist3 = geogDist(lat3, lon3, lat4, lon4); 5605 var dist4 = geogDist(lat4, lon4, lat5, lon5); 5606 setInfoDiv2("Elevation: Not available"); 5607 setInfoDiv3("Plays Like: Not available"); 5608 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5609 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5610 var club3 = getBestClubForDist(dist3); var name3 = getClubShortName(club3); var perc3 = getClubDistPercentage(club3, dist3); 5611 var club4 = getBestClubForDist(dist4); var name4 = getClubShortName(club4); var perc4 = getClubDistPercentage(club4, dist4); 5612 setInfoDiv4("" 5613 + name1 + " " + perc1.toString() + "% + " 5614 + name2 + " " + perc2.toString() + "% + " 5615 + name3 + " " + perc3.toString() + "% + " 5616 + name4 + " " + perc4.toString() + "%"); 5617 elevationTimestamp = viewTimestamp; 5618 bestClubID[1] = club1; 5619 bestClubID[2] = club2; 5620 bestClubID[3] = club3; 5621 bestClubID[4] = club4; 5622 } 5623 else if (elev1 == -999999 || elev2 == -999999 || elev3 == -999999 || elev4 == -999999 || elev5 == -999999) 5624 { 5625 setInfoDiv2(" ");//"Elevation: Fetching..."); 5626 setInfoDiv3(" ");//Plays Like:"); 5627 } 5628 else 5629 { 5630 if (elev1 < elev2 - 6) elev1 += 3; else if (elev1 < elev2) elev1 += 0.5 * (elev2 - elev1); 5631 if (elev2 < elev1 - 4) elev2 += 2;
5631 else if (elev2 < elev1) elev2 += 0.5 * (elev1 - elev2); 5632 if (elev2 > elev1 + 4) elev2 -= 2; else if (elev2 > elev1) elev2 += 0.5 * (elev1 - elev2); 5633 if (elev3 < elev1 - 4) elev3 += 2; else if (elev3 < elev1) elev3 += 0.5 * (elev1 - elev3); 5634 if (elev3 > elev1 + 4) elev3 -= 2; else if (elev3 > elev1) elev3 += 0.5 * (elev1 - elev3); 5635 if (elev4 < elev1 - 4) elev4 += 2; else if (elev4 < elev1) elev4 += 0.5 * (elev1 - elev4); 5636 if (elev4 > elev1 + 4) elev4 -= 2; else if (elev4 > elev1) elev4 += 0.5 * (elev1 - elev4); 5637 if (elev5 < elev1 - 4) elev5 += 2; else if (elev5 < elev1) elev5 += 0.5 * (elev1 - elev5); 5638 if (elev5 > elev1 + 4) elev5 -= 2; else if (elev5 > elev1) elev5 += 0.5 * (elev1 - elev5); 5639 var dist1 = playsLikeDist(geogDist(lat1, lon1, lat2, lon2), elev1, elev2); 5640 var dist2 = playsLikeDist(geogDist(lat2, lon2, lat3, lon3), elev2, elev3); 5641 var dist3 = playsLikeDist(geogDist(lat3, lon3, lat4, lon4), elev3, elev4); 5642 var dist4 = playsLikeDist(geogDist(lat4, lon4, lat5, lon5), elev4, elev5); 5643 setInfoDiv2("Elevation: " 5644 + Math.round(elev1).toString() + " " 5645 + formatElevDiff(elev2-elev1) + " " 5646 + formatElevDiff(elev3-elev2) + " " 5647 + formatElevDiff(elev4-elev3) + " " 5648 + formatElevDiff(elev5-elev4) + " metres " 5649 + elevationRef()); 5650 setInfoDiv3("Plays Like: " 5651 + Math.round(metresToMyUnits(dist1 + dist2 + dist3 + dist4)).toString() + " " + myUnitName() + " (" 5652 + Math.round(metresToMyUnits(dist1)).toString() + " + " 5653 + Math.round(metresToMyUnits(dist2)).toString() + " + " 5654 + Math.round(metresToMyUnits(dist3)).toString() + " + " 5655 + Math.round(metresToMyUnits(dist4)).toString() + ")"); 5656 var club1 = getBestClubForDist(dist1); var name1 = getClubShortName(club1); var perc1 = getClubDistPercentage(club1, dist1); 5657 var club2 = getBestClubForDist(dist2); var name2 = getClubShortName(club2); var perc2 = getClubDistPercentage(club2, dist2); 5658 var club3 = getBestClubForDist(dist3); var name3 = getClubShortName(club3); var perc3 = getClubDistPercentage(club3, dist3); 5659 var club4 = getBestClubForDist(dist4); var name4 = getClubShortName(club4); var perc4 = getClubDistPercentage(club4, dist4); 5660 setInfoDiv4("" 5661 + name1 + " " + perc1.toString() + "% + " 5662 + name2 + " " + perc2.toString() + "% + " 5663 + name3 + " " + perc3.toString() + "% + " 5664 + name4 + " " + perc4.toString() + "%"); 5665 elevationTimestamp = viewTimestamp; 5666 bestClubID[1] = club1; 5667 bestClubID[2] = club2; 5668 bestClubID[3] = club3; 5669 bestClubID[4] = club4; 5670 } 5671 } 5672 window.setTimeout(refreshElevation, 1000); 5673} 5674 5675function lookupElevation(lat, lon) 5676{ 5677 elevationSource = 0; 5678 var e = -9999; 5679 if (terrainMode == 2 && terrain1SourceID >= 1) // Lidar 5680 { 5681 e = getProVisualizerTerrain1Height(lat, lon); 5682 if (e > -9999) 5683 { 5684 elevationSource = 100; 5685 elevationSourceRef = "(<a href="+terrainURLs[terrain1SourceID]+">Lidar</a>)"; 5686 return e; 5687 } 5688 } 5689 e = elevationOverride(lat, lon); 5690 if (e > -9999) 5691 { 5692 elevationSource = 99; 5693 return e; 5694 } 5695 var dLat = distToDeltaLat(3, lat, lon); 5696 var dLon = distToDeltaLon(3, lat, lon); 5697 var lat1 = lat - dLat; 5698 var lat2 = lat + dLat; 5699 var lon1 = lon - dLon; 5700 var lon2 = lon + dLon; 5701 var lat3 = lat - dLat*1000; 5702 var lat4 = lat + dLat*1000; 5703 var lon3 = lon - dLon*1000; 5704 var lon4 = lon + dLon*1000; 5705 var elev = null; 5706 var minReqTimestamp = Date.now() - 10000; 5707 var i = 0; 5708 while (elev == null && i < elevation.length) 5709 { 5710 var eLat = elevationLat[i]; 5711 var eLon = elevationLon[i]; 5712 var e = elevation[i]; 5713 if (e == -1000000) 5714 { 5715 if (eLat >= lat3 && eLat <= lat4 && eLon >= lon3 && eLon <= lon4) 5716 elev = e; 5717 } 5718 else 5719 { 5720 if (eLat >= lat1 && eLat <= lat2 && eLon >= lon1 && eLon <= lon2) 5721 { 5722 elev = e; 5723 if (elev == -999999 && elevationReqTimestamp[i] < minReqTimestamp) 5724 { 5725 elevation[i] = -1000000; 5726 elev = -1000000; 5727 } 5728 } 5729 } 5730 i += 1; 5731 } 5732 if (elev == null) 5733 { 5734 fetchElevation(lat, lon); 5735 return -999999; // fetching 5736 } 5737 else if (elev == -999999) 5738 { 5739 return elev; // fetching 5740 } 5741 else if (elev < -1000 || elev > 5000) 5742 { 5743 return lookupElevation2(lat, lon); 5744 } 5745 elevationSource = 1; 5746 return elev; 5747} 5748 5749function lookupElevation2(lat, lon) 5750{ 5751 var dLat = distToDeltaLat(3, lat, lon); 5752 var dLon = distToDeltaLon(3, lat, lon); 5753 var lat1 = lat - dLat; 5754 var lat2 = lat + dLat; 5755 var lon1 = lon - dLon; 5756 var lon2 = lon + dLon; 5757 var lat3 = lat - dLat*1000; 5758 var lat4 = lat + dLat*1000; 5759 var lon3 = lon - dLon*1000; 5760 var lon4 = lon + dLon*1000; 5761 var elev = null; 5762 var i = 0; 5763 while (elev == null && i < elevation2.length) 5764 { 5765 var eLat = elevation2Lat[i]; 5766 var eLon = elevation2Lon[i]; 5767 var e = elevation2[i]; 5768 if (e == -1000000) 5769 { 5770 if (eLat >= lat3 && eLat <= lat4 && eLon >= lon3 && eLon <= lon4) 5771 elev = e; 5772 } 5773 else 5774 { 5775 if (eLat >= lat1 && eLat <= lat2 && eLon >= lon1 && eLon <= lon2) 5776 elev = e; 5777 } 5778 i += 1; 5779 } 5780 if (elev == null) 5781 { 5782 fetchElevation2(lat, lon); 5783 return -999999; // fetching 5784 } 5785 else if (elev == -999999) 5786 { 5787 return elev; // fetching 5788 } 5789 else if (elev < -1000 || elev > 5000) 5790 { 5791 return -1000000; // not available 5792 } 5793 elevationSource = 2; 5794 if (lat > 60 || lat < -56) elevationSource = 3; 5795 return elev; 5796} 5797 5798function fetchElevation(lat, lon) 5799{ 5800 var i = elevation.length; 5801 elevationReqTimestamp[i] = Date.now(); 5802 elevationLat[i] = lat; 5803 elevationLon[i] = lon; 5804 elevation[i] = -999999; // fetching 5805 if (lon > -20) 5806 { 5807 elevation[i] = -1000000; 5808 } 5809 else if (window.XMLHttpRequest) 5810 { 5811 var req = "https://epqs.nationalmap.gov/v1/json?x=" + lon.toString() + "&y=" + lat.toString() + "&units=Meters&wkid=4326&includeDate=False"; 5812 var xhr = new XMLHttpRequest(); 5813 xhr.onreadystatechange = function() 5814 { 5815 if (xhr.readyState == 4 && xhr.status == 200) 5816 { 5817 if (xhr.responseText == "") 5818 { 5819 elevation[i] = -1000000; 5820 } 5821 else 5822 { 5823 var obj = JSON.parse(xhr.responseText); 5824 elevation[i] = Math.round(parseFloat(obj.value)); 5825 } 5826 } 5827 } 5828 xhr.open("GET", req, true); 5829 xhr.send(); 5830 } 5831} 5832 5833function fetchElevation2(lat, lon) 5834{ 5835 var i = elevation2.length; 5836 elevation2Lat[i] = lat; 5837 elevation2Lon[i] = lon; 5838 elevation2[i] = -999999; // fetching 5839 if (window.XMLHttpRequest) 5840 { 5841 var req = "/elevation.php?coords="; 5842 req += Math.round(lat * 111111).toString() + ","; 5843 req += Math.round(lon * 111111).toString(); 5844 var xhr = new XMLHttpRequest(); 5845 xhr.onreadystatechange = function() 5846 { 5847 if (xhr.readyState == 4 && xhr.status == 200) 5848 { 5849 if (xhr.responseText.length > 5) 5850 { 5851 eval(xhr.responseText); 5852 if (elevHeightsMetres.length == 1) 5853 elevation2[i] = elevHeightsMetres[0]; 5854 else if (elevHeightsFeet.length == 1) 5855 elevation2[i] = elevHeightsFeet[0] * (1609.344 / (3*1760)); 5856 else 5857 elevation2[i] = -1000000; 5858 } 5859 else 5860 { 5861 elevation2[i] = -1000000; 5862 } 5863 } 5864 } 5865 xhr.open("GET", req, true); 5866 xhr.send(); 5867 } 5868} 5869 5870function spreadHandler() 5871{ 5872 dispersionHandler(true); 5873} 5874 5875var showPuttDispersion = 0; // 0 = off, 1 = dispersion, 2 = spread 5876 5877function dispersionHandler(showBox = false) 5878{ 5879 setMode(0); 5880 if (greenSlopesCount > 0 && puttHistoryOp.length > 0) 5881 { 5882 if (showBox && showPuttDispersion != 2) 5883 { 5884 showPuttDispersion = 2; 5885 selectGreenSpeedHandler(); 5886 } 5887 if (!showBox && showPuttDispersion != 1) 5888 { 5889 showPuttDispersion = 1; 5890 selectGreenSpeedHandler(); 5891 } 5892 return; 5893 } 5894 var courseID = currentCourseID(); 5895 var hole = currentHole(); 5896 var dispersionProfileID = getDispersionProfileID(); 5897 if (hole < 1) 5898 { 5899 alert("Please select hole."); 5900 return; 5901 } 5902 var teeLat = getCourseHoleTeeLat(courseID, hole); 5903 var teeLon = getCourseHoleTeeLon(courseID, hole); 5904 if (teeLat == 0 && teeLon == 0) 5905 { 5906 alert("Please set tee position."); 5907 return; 5908 } 5909 var pinLat = getCourseHolePinLat(courseID, hole); 5910 var pinLon = getCourseHolePinLon(courseID, hole); 5911 if (pinLat == 0 && pinLon == 0) 5912 { 5913 alert("Please set pin position."); 5914 return; 5915 }
5916 var par = getCourseHolePar(courseID, hole); 5917 if (par < 3) 5918 { 5919 alert("Please set tee and pin position."); 5920 return; 5921 } 5922 5923 var target1Lat = getCourseHoleTargetLat(courseID, hole, 1); 5924 var target1Lon = getCourseHoleTargetLon(courseID, hole, 1); 5925 5926 var target2Lat = getCourseHoleTargetLat(courseID, hole, 2); 5927 var target2Lon = getCourseHoleTargetLon(courseID, hole, 2); 5928 5929 var target3Lat = getCourseHoleTargetLat(courseID, hole, 3); 5930 var target3Lon = getCourseHoleTargetLon(courseID, hole, 3); 5931 5932 if (dispersionProfileID <= 10) 5933 { 5934 // Pro+X% 5935 var nrl = getNRL(); 5936 var dispersionMultiplier = 1.0 + 0.1 * dispersionProfileID; 5937 var dispersionMultiplier2 = 1.0 + 0.05 * dispersionProfileID; 5938 for (var i = 0; i < 100; ++i) 5939 { 5940 var lat1 = teeLat; 5941 var lon1 = teeLon; 5942 5943 for (var shot = 1; shot <= par - 2; ++shot) 5944 { 5945 var lat0 = teeLat; 5946 var lon0 = teeLon; 5947 if (shot == 2) 5948 { 5949 lat0 = target1Lat; 5950 lon0 = target1Lon; 5951 } 5952 else if (shot == 3) 5953 { 5954 lat0 = target2Lat; 5955 lon0 = target2Lon; 5956 } 5957 else if (shot == 4) 5958 { 5959 lat0 = target3Lat; 5960 lon0 = target3Lon; 5961 } 5962 var lat2 = 0; 5963 var lon2 = 0; 5964 if (shot == par - 2) 5965 { 5966 lat2 = pinLat; 5967 lon2 = pinLon; 5968 } 5969 else if (shot == 1) 5970 { 5971 lat2 = target1Lat; 5972 lon2 = target1Lon; 5973 } 5974 else if (shot == 2) 5975 { 5976 lat2 = target2Lat; 5977 lon2 = target2Lon; 5978 } 5979 else if (shot == 3) 5980 { 5981 lat2 = target3Lat; 5982 lon2 = target3Lon; 5983 } 5984 var nominalDist = geogDist(lat0, lon0, lat2, lon2); 5985 var dist = geogDist(lat1, lon1, lat2, lon2); 5986 var bearing = geogBearing(lat1, lon1, lat2, lon2); 5987 5988 // Distance Error 5989 // -8% to +6% 5990 var x = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 5991 x = 14*x - 7; // Normal distribution -7 .. +7 5992 if (dist < 90) x *= 1 + (90 - dist)/45; // Distance error gradually increases below 90 metres 5993 x = x - 1; // Normal distribution -8 .. +6 5994 x = x*dist/100; // Normal distribution -8% .. +6% 5995 if (x < 0) x *= dispersionMultiplier; 5996 else x *= dispersionMultiplier2; 5997 5998 // Direction Error 5999 // +/- 3/5/7 degrees at 100/200/300 yards 6000 var max = 1 + 2*dist/90; // 3/5/7 at 100/200/300 yards 6001 var y = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 6002 y = 2*max*y - max; // Normal distribution -max .. +max 6003 y *= dispersionMultiplier; 6004 if (nrl == 1) 6005 { 6006 if (y > 0) x -= 0.3*dist*y*Math.PI/180; 6007 else x -= 0.1*dist*y*Math.PI/180; 6008 } 6009 if (nrl == 2) 6010 { 6011 if (y > 0) x += 0.1*dist*y*Math.PI/180; 6012 else x += 0.3*dist*y*Math.PI/180; 6013 } 6014 6015 var dist2 = dist + x; 6016 var bearing2 = bearing + y; 6017 var lat3 = moveLat(lat1, lon1, dist2, bearing2); 6018 var lon3 = moveLon(lat1, lon1, dist2, bearing2); 6019 6020 // Wind and bounce 6021 // Up to 5% in random direction 6022 var z = (Math.random() + Math.random() + Math.random() + Math.random()) / 4; // Normal distribution 0 .. 1 6023 z = 10*z - 5; // Normal distribution -5 .. +5 6024 var dist3 = z*dist/100; // Normal distribution -5% .. +5% 6025 var bearing3 = 360 * Math.random(); 6026 var lat4 = moveLat(lat3, lon3, dist3, bearing3); 6027 var lon4 = moveLon(lat3, lon3, dist3, bearing3); 6028 6029 if (!showBox && nominalDist > 10.0) 6030 addDispersionMarker(lat4, lon4); 6031 6032 lat1 = lat4; 6033 lon1 = lon4; 6034 } 6035 } 6036 } 6037 else 6038 { 6039 // Custom 6040 var lat1 = teeLat; 6041 var lon1 = teeLon; 6042 6043 for (var shot = 1; shot <= par - 2; ++shot) 6044 { 6045 var lat0 = teeLat; 6046 var lon0 = teeLon; 6047 if (shot == 2) 6048 { 6049 lat0 = target1Lat; 6050 lon0 = target1Lon; 6051 } 6052 else if (shot == 3) 6053 { 6054 lat0 = target2Lat; 6055 lon0 = target2Lon; 6056 } 6057 else if (shot == 4) 6058 { 6059 lat0 = target3Lat; 6060 lon0 = target3Lon; 6061 } 6062 var lat2 = 0; 6063 var lon2 = 0; 6064 if (shot == par - 2) 6065 { 6066 lat2 = pinLat; 6067 lon2 = pinLon; 6068 } 6069 else if (shot == 1) 6070 { 6071 lat2 = target1Lat; 6072 lon2 = target1Lon; 6073 } 6074 else if (shot == 2) 6075 { 6076 lat2 = target2Lat; 6077 lon2 = target2Lon; 6078 } 6079 else if (shot == 3) 6080 { 6081 lat2 = target3Lat; 6082 lon2 = target3Lon; 6083 } 6084 var nominalDist = geogDist(lat0, lon0, lat2, lon2); 6085 var dist = geogDist(lat1, lon1, lat2, lon2); 6086 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6087 var clubID = getBestClubForDist(dist); 6088 var clubPercent = getClubDistPercentage(clubID, dist, false); 6089 var clubSpreadL = myUnitsToMetres(getClubSpreadL(clubID)) * (clubPercent/100.0); 6090 var clubSpreadR = myUnitsToMetres(getClubSpreadR(clubID)) * (clubPercent/100.0); 6091 var clubSpreadO = myUnitsToMetres(getClubSpreadO(clubID)) * (clubPercent/100.0); 6092 var clubSpreadS = myUnitsToMetres(getClubSpreadS(clubID)) * (clubPercent/100.0); 6093 var clubSpreadOL = clubSpreadO; 6094 var clubSpreadOR = clubSpreadO; 6095 var clubSpreadSL = clubSpreadS; 6096 var clubSpreadSR = clubSpreadS; 6097 var clubShape = getClubShape(clubID); 6098 if (clubShape == 2) { clubSpreadOL += 0.20*clubSpreadL; clubSpreadOR -= 0.20*clubSpreadR; 6099 clubSpreadSL -= 0.20*clubSpreadL; clubSpreadSR += 0.20*clubSpreadR; } 6100 if (clubShape == 1) { clubSpreadOL += 0.10*clubSpreadL; clubSpreadOR -= 0.10*clubSpreadR; 6101 clubSpreadSL -= 0.10*clubSpreadL; clubSpreadSR += 0.10*clubSpreadR; } 6102 if (clubShape == -1) { clubSpreadOL -= 0.10*clubSpreadL; clubSpreadOR += 0.10*clubSpreadR; 6103 clubSpreadSL += 0.10*clubSpreadL; clubSpreadSR -= 0.10*clubSpreadR; } 6104 if (clubShape == -2) { clubSpreadOL -= 0.20*clubSpreadL; clubSpreadOR += 0.20*clubSpreadR; 6105 clubSpreadSL += 0.20*clubSpreadL; clubSpreadSR -= 0.20*clubSpreadR; } 6106 if (dist < 90) 6107 { 6108 // Distance error gradually increases below 90 metres 6109 clubSpreadO *= 1 + (90 - dist)/45; 6110 clubSpreadS *= 1 + (90 - dist)/45; 6111 } 6112 for (var i = 0; i < 100; ++i) 6113 { 6114 // Distance Error 6115 var x = (Math.random() + Math.random() + Math.random()) / 3; // Normal distribution 0 .. 1 6116 x = 2*x - 1; // Normal distribution -1 .. +1 6117 if (x < 0) x *= clubSpreadS; 6118 if (x > 0) x *= clubSpreadO; 6119 x *= 1.3; 6120 6121 // Direction Error 6122 var y = (Math.random() + Math.random() + Math.random()) / 3; // Normal distribution 0 .. 1 6123 y = 2*y - 1; // Normal distribution -1 .. +1 6124 if (y < 0) y *= clubSpreadL; 6125 if (y > 0) y *= clubSpreadR; 6126 y *= 1.3; 6127 if (clubShape == 2) { if (y > 0) x -= 0.30*y; else x -= 0.10*y; } 6128 if (clubShape == 1) { if (y > 0) x -= 0.15*y; else x -= 0.05*y; } 6129 if (clubShape == -1) { if (y < 0) x += 0.15*y; else x += 0.05*y; } 6130 if (clubShape == -2) { if (y < 0) x += 0.30*y; else x += 0.10*y; } 6131 6132 var dist2 = dist + x; 6133 var bearing2 = bearing + (y/dist)*(180.0/Math.PI); 6134 var lat3 = moveLat(lat1, lon1, dist2, bearing2); 6135 var lon3 = moveLon(lat1, lon1, dist2, bearing2); 6136 6137 // Wind and bounce 6138 // Up to 2m in a random direction 6139 var z = (Math.random() + Math.random() + Math.random()) / 3; // Normal distribution 0 .. 1 6140 var dist3 = 4*z - 2; // Normal distribution -2 .. +2 6141 var bearing3 = 360 * Math.random(); 6142 var lat4 = moveLat(lat3, lon3, dist3, bearing3); 6143 var lon4 = moveLon(lat3, lon3, dist3, bearing3); 6144 6145 if (!showBox && nominalDist > 10.0) 6146 addDispersionMarker(lat4, lon4); 6147 } 6148 lat1 = lat2; 6149 lon1 = lon2; 6150 } 6151 } 6152 if (showBox) 6153 { 6154 // Draw dispersion boxes... 6155 for (var shot = 1; shot <= par - 2; ++shot) 6156 { 6157 var lat1 = teeLat; 6158 var lon1 = teeLon; 6159 if (shot == 2) 6160 { 6161 lat1 = target1Lat; 6162 lon1 = target1Lon; 6163 } 6164 else if (shot == 3) 6165 { 6166 lat1 = target2Lat; 6167 lon1 = target2Lon; 6168 } 6169 else if (shot == 4) 6170 { 6171 lat1 = target3Lat; 6172 lon1 = target3Lon; 6173 } 6174 var lat2 = 0; 6175 var lon2 = 0; 6176 if (shot == par - 2) 6177 { 6178 lat2 = pinLat; 6179 lon2 = pinLon; 6180 } 6181 else if (shot == 1) 6182 { 6183 lat2 = target1Lat; 6184 lon2 = target1Lon; 6185 } 6186 else if (shot == 2) 6187 { 6188 lat2 = target2Lat; 6189 lon2 = target2Lon; 6190 } 6191 else if (shot == 3) 6192 { 6193 lat2 = target3Lat; 6194 lon2 = target3Lon; 6195 } 6196 var dist = geogDist(lat1, lon1, lat2, lon2); 6197 if (dist > 10.0) 6198 { 6199 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6200 var clubSpreadL = 0.10*dist; 6201 var clubSpreadR = 0.10*dist; 6202 var clubSpreadO = 0.06*dist; 6203 var clubSpreadS = 0.08*dist; 6204 var clubShape = 0; 6205 if (dispersionProfileID <= 10) 6206 { 6207 // Pro+X% 6208 var k = 0.8; 6209 var tO = (dist > 275.0 ? 0.0 : (1.0 - dist/275.0)); 6210 var kL = 1.0 + 0.08 * dispersionProfileID; 6211 var kR = 1.0 + 0.08 * dispersionProfileID; 6212 var kO = 1.0 + 0.08 * dispersionProfileID * tO; 6213 var kS = 1.0 + 0.08 * dispersionProfileID; 6214 clubSpreadLRDegrees = 1 + 2*dist/90; 6215 if (clubSpreadLRDegrees < 3) clubSpreadLRDegrees = 3; 6216 clubSpreadLRRadians = clubSpreadLRDegrees*(Math.PI/180.0); 6217 clubSpreadL = k*kL*clubSpreadLRRadians*dist; 6218 clubSpreadR = k*kR*clubSpreadLRRadians*dist; 6219 clubSpreadO = k*kO*0.06*dist; 6220 clubSpreadS = k*kS*0.09*dist; 6221 if (dist < 90) 6222 { 6223 // Error gradually increases below 90 metres 6224 clubSpreadL *= 1 + (90 - dist)/90; 6225 clubSpreadR *= 1 + (90 - dist)/90; 6226 clubSpreadO *= 1 + (90 - dist)/90; 6227 clubSpreadS *= 1 + (90 - dist)/90; 6228 } 6229 var nrl = getNRL(); 6230 if (nrl == 1) clubShape = +2; 6231 if (nrl == 2) clubShape = -2; 6232 } 6233 else 6234 { 6235 // Custom 6236 var clubID = getBestClubForDist(dist); 6237 var clubPercent = getClubDistPercentage(clubID, dist, false); 6238 clubSpreadL = myUnitsToMetres(getClubSpreadL(clubID)) * (clubPercent/100.0); 6239 clubSpreadR = myUnitsToMetres(getClubSpreadR(clubID)) * (clubPercent/100.0); 6240 clubSpreadO = myUnitsToMetres(getClubSpreadO(clubID)) * (clubPercent/100.0); 6241 clubSpreadS = myUnitsToMetres(getClubSpreadS(clubID)) * (clubPercent/100.0); 6242 if (dist < 90) 6243 { 6244 // Distance error gradually increases below 90 metres 6245 clubSpreadO *= 1 + (90 - dist)/90; 6246 clubSpreadS *= 1 + (90 - dist)/90; 6247 } 6248 clubShape = getClubShape(clubID); 6249 } 6250 var clubSpreadOL = clubSpreadO; 6251 var clubSpreadOR = clubSpreadO; 6252 var clubSpreadSL = clubSpreadS; 6253 var clubSpreadSR = clubSpreadS; 6254 if (clubShape == 2) { clubSpreadOL += 0.20*clubSpreadL; clubSpreadOR -= 0.20*clubSpreadR; 6255 clubSpreadSL -= 0.20*clubSpreadL; clubSpreadSR += 0.20*clubSpreadR; } 6256 if (clubShape == 1) { clubSpreadOL += 0.10*clubSpreadL; clubSpreadOR -= 0.10*clubSpreadR; 6257 clubSpreadSL -= 0.10*clubSpreadL; clubSpreadSR += 0.10*clubSpreadR; } 6258 if (clubShape == -1) { clubSpreadOL -= 0.10*clubSpreadL; clubSpreadOR += 0.10*clubSpreadR; 6259 clubSpreadSL += 0.10*clubSpreadL; clubSpreadSR -= 0.10*clubSpreadR; } 6260 if (clubShape == -2) { clubSpreadOL -= 0.20*clubSpreadL; clubSpreadOR += 0.20*clubSpreadR; 6261 clubSpreadSL += 0.20*clubSpreadL; clubSpreadSR -= 0.20*clubSpreadR; } 6262 // Draw box around dispersion... 6263 var brightness = 0.03 * getCircleBrightness(); 6264 var c = getCircleColor(); 6265 var color = new Cesium.Color(1,1,0,brightness); 6266 if (c == 1) color = new Cesium.Color(0.5,1.0,0.5,brightness); 6267 if (c == 2) color = new Cesium.Color(0.5,1.0,1.0,brightness); 6268 if (c == 3) color = new Cesium.Color(1.0,1.0,1.0,brightness); 6269 var bearingLO = bearing - (clubSpreadL/dist)*(180.0/Math.PI); 6270 var bearingRO = bearing + (clubSpreadR/dist)*(180.0/Math.PI); 6271 var bearingLS = bearing - (clubSpreadL/dist)*(180.0/Math.PI); 6272 var bearingRS = bearing + (clubSpreadR/dist)*(180.0/Math.PI); 6273 var coords = []; 6274 var bb = 5000; 6275 var n = 1 + Math.round(clubSpreadL); 6276 for (var i = 0; i <= n; ++i) 6277 { 6278 var d = dist+clubSpreadO+(i/n)*(clubSpreadOL-clubSpreadO); 6279 var b = bearing+(i/n)*(bearingLO-bearing); 6280 d -= d*(b-bearing)*(b-bearing)/bb; 6281 var lat3 = moveLat(lat1, lon1, d, b); 6282 var lon3 = moveLon(lat1, lon1, d, b); 6283 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6284 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6285 } 6286 var n = 1 + Math.round(clubSpreadO+clubSpreadS); 6287 for (var i = 1; i <= n; ++i) 6288 { 6289 var d = dist+clubSpreadOL-(i/n)*(clubSpreadSL+clubSpreadOL); 6290 var b = bearingLO+(i/n)*(bearingLS-bearingLO); 6291 d -= d*(b-bearing)*(b-bearing)/bb; 6292 var lat3 = moveLat(lat1, lon1, d, b); 6293 var lon3 = moveLon(lat1, lon1, d, b); 6294 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6295 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6296 } 6297 var n = 1 + Math.round(clubSpreadL); 6298 for (var i = 1; i <= n; ++i) 6299 { 6300 var d = dist-clubSpreadSL-(i/n)*(clubSpreadS-clubSpreadSL); 6301 var b = bearingLS+(i/n)*(bearing-bearingLS); 6302 d -= d*(b-bearing)*(b-bearing)/bb; 6303 var lat3 = moveLat(lat1, lon1, d, b); 6304 var lon3 = moveLon(lat1, lon1, d, b); 6305 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6306 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6307 } 6308 var n = 1 + Math.round(clubSpreadR); 6309 for (var i = 1; i <= n; ++i) 6310 { 6311 var d = dist-clubSpreadS-(i/n)*(clubSpreadSR-clubSpreadS); 6312 var b = bearing+(i/n)*(bearingRS-bearing); 6313 d -= d*(b-bearing)*(b-bearing)/bb; 6314 var lat3 = moveLat(lat1, lon1, d, b); 6315 var lon3 = moveLon(lat1, lon1, d, b); 6316 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6317 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6318 } 6319 var n = 1 + Math.round(clubSpreadO+clubSpreadS); 6320 for (var i = 1; i <= n; ++i) 6321 { 6322 var d = dist-clubSpreadSR+(i/n)*(clubSpreadSR+clubSpreadOR); 6323 var b = bearingRS+(i/n)*(bearingRO-bearingRS); 6324 d -= d*(b-bearing)*(b-bearing)/bb; 6325 var lat3 = moveLat(lat1, lon1, d, b); 6326 var lon3 = moveLon(lat1, lon1, d, b); 6327 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6328 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6329 } 6330 var n = 1 + Math.round(clubSpreadR); 6331 for (var i = 1; i <= n; ++i) 6332 { 6333 var d = dist+clubSpreadOR+(i/n)*(clubSpreadO-clubSpreadOR); 6334 var b = bearingRO+(i/n)*(bearing-bearingRO); 6335 d -= d*(b-bearing)*(b-bearing)/bb; 6336 var lat3 = moveLat(lat1, lon1, d, b); 6337 var lon3 = moveLon(lat1, lon1, d, b); 6338 var h3 = (terrainMode == 1 ? 0 : getProVisualizerTerrainHeight(lat3, lon3)); 6339 coords.push(Cesium.Cartesian3.fromDegrees(lon3, lat3, h3)); 6340 } 6341 var line = viewer.entities.add({ polyline : { positions: coords, width: 1.0, material: color } }); 6342 mapLines2.push(line); 6343 } 6344 } 6345 } 6346} 6347 6348function shotHelp() 6349{ 6350 setMode(0);
6351 alert("Use the Next Hole/Shot and Prev Hole/Shot buttons to navigate around the course, and use the 2D/3D button to switch between 2D and 3D mode. Distances are based on the tee, pin and target positions shown, and may not agree exactly with the official scorecard. Elevation is based on USGS National Map Elevation Point Query Service in the United States and the SRTM 1 Arc-Second Global Dataset and ASTER GDEM V3 Dataset elsewhere, as indicated. Click on the link for more information regarding coverage and accuracy. Plays like is based on horizontal distance plus/minus 1 yard for each yard of elevation gain/loss, minus 3% for each 1000 metres above sea level. This is only approximate as it depends on the exact trajectory and spin of each shot, but hopefully it gives a reasonably accurate estimate in most cases. Clubbing is based on My Profile."); 6352} 6353 6354function dispersionHelp() 6355{ 6356 setMode(0); 6357 alert("Dispersion is based on PGA Tour statistics and may not accurately reflect your individual dispersion profile. Specifically, it is based on a distance error of -8% to +6% and a direction error of 3 degrees left/right at 100 yards, rising to 5 degrees at 200 yards and 7 degrees at 300 yards. A random error of up to 5% is also added to simulate unpredictable wind and bounce. Distance, direction, wind and bounce are all modelled using a fourth order approximation to a normal distribution, i.e. by taking the average of 4 random numbers in the given range. This is only approximate, and every player is different, but we believe it gives a fairly good representation of pro-level dispersion patterns in good weather conditions. Select Pro+X% to increase the dispersion by the specified factor, and N/R/L to switch between neutral, right-handed and left-handed patterns. Use the Custom option if you want to specify the exact width, depth and shape of each individual club."); 6358} 6359 6360function spreadHelp() 6361{ 6362 setMode(0); 6363 alert("Spread puts a box around your chosen dispersion pattern (Pro+X% or Custom). If you have selected Pro+X%, the box will be based on our Pro+X% dispersion model. If you have selected Custom, the box will be exactly as specified by the Left/Right/Long/Short dispersion settings in My Profile. Note that in reality dispersion doesn't have a precise boundary, so there will be a small percentage of shots that stray outside the box. If you use the Custom option, you should set Left/Right/Long/Short to be your estimated spread at your preferred confidence level, e.g. 90%, 95%, etc."); 6364} 6365 6366function slopesHelp() 6367{ 6368 setMode(0); 6369 alert("Slopes and contours are based on lidar survey data. We believe the data is fairly accurate in most cases. However, please note that the data can often be several years old, and might not accurately represent the current state of the course. Therefore, it is essential that you verify the accuracy of the data before using it. When the slopes are visible, you can right click anywhere on the green to set the hole location, and then left click anywhere on the green to simulate a putt to the specified hole location. Green speed is the stimp rating. Putt speed is the speed the ball is rolling when it reaches the hole. Double or triple right click anywhere on the green to simulate 10-foot putts from all directions. Left click a hole location to remove it. Right click a putt start point to remove it. Note that putting simulations are not saved, so please capture a screenshot if you wish to save the image."); 6370} 6371 6372function moveLat(lat, lon, dist, bearing) 6373{ 6374 var radians = Math.PI * (90 - bearing) / 180; 6375 var dLat = Math.sin(radians) * dist * 360 / 40000000; 6376 return lat + dLat; 6377} 6378 6379function moveLon(lat, lon, dist, bearing) 6380{ 6381 var radians = Math.PI * (90 - bearing) / 180; 6382 var k = 1 / Math.cos(Math.PI * lat / 180); 6383 var dLon = Math.cos(radians) * dist * k * 360 / 40000000; 6384 return lon + dLon; 6385} 6386 6387function GETeeHandler() 6388{ 6389 setMode(0); 6390 var courseID = currentCourseID(); 6391 var hole = currentHole(); 6392 if (hole < 1) 6393 { 6394 alert("Please select hole."); 6395 return; 6396 } 6397 var lat1 = getCourseHoleTeeLat(courseID, hole); 6398 var lon1 = getCourseHoleTeeLon(courseID, hole); 6399 if (lat1 == 0 && lon1 == 0) 6400 { 6401 alert("Please set tee position."); 6402 return; 6403 } 6404 var lat2 = getCourseHoleTargetLat(courseID, hole, 1); 6405 var lon2 = getCourseHoleTargetLon(courseID, hole, 1); 6406 if (lat2 == 0 && lon2 == 0) 6407 { 6408 lat2 = getCourseHolePinLat(courseID, hole); 6409 lon2 = getCourseHolePinLon(courseID, hole); 6410 } 6411 if (lat2 == 0 && lon2 == 0) 6412 { 6413 alert("Please set pin position."); 6414 return; 6415 } 6416 var i = document.getElementById("geheightselect").selectedIndex; 6417 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6418 var height = 24; 6419 var angle = 70; 6420 var range = 90; 6421 var desc = "Tee High"; 6422 if (i == 1) 6423 { 6424 height = 18; 6425 angle = 75; 6426 range = 70; 6427 desc = "Tee Mid" 6428 } 6429 else if (i == 2) 6430 { 6431 height = 12; 6432 angle = 80; 6433 range = 50; 6434 desc = "Tee Low" 6435 } 6436 else if (i == 3) 6437 { 6438 height = 0; 6439 angle = 0; 6440 range = 200; 6441 desc = "Tee 2D" 6442 } 6443 GEViewLink(lat1, lon1, height, bearing, angle, range, GEViewName(courseID, hole, desc)); 6444} 6445 6446function GET1Handler() 6447{ 6448 setMode(0); 6449 var courseID = currentCourseID(); 6450 var hole = currentHole(); 6451 if (hole < 1) 6452 { 6453 alert("Please select hole."); 6454 return; 6455 } 6456 var lat1 = getCourseHoleTargetLat(courseID, hole, 1); 6457 var lon1 = getCourseHoleTargetLon(courseID, hole, 1); 6458 if (lat1 == 0 && lon1 == 0) 6459 { 6460 alert("Please set target 1."); 6461 return; 6462 } 6463 var lat2 = getCourseHoleTargetLat(courseID, hole, 2); 6464 var lon2 = getCourseHoleTargetLon(courseID, hole, 2); 6465 if (lat2 == 0 && lon2 == 0) 6466 { 6467 lat2 = getCourseHolePinLat(courseID, hole); 6468 lon2 = getCourseHolePinLon(courseID, hole); 6469 } 6470 if (lat2 == 0 && lon2 == 0) 6471 { 6472 alert("Please set pin position."); 6473 return; 6474 } 6475 var i = document.getElementById("geheightselect").selectedIndex; 6476 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6477 var height = 15; 6478 var angle = 75; 6479 var range = 70; 6480 var desc = "Target 1 High"; 6481 if (i == 1) 6482 { 6483 height = 10; 6484 angle = 80; 6485 range = 50; 6486 desc = "Target 1 Mid"; 6487 } 6488 else if (i == 2) 6489 { 6490 height = 5; 6491 angle = 85; 6492 range = 30; 6493 desc = "Target 1 Low"; 6494 } 6495 else if (i == 3) 6496 { 6497 height = 0; 6498 angle = 0; 6499 range = 400; 6500 desc = "Target 1 2D" 6501 } 6502 GEViewLink(lat1, lon1, height, bearing, angle, range, GEViewName(courseID, hole, desc)); 6503} 6504 6505function GET2Handler() 6506{ 6507 setMode(0); 6508 var courseID = currentCourseID(); 6509 var hole = currentHole(); 6510 if (hole < 1) 6511 { 6512 alert("Please select hole."); 6513 return; 6514 } 6515 var lat1 = getCourseHoleTargetLat(courseID, hole, 2); 6516 var lon1 = getCourseHoleTargetLon(courseID, hole, 2); 6517 if (lat1 == 0 && lon1 == 0) 6518 { 6519 alert("Please set target 2."); 6520 return; 6521 } 6522 var lat2 = getCourseHoleTargetLat(courseID, hole, 3); 6523 var lon2 = getCourseHoleTargetLon(courseID, hole, 3); 6524 if (lat2 == 0 && lon2 == 0) 6525 { 6526 lat2 = getCourseHolePinLat(courseID, hole); 6527 lon2 = getCourseHolePinLon(courseID, hole); 6528 } 6529 if (lat2 == 0 && lon2 == 0) 6530 { 6531 alert("Please set pin position."); 6532 return; 6533 } 6534 var i = document.getElementById("geheightselect").selectedIndex; 6535 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6536 var height = 15; 6537 var angle = 75; 6538 var range = 70; 6539 var desc = "Target 2 High"; 6540 if (i == 1) 6541 { 6542 height = 10; 6543 angle = 80; 6544 range = 50; 6545 desc = "Target 2 Mid"; 6546 } 6547 else if (i == 2) 6548 { 6549 height = 5; 6550 angle = 85; 6551 range = 30; 6552 desc = "Target 2 Low"; 6553 } 6554 else if (i == 3) 6555 { 6556 height = 0; 6557 angle = 0; 6558 range = 300; 6559 desc = "Target 2 2D" 6560 } 6561 GEViewLink(lat1, lon1, height, bearing, angle, range, GEViewName(courseID, hole, desc)); 6562} 6563 6564function GET3Handler() 6565{ 6566 setMode(0); 6567 var courseID = currentCourseID(); 6568 var hole = currentHole(); 6569 if (hole < 1) 6570 { 6571 alert("Please select hole."); 6572 return; 6573 } 6574 var lat1 = getCourseHoleTargetLat(courseID, hole, 3); 6575 var lon1 = getCourseHoleTargetLon(courseID, hole, 3); 6576 if (lat1 == 0 && lon1 == 0) 6577 { 6578 alert("Please set target 3."); 6579 return; 6580 } 6581 lat2 = getCourseHolePinLat(courseID, hole); 6582 lon2 = getCourseHolePinLon(courseID, hole); 6583 if (lat2 == 0 && lon2 == 0) 6584 { 6585 alert("Please set pin position."); 6586 return; 6587 } 6588 var i = document.getElementById("geheightselect").selectedIndex; 6589 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6590 var height = 15; 6591 var angle = 75; 6592 var range = 70; 6593 var desc = "Target 3 High"; 6594 if (i == 1) 6595 { 6596 height = 10; 6597 angle = 80; 6598 range = 50; 6599 desc = "Target 3 Mid"; 6600 } 6601 else if (i == 2) 6602 { 6603 height = 5; 6604 angle = 85; 6605 range = 30; 6606 desc = "Target 3 Low"; 6607 } 6608 else if (i == 3) 6609 { 6610 height = 0; 6611 angle = 0; 6612 range = 300; 6613 desc = "Target 3 2D" 6614 } 6615 GEViewLink(lat1, lon1, height, bearing, angle, range, GEViewName(courseID, hole, desc)); 6616} 6617 6618function GEGreenHandler() 6619{ 6620 setMode(0); 6621 var courseID = currentCourseID(); 6622 var hole = currentHole(); 6623 if (hole < 1) 6624 { 6625 alert("Please select hole."); 6626 return; 6627 } 6628 var lat2 = getCourseHolePinLat(courseID, hole); 6629 var lon2 = getCourseHolePinLon(courseID, hole); 6630 if (lat2 == 0 && lon2 == 0) 6631 { 6632 alert("Please set pin position."); 6633 return; 6634 } 6635 var lat1 = getCourseHoleTargetLat(courseID, hole, 3); 6636 var lon1 = getCourseHoleTargetLon(courseID, hole, 3); 6637 if (lat1 == 0 && lon1 == 0) 6638 { 6639 lat1 = getCourseHoleTargetLat(courseID, hole, 2); 6640 lon1 = getCourseHoleTargetLon(courseID, hole, 2); 6641 } 6642 if (lat1 == 0 && lon1 == 0) 6643 { 6644 lat1 = getCourseHoleTargetLat(courseID, hole, 1); 6645 lon1 = getCourseHoleTargetLon(courseID, hole, 1); 6646 } 6647 if (lat1 == 0 && lon1 == 0) 6648 { 6649 lat1 = getCourseHoleTeeLat(courseID, hole); 6650 lon1 = getCourseHoleTeeLon(courseID, hole); 6651 } 6652 if (lat1 == 0 && lon1 == 0) 6653 { 6654 alert("Please set tee position."); 6655 return; 6656 } 6657 var i = document.getElementById("geheightselect").selectedIndex; 6658 var bearing = geogBearing(lat1, lon1, lat2, lon2); 6659 var height = 0; 6660 var angle = 75; 6661 var range = 70; 6662 var desc = "Green High"; 6663 if (i == 1) 6664 { 6665 height = 0; 6666 angle = 80; 6667 range = 50; 6668 desc = "Green Mid"; 6669 } 6670 else if (i == 2) 6671 { 6672 height = 0; 6673 angle = 85; 6674 range = 30; 6675 desc = "Green Low"; 6676 } 6677 else if (i == 3) 6678 { 6679 height = 0; 6680 angle = 0; 6681 range = 200; 6682 desc = "Green 2D" 6683 } 6684 GEViewLink(lat2, lon2, height, bearing, angle, range, GEViewName(courseID, hole, desc)); 6685} 6686 6687function GEViewName(courseID, hole, view) 6688{ 6689 var courseName = getCourseNameFromID(courseID); 6690 if (courseName == null || courseName == "") 6691 courseName = cleanString(document.getElementById("saveasinput").value); 6692 if (courseName == null || courseName == "") 6693 courseName = "NewCourse"; 6694 var h = hole.toString(); 6695 if (h.length < 2) h = "0" + h; 6696 return courseName + " " + h + " " + view; 6697} 6698 6699function GEViewLink(lat, lon, height, bearing, angle, range, name) 6700{ 6701 window.location.href = "flyto.php?" 6702 +"lat="+lat.toString() 6703 +"&lon="+lon.toString() 6704 +"&h="+height.toString() 6705 +"&b="+bearing.toString() 6706 +"&a="+angle.toString() 6707 +"&r="+range.toString() 6708 +"&n="+encodeURIComponent(name); 6709} 6710 6711function GEHelp() 6712{ 6713 alert("Use these buttons to quickly navigate to the required location in Google Earth. To get the most accurate available elevation and terrain, make sure you have 3D Buildings and Terrain selected in the Google Earth Layers, and adjust Tools > Options > Navigation > Fly-To Speed so that Google Earth moves at the required speed. These buttons work by generating small KML files with the required view parameters. If you associate KML files with Google Earth, then Google Earth will automatically open the KML file and fly to the required location when you click the button. The KML files also work with Esri ArcGIS Earth, which can be a good option in some regions where the Google Earth imagery/terrain isn't very good."); 6714} 6715 6716var weather = []; // Array of forecasts 6717var weatherIndex = -1; // Which forecast to show 6718var weatherIndex2 = -1; // Which time offset to show 6719var weatherRequests = 0; 6720var weatherWindSpeed = 0; 6721var weatherWindBearing = 0; 6722 6723function toggleWeather() 6724{ 6725 setMode(0); 6726 if (weatherIndex < 0) showWeather(true); 6727 else hideWeather(); 6728} 6729 6730function startWeatherHandler() 6731{ 6732 weatherIndex2 = 0; 6733 setWeatherData(); 6734} 6735 6736function nextWeatherHandler() 6737{ 6738 weatherIndex2 += 1; 6739 setWeatherData(); 6740} 6741 6742function prevWeatherHandler() 6743{ 6744 weatherIndex2 -= 1; 6745 setWeatherData(); 6746} 6747 6748function setWeatherData() 6749{ 6750 if (weatherIndex < 0) return; 6751 var w = weather[weatherIndex]; 6752 while (weatherIndex2 < 0) weatherIndex2 += 1; 6753 while (weatherIndex2 >= w.list.length) weatherIndex2 -= 1; 6754 var w2 = w.list[weatherIndex2]; 6755 weatherTempC = Math.round(w2.main.temp); 6756 weatherWindSpeed = Math.round(w2.wind.speed*3600/(getDistanceUnit() == "m" ? 1000.0 : 1609.344)); 6757 weatherWindBearing = w2.wind.deg + 180; 6758 while (weatherWindBearing < 0) weatherWindBearing += 360; 6759 while (weatherWindBearing > 360) weatherWindBearing -= 360; 6760 var div1 = document.getElementById('weatherdiv1'); 6761 var div3 = document.getElementById('weatherdiv3'); 6762 var div4 = document.getElementById('weatherdiv4'); 6763 var div5 = document.getElementById('weatherdiv5'); 6764 var div6 = document.getElementById('weatherdiv6'); 6765 div1.innerHTML = "<img src='https://openweathermap.org/img/w/"+w2.weather[0].icon+".png'/>"; 6766 div3.innerHTML = weatherWindSpeed.toString() + (getDistanceUnit() == "m" ? " KPH" : " MPH"); 6767 div4.innerHTML = weatherTempC.toString()+"°C";; 6768 div5.innerHTML = w2.localDayTimeStr; 6769 div6.innerHTML = w.timezoneStr; 6770} 6771 6772function hideWeather() 6773{ 6774 weatherIndex = -1; 6775 document.getElementById('weatherdiv1').style.display = 'none'; 6776 document.getElementById('weatherdiv2').style.display = 'none'; 6777 document.getElementById('weatherdiv3').style.display = 'none'; 6778 document.getElementById('weatherdiv4').style.display = 'none'; 6779 document.getElementById('weatherdiv5').style.display = 'none'; 6780 document.getElementById('weatherdiv6').style.display = 'none'; 6781 document.getElementById('weatherdiv7').style.display = 'none'; 6782 document.getElementById('weatherdiv8').style.display = 'none'; 6783} 6784 6785function showWeather(allowGet) 6786{ 6787 weatherIndex = -1; 6788 var courseID = currentCourseID(); 6789 var hole = 0; 6790 do 6791 { 6792 hole += 1; 6793 lat = getCourseHoleTeeLat(courseID, 1); 6794 lon = getCourseHoleTeeLon(courseID, 1); 6795 } 6796 while (lat == 0 && lon == 0 && hole < 18); 6797 if (lat == 0 && lon == 0) 6798 { 6799 alert("Please set 1st tee position."); 6800 return; 6801 } 6802 var minTime = Date.now() - 3600*1000; 6803 var minLat = moveLat(lat, lon, 5000, 180); 6804 var minLon = moveLon(lat, lon, 5000, 270); 6805 var maxLat = moveLat(lat, lon, 5000, 0); 6806 var maxLon = moveLon(lat, lon, 5000, 90); 6807 var i = 0; 6808 var found = false;
6809 while (!found && i < weather.length) 6810 { 6811 if (weather[i].reqTime > minTime && weather[i].reqLat > minLat && weather[i].reqLat < maxLat && weather[i].reqLon > minLon && weather[i].reqLon < maxLon) found = true; 6812 else ++i; 6813 } 6814 if (found) 6815 { 6816 weatherIndex = i; 6817 document.getElementById('weatherdiv1').innerHTML = ''; 6818 document.getElementById('weatherdiv3').innerHTML = ''; 6819 document.getElementById('weatherdiv4').innerHTML = ''; 6820 document.getElementById('weatherdiv5').innerHTML = ''; 6821 document.getElementById('weatherdiv6').innerHTML = ''; 6822 document.getElementById('weatherdiv1').style.display = ''; 6823 document.getElementById('weatherdiv2').style.display = ''; 6824 document.getElementById('weatherdiv3').style.display = ''; 6825 document.getElementById('weatherdiv4').style.display = ''; 6826 document.getElementById('weatherdiv5').style.display = ''; 6827 document.getElementById('weatherdiv6').style.display = ''; 6828 document.getElementById('weatherdiv7').style.display = ''; 6829 document.getElementById('weatherdiv8').style.display = ''; 6830 startWeatherHandler(); 6831 return; 6832 } 6833 if (!allowGet) 6834 { 6835 alert("Weather not available."); 6836 return; 6837 } 6838 getWeather(lat, lon); 6839} 6840 6841function getWeather(lat, lon) 6842{ 6843 if (weatherRequests >= 10) 6844 { 6845 alert('Request limit exceeded.'); 6846 return; 6847 } 6848 if (lat < -75 || lat > 75) 6849 { 6850 alert('Weather not available for this location.'); 6851 return; 6852 } 6853 while (lon < -180.0) lon += 360.0; 6854 while (lon > 180.0) lon -= 360.0; 6855 weatherRequests += 1; 6856 var req = "https://api.openweathermap.org/data/2.5/forecast?lat=" + lat.toString() + "&lon=" + lon.toString() + "&appid=58e7b34079d0f22aa5fd07d1cab41d19&units=metric"; 6857 if (window.XMLHttpRequest) 6858 { 6859 var xhr = new XMLHttpRequest(); 6860 xhr.onreadystatechange = function() 6861 { 6862 if (xhr.readyState == 4 && xhr.status == 200) 6863 { 6864 var w = JSON.parse(xhr.responseText); 6865 if (w == null || w.cod == null || w.cod != "200" || w.list == null || w.list.length < 10 6866 || w.list[0].wind == null || w.list[0].wind.speed == null || w.list[0].wind.deg == null 6867 || w.list[0].main == null || w.list[0].main.temp == null) 6868 { 6869 alert("Weather not available for this location."); 6870 hideWeather(); 6871 return; 6872 } 6873 w.reqLat = lat; 6874 w.reqLon = lon; 6875 w.reqTime = Date.now(); 6876 w.utcOffsetHours = Math.round((12 * lon) / 180); 6877 w.timezoneStr = (w.utcOffsetHours >= 0 ? "UTC+" + w.utcOffsetHours.toString() : "UTC-" + (-(w.utcOffsetHours)).toString())+"h"; 6878 for (var i = 0; i < w.list.length; ++i) 6879 { 6880 var localTime = w.list[i].dt + w.utcOffsetHours * 3600; 6881 var hour = Math.floor(localTime / 3600) % 24; 6882 var day = Math.floor(localTime / 86400) % 7; 6883 var s1 = (hour < 10 ? "0" : "") + hour.toString() + "00"; 6884 var s2 = ""; 6885 if (day == 0) s2 = "Thu"; 6886 else if (day == 1) s2 = "Fri"; 6887 else if (day == 2) s2 = "Sat"; 6888 else if (day == 3) s2 = "Sun"; 6889 else if (day == 4) s2 = "Mon"; 6890 else if (day == 5) s2 = "Tue"; 6891 else if (day == 6) s2 = "Wed"; 6892 w.list[i].localDayTimeStr = s2 + " " + s1; 6893 } 6894 weather.push(w); 6895 showWeather(false); 6896 } 6897 } 6898 xhr.open("GET", req, true); 6899 xhr.send(); 6900 } 6901} 6902 6903function showSymbolsPanel() 6904{ 6905 setMode(0); 6906 var x = document.getElementById('symbolsdiv'); 6907 var y = x.style.display; 6908 if (y == '') x.style.display = 'none'; 6909 else x.style.display = ''; 6910} 6911 6912var activeSymbol = ''; 6913 6914function symbolClickHandler(symbol) 6915{ 6916 if (mode == 9 && symbol == activeSymbol) 6917 { 6918 activeSymbol = ''; 6919 setMode(0); 6920 return; 6921 } 6922 if (symbol == 'Z' || symbol == 'ZZ') 6923 { 6924 var courseID = currentCourseID(); 6925 var hole = currentHole(); 6926 var n = getCourseHoleSymbols(courseID, hole); 6927 if (n < 1) 6928 { 6929 activeSymbol = ''; 6930 setMode(0); 6931 return; 6932 } 6933 if (symbol == 'ZZ') 6934 { 6935 activeSymbol = ''; 6936 setMode(0); 6937 if (!confirm("Clear all symbols?")) 6938 return; 6939 for (var i = n - 1; i >= 0; --i) 6940 deleteCourseHoleSymbol(courseID, hole, i); 6941 refreshView(); 6942 return; 6943 } 6944 } 6945 else if (symbol != 'T' && symbol != 'X') 6946 { 6947 var courseID = currentCourseID(); 6948 var hole = currentHole(); 6949 var n = getCourseHoleSymbols(courseID, hole); 6950 var i = 0; 6951 while (i < n && decodeSymbol(getCourseHoleSymbol(courseID, hole, i)).symbol != symbol) 6952 i += 1; 6953 while (i < n - 1) 6954 { 6955 setCourseHoleSymbol(courseID, hole, i, getCourseHoleSymbol(courseID, hole, i+1)); 6956 i += 1; 6957 } 6958 if (i < n) 6959 { 6960 deleteCourseHoleSymbol(courseID, hole, i); 6961 refreshView(); 6962 } 6963 } 6964 activeSymbol = symbol; 6965 setMode(9); 6966} 6967
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.