1/* 2 * HTML Color names: https://www.w3schools.com/colors/colors_names.asp 3 * 4 * Attributes: 5 * Values: width, height, BSP, HDG, AWS, AWA, TWS, TWA, TWD, CDR, CSP, Leeway, CMG, VMG, B2WP, Decl, dev 6 * Booleans: displayCurrent, displayLabels, displayVMG, vmgOnWind or vmgOnWP, with D, d, W 7 * Options: BoatShape (mono, cata, tri, plane), WPName 8 * 9 * Designed to represent the data coming from the NavServer. 10 * NOTE: The consistency of the data is not maintained by the Component!! 11 * Example: 12{ 13 "Damping": 30, 14 "HDG Offset": 0, 15 "To Waypoint": "RANGI ", 16 "CDR": { 17 "angle": 115.39230004857473 18 }, 19 "Daily": { 20 "distance": 12.2 21 }, 22 "Max Leeway": 10, 23 "VMG on Wind": -3.8584310339481522, 24 "HDG c.": { 25 "angle": 213 26 }, 27 "CMG": { 28 "angle": 222.01692220976113 29 }, 30 "BSP": { 31 "speed": 7.1 32 }, 33 "TWA": { 34 "angle": -130.38922223613739 35 }, 36 "TWD": { 37 "angle": 91 38 }, 39 "Current calculated with damping": { 40 "30000": { 41 "bufferLength": 30000, 42 "speed": { 43 "speed": 0.32396646536173773 44 }, 45 "direction": { 46 "angle": 48.891255868064114 47 }, 48 "nbPoints": 11, 49 "oldest": "Sun, 2010 Nov 21 22:07:44 UTC", 50 "latest": "Sun, 2010 Nov 21 22:07:44 UTC", 51 "len": 30000 52 }, 53 "60000": { 54 "bufferLength": 60000, 55 "speed": { 56 "speed": 0.32396646536173773 57 }, 58 "direction": { 59 "angle": 48.891255868064114 60 }, 61 "nbPoints": 11, 62 "oldest": "Sun, 2010 Nov 21 22:07:14 UTC", 63 "latest": "Sun, 2010 Nov 21 22:07:44 UTC", 64 "len": 30000 65 }, 66 "600000": { 67 "bufferLength": 600000, 68 "speed": { 69 "speed": 0.32396646536173773 70 }, 71 "direction": { 72 "angle": 48.891255868064114 73 }, 74 "nbPoints": 11, 75 "oldest": "Sun, 2010 Nov 21 22:07:14 UTC", 76 "latest": "Sun, 2010 Nov 21 22:07:44 UTC", 77 "len": 30000 78 } 79 }, 80 "Position": { 81 "lat": -9.108366666666667, 82 "lng": -140.20933333333332 83 }, 84 "Log": { 85 "distance": 3013 86 }, 87 "Solar Time": { 88 "date": "Nov 21, 2010, 4:46:56 AM", 89 "fmtDate": { 90 "epoch": 0, 91 "year": 0, 92 "month": 0, 93 "day": 0, 94 "hour": 12, 95 "min": 46, 96 "sec": 56 97 } 98 }, 99 "BSP Factor": 1, 100 "Set and Drift": { 101 "speed": 0.32, 102 "angle": 49 103 }, 104 "From Waypoint": "", 105 "TWS": { 106 "speed": 18.5 107 }, 108 "GPS Time": { 109 "date": "Nov 21, 2010, 2:07:47 PM", 110 "fmtDate": { 111 "epoch": 0, 112 "year": 0, 113 "month": 0, 114 "day": 0, 115 "hour": 22, 116 "min": 7, 117 "sec": 47 118 } 119 }, 120 "Distance to WP": { 121 "distance": 561.7 122 }, 123 "AWS Factor": 1, 124 "Water Temperature": { 125 "temperature": 26.5 126 }, 127 "Small Distance": 0.06425312432932662, 128 "NMEA": "$CCVDR,49.0,T,39.0,M,0.32,N*0F\r\n", 129 "NMEA_AS_IS": { 130 "VLW": "$IIVLW,03013,N,012.2,N*53\r", 131 "VHW": "$IIVHW,,,213,M,07.1,N,,*62\r", 132 "VDR": "$CCVDR,49.0,T,39.0,M,0.32,N*0F\r\n", 133 "GLL": "$IIGLL,0906.498,S,14012.558,W,220745,A,A*5D\r", 134 "RMB": "$IIRMB,A,3.00,R,,RANGI ,,,,,561.70,230,06.7,V,A*02\r", 135 "RMC": "$IIRMC,220747,A,0906.502,S,14012.560,W,06.9,215,211110,10,E,A*00\r", 136 "DPT": "$IIDPT,001.3,+0.7,*42\r", 137 "HDG": "$IIHDG,211,,,10,E*11\r", 138 "MWV": "$CCMWV,230.0,T,018.5,N,A*36\r\n", 139 "VWR": "$IIVWR,109,L,15.8,N,,,,*7B\r", 140 "MTW": "$IIMTW,+26.5,C*39\r", 141 "VWT": "$CCVWT,130.4,L,18.5,N,9.5,M,34.3,K*53\r\n", 142 "MWD": "$CCMWD,091.0,T,081.0,M,18.5,N,9.5,M*75\r\n" 143 }, 144 "D": { 145 "angle": 10 146 }, 147 "XTE": { 148 "distance": 3 149 }, 150 "AWA": { 151 "angle": -109 152 }, 153 "Depth": { 154 "depthInMeters": 2 155 }, 156 "Bearing to WP": { 157 "angle": 230 158 }, 159 "W": { 160 "angle": 9.01692220976113 161 }, 162 "Speed to WP": { 163 "speed": 6.7 164 }, 165 "COG": { 166 "angle": 215 167 }, 168 "AWS": { 169 "speed": 15.8 170 }, 171 "HDG true": { 172 "angle": 222.01692220976113 173 }, 174 "AWA Offset": 0, 175 "CSP": { 176 "speed": 0.8796925520240182 177 }, 178 "d": { 179 "angle": -0.9830777902388692 180 }, 181 "Default Declination": { 182 "angle": 14 183 }, 184 "Deviation file name": "dp_2011_04_15.csv", 185 "HDG mag.": { 186 "angle": 212.01692220976113 187 }, 188 "SOG": { 189 "speed": 6.9 190 }, 191 "Leeway": { 192 "angle": 0 193 }, 194 "GPS Date & Time": { 195 "date": "Nov 21, 2010, 2:07:47 PM", 196 "epoch": 1290377267000, 197 "fmtDate": { 198 "epoch": 1290377267000, 199 "year": 2010, 200 "month": 11, 201 "day": 21, 202 "hour": 22, 203 "min": 7, 204 "sec": 47 205 } 206 }, 207 "WayPoint pos": { 208 "lat": 0, 209 "lng": 0 210 }, 211 "VMG to Waypoint": 6.664888201394572, 212 "Steer": "R" 213} 214 * 215 */ 216const boatOverviewVerbose = false;
217const BOAT_OVERVIEW_TAG_NAME = 'boat-overview'; 218 219const boatOverviewDefaultColorConfig = { 220 displayBackgroundGradient: { 221 from: 'silver', 222 to: 'lightgray' 223 }, 224 gridColor: 'gray', 225 226 twArrowColor: 'black', 227 bspArrowColor: 'red', 228 cmgArrowColor:'cyan', 229 awArrowColor: 'blue', 230 gpsWsArrowColor: 'coral', 231 vmgArrowColor: 'red', 232 dDWArrowColor: 'yellow', 233 currentArrowColor: 'royalblue', 234 boatFillColor: 'silver', 235 boatOutlineColor: 'blue', 236 237 nmeaDataDisplayColor: 'royalblue', 238 calculatedDataDisplayColor: 'darkcyan', 239 vmgDataDisplayColor: 'red', 240 dDWDataDisplayColor: 'yellow', 241 242}; 243 244import * as Utilities from "../utilities/Utilities.js"; 245 246/* global HTMLElement */ 247class BoatOverview extends HTMLElement { 248 249 static get observedAttributes() { // That's a big one... 250 return [ 251 "width", // Integer. Canvas width 252 "height", // Integer. Canvas height 253 "bsp", // Float. Boat Speed Numeric value, knots 254 "hdg", // Integer, True Heading [0..360] 255 "aws", // Float. App Wind Speed Numeric value, knots 256 "awa", // Integer. App Wind Angle [-180..180] Numeric value 257 "tws", // Float. True Wind Speed Numeric value, knots 258 "twa", // Integer. True Wind Angle [-180..180] Numeric value 259 "twd", // Integer. True Wind Direction [0..360] Numeric value 260 "vmg", // Float. Velocity Made Good value, knots 261 "sog", // Float. Speed Over Ground Numeric value, knots 262 "cog", // Integer. Course Over Ground [0..360] Numeric value 263 "cdr", // Integer. Current Direction [0..360] Numeric value 264 "csp", // Float. Current Speed Numeric value, knots 265 "lwy", // Float. Leeway [-180..180] Numeric value 266 "cmg", // Integer. Course Made Good [0..360] Numeric value 267 "b2wp", // Integer. Bearing to Next Way Point [0..360] Numeric value 268 "decl", // Float. Magnetic Declination Numeric value +/- 269 "dev", // Float. Magnetic deviation Numeric value +/- 270 "with-current", // Boolean. Draw current 271 "with-gps", // Boolean. Draw SOG & COG 272 "with-wind", // Boolean. Draw wind (app & true) 273 "with-true-wind", // Boolean. Draw true wind 274 "with-labels", // Boolean. Draw Labels on graphic 275 "with-vmg", // Boolean. Draw VMG 276 "vmg-on-wind", // Boolean. true: on Wind, false: on WayPoint 277 "with-w", // Boolean. Draw D & d 278 "boat-shape", // String. MONO, CATA, TRI, PLANE 279 "zoom-on-boat", // Float, enforce zoom in (>1) or out (<1). Default 1 280 "wp-name" // String. Next WayPoint name 281 282 ]; 283 } 284 285 constructor() { 286 super(); 287 this._shadowRoot = this.attachShadow({mode: 'open'}); // 'open' means it is accessible from external JavaScript. 288 // create and append a <canvas> 289 this.canvas = document.createElement("canvas"); 290 let fallbackElemt = document.createElement("h1"); 291 let content = document.createTextNode("This is a Boat Overview Web Component, on an HTML5 canvas"); 292 fallbackElemt.appendChild(content); 293 this.canvas.appendChild(fallbackElemt); 294 this.shadowRoot.appendChild(this.canvas); 295 296 // Default values 297 this._width = 600; 298 this._height = 500; 299 300 this._bsp = 0; // Boat Speed 301 this._hdg = 0; // True Heading 302 this._aws = 0; // App Wind Speed 303 this._awa = 0; // App Wind Angle 304 this._tws = 0; // True Wind Speed 305 this._twa = 0; // True Wind Angle 306 this._twd = 0; // True Wind Direction 307 this._vmg = 0; // Velocity Made Good 308 this._cog = 0; // Course Over Ground 309 this._sog = 0; // Speed Over Ground 310 this._cdr = 0; // Current Direction 311 this._csp = 0; // Current Speed 312 this._lwy = 0; // Leeway 313 this._cmg = 0; // Course Made Good 314 this._b2wp = 0; // Bearing to WP 315 this._Decl = 0; // Declination 316 this._dev = 0; // deviation 317 318 this._zoom = 1.0; 319 320 this._withCurrent = false; 321 this._withLabels = true; 322 this._withGPS = true; 323 this._withWind = true; 324 this._withTrueWind = true; 325 this._withVMG = true; 326 this._vmgOnWind = true; // False means vmg on WP 327 this._withW = false; // Requires D and d 328 329 this._boatShape = "MONO"; // "CATA"; //"MONO"; // "TRI"; // "PLANE"; 330 this._wpName = ""; 331 332 this._previousClassName = ""; 333 this.boatOverviewColorConfig = boatOverviewDefaultColorConfig; 334 335 this.speedScale = 10; // Default value 336 337 this.WL_RATIO_COEFF = 0.75; // Ratio to apply to (3.5 * Width / Length) 338 this.BOAT_LENGTH = 100; // 50; 339 } 340 341 // Called whenever the custom element is inserted into the DOM. 342 connectedCallback() { 343 if (boatOverviewVerbose) { 344 console.log("connectedCallback invoked."); 345 } 346 this.repaint(); 347 } 348 349 // Called whenever the custom element is removed from the DOM. 350 disconnectedCallback() { 351 if (boatOverviewVerbose) { 352 console.log("disconnectedCallback invoked"); 353 } 354 } 355 356 // Called whenever an attribute is added, removed or updated. 357 // Only attributes listed in the observedAttributes property are affected. 358 attributeChangedCallback(attrName, oldVal, newVal) { 359 if (boatOverviewVerbose) { 360 console.log("attributeChangedCallback invoked on " + attrName + " from " + oldVal + " to " + newVal); 361 } 362 switch (attrName) { 363 case "width": 364 this._width = parseInt(newVal); 365 break; 366 case "height": 367 this._height = parseInt(newVal); 368 break; 369 case "bsp": 370 this._bsp = parseFloat(newVal); 371 break; 372 case "hdg": 373 this._hdg = parseInt(newVal); 374 break; 375 case "aws": 376 this._aws = parseFloat(newVal); 377 break; 378 case "awa": 379 this._awa = parseInt(newVal); 380 break; 381 case "tws": 382 this._tws = parseFloat(newVal); 383 break; 384 case "twa": 385 this._twa = parseInt(newVal); 386 break; 387 case "twd": 388 this._twd = parseInt(newVal); 389 break; 390 case "vmg": 391 this._vmg = parseFloat(newVal); 392 break; 393 case "sog": 394 this._sog = parseFloat(newVal); 395 break; 396 case "cog": 397 this._cog = parseInt(newVal); 398 break; 399 case "cdr": 400 this._cdr = parseInt(newVal); 401 break; 402 case "csp": 403 this._csp = parseFloat(newVal); 404 break; 405 case "lwy": 406 this._lwy = parseFloat(newVal); 407 break; 408 case "cmg": 409 this._cmg = parseInt(newVal); 410 break; 411 case "b2wp": 412 this._b2wp = parseFloat(newVal); 413 break; 414 case "decl": 415 this._Decl = parseFloat(newVal); 416 break; 417 case "dev": 418 this._dev = parseFloat(newVal); 419 break; 420 421 case "zoom-on-boat": 422 this._zoom = parseFloat(newVal); 423 break; 424 425 case "with-current": 426 this._withCurrent = (newVal === 'true'); 427 break; 428 case "with-gps":
429 this._withGPS = (newVal === 'true'); 430 break; 431 case "with-true-wind": 432 this._withTrueWind = (newVal === 'true'); 433 break; 434 case "with-wind": 435 this._withWind = (newVal === 'true'); 436 break; 437 case "with-labels": 438 this._withLabels = (newVal === 'true'); 439 break; 440 case "with-vmg": 441 this._withVMG = (newVal === 'true'); 442 break; 443 case "vmg-on-wind": 444 this._vmgOnWind = (newVal === 'true'); 445 break; 446 case "with-w": 447 this._withW = (newVal === 'true'); 448 break; 449 450 case "boat-shape": 451 console.log('BoatShape changing'); 452 if (newVal === 'MONO' || newVal === 'CATA' || newVal === 'TRI' || newVal === 'PLANE') { 453 this._boatShape = newVal; 454 } 455 break; 456 case "wp-name": 457 this._wpName = newVal; 458 break; 459 default: 460 break; 461 } 462 this.repaint(); 463 } 464 465 // Called whenever the custom element has been moved into a new document. 466 adoptedCallback() { 467 if (boatOverviewVerbose) { 468 console.log("adoptedCallback invoked"); 469 } 470 } 471 472 // Setters 473 set width(val) { 474 this.setAttribute("width", val); 475 } 476 477 set height(val) { 478 this.setAttribute("height", val); 479 } 480 481 set bsp(val) { 482 this.setAttribute("bsp", val); 483 } 484 485 set hdg(val) { 486 this.setAttribute("hdg", val); 487 } 488 489 set aws(val) { 490 this.setAttribute("aws", val); 491 } 492 493 set awa(val) { 494 this.setAttribute("awa", val); 495 } 496 497 set tws(val) { 498 this.setAttribute("tws", val); 499 } 500 501 set twa(val) { 502 this.setAttribute("twa", val); 503 } 504 505 set twd(val) { 506 this.setAttribute("twd", val); 507 } 508 509 set vmg(val) { 510 this.setAttribute("vmg", val); 511 } 512 513 set cog(val) { 514 this.setAttribute("cog", val); 515 } 516 517 set sog(val) { 518 this.setAttribute("sog", val); 519 } 520 521 set cdr(val) { 522 this.setAttribute("cdr", val); 523 } 524 525 set csp(val) { 526 this.setAttribute("csp", val); 527 } 528 529 set lwy(val) { 530 this.setAttribute("lwy", val); 531 } 532 533 set cmg(val) { 534 this.setAttribute("cmg", val); 535 } 536 537 set b2wp(val) { 538 this.setAttribute("b2wp", val); 539 } 540 541 set Decl(val) { 542 this.setAttribute("decl", val); 543 } 544 545 set dev(val) { 546 this.setAttribute("dev", val); 547 } 548 549 set zoomOnBoat(val) { 550 this.setAttribute("zoom-on-boat", val); 551 } 552 553 set withGPS(val) { 554 this.setAttribute("with-gps", val); 555 } 556 557 set withCurrent(val) { 558 this.setAttribute("with-current", val); 559 } 560 561 set withTrueWind(val) { 562 this.setAttribute("with-true-wind", val); 563 } 564 565 set withWind(val) { 566 this.setAttribute("with-wind", val); 567 } 568 569 set withLabels(val) { 570 this.setAttribute("with-labels", val); 571 } 572 573 set withVMG(val) { 574 this.setAttribute("with-vmg", val); 575 } 576 577 set vmgOnWind(val) { 578 this.setAttribute("vmg-on-wind", val); 579 } 580 581 set withW(val) { 582 this.setAttribute("with-w", val); 583 } 584 585 set wpName(val) { 586 this.setAttribute("wp-name", val); 587 } 588 589 set boatShape(val) { 590 this.setAttribute("boat-shape", val); 591 } 592 593 set shadowRoot(val) { 594 this._shadowRoot = val; 595 } 596 597 // Getters 598 get width() { 599 return this._width; 600 } 601 602 get height() { 603 return this._height; 604 } 605 606 get bsp() { 607 return this._bsp; 608 } 609 610 get hdg() { 611 return this._hdg; 612 } 613 614 get aws() { 615 return this._aws; 616 } 617 618 get awa() { 619 return this._awa; 620 } 621 622 get twa() { 623 return this._twa; 624 } 625 626 get tws() { 627 return this._tws; 628 } 629 630 get twd() { 631 return this._twd; 632 } 633 634 get vmg() { 635 return this._vmg; 636 } 637 638 get cog() { 639 return this._cog; 640 } 641 642 get sog() { 643 return this._sog; 644 } 645 646 get cdr() { 647 return this._cdr; 648 } 649 650 get csp() { 651 return this._csp; 652 } 653 654 get lwy() { 655 return this._lwy; 656 } 657 658 get cmg() { 659 return this._cmg; 660 } 661 662 get b2wp() { 663 return this._b2wp; 664 } 665 666 get Decl() { 667 return this._Decl; 668 } 669 670 get dev() { 671 return this._dev; 672 } 673 674 get zoomOnBoat() { 675 return this._zoom; 676 } 677 678 get withGPS() { 679 return this._withGPS; 680 } 681 682 get withCurrent() { 683 return this._withCurrent; 684 } 685 686 get withTrueWind() { 687 return this._withTrueWind; 688 } 689 690 get withWind() { 691 return this._withWind; 692 } 693 694 get withLabels() { 695 return this._withLabels; 696 } 697 698 get withVMG() { 699 return this._withVMG; 700 } 701 702 get vmgOnWind() { 703 return this._vmgOnWind; 704 } 705 706 get withW() { 707 return this._withW; 708 } 709 710 get wpName() { 711 return this._wpName; 712 } 713 714 get boatShape() { 715 return this._boatShape; 716 } 717 718 get shadowRoot() { 719 return this._shadowRoot; 720 } 721 722 // Component methods 723 getColorConfig(classNames) { 724 let colorConfig = boatOverviewDefaultColorConfig; 725 let classes = classNames.split(" "); 726 for (let cls = 0; cls < classes.length; cls++) { 727 let cssClassName = classes[cls]; 728 for (let s = 0; s < document.styleSheets.length; s++) { 729 // console.log("Walking though ", document.styleSheets[s]);
730 try { 731 for (let r = 0; document.styleSheets[s].cssRules !== null && r < document.styleSheets[s].cssRules.length; r++) { 732 let selector = document.styleSheets[s].cssRules[r].selectorText; 733 // console.log(">>> ", selector); 734 if (selector !== undefined && (selector === '.' + cssClassName || (selector.indexOf('.' + cssClassName) > -1 && selector.indexOf(BOAT_OVERVIEW_TAG_NAME) > -1))) { // Cases like "tag-name .className" 735 // console.log(" >>> Found it! [%s]", selector); 736 let cssText = document.styleSheets[s].cssRules[r].style.cssText; 737 let cssTextElems = cssText.split(";"); 738 cssTextElems.forEach((elem) => { 739 if (elem.trim().length > 0) { 740 let keyValPair = elem.split(":"); 741 let key = keyValPair[0].trim(); 742 let value = keyValPair[1].trim(); 743 switch (key) { 744 case '--display-background-gradient-from': 745 colorConfig.displayBackgroundGradient.from = value; 746 break; 747 case '--display-background-gradient-to': 748 colorConfig.displayBackgroundGradient.to = value; 749 break; 750 case '--grid-color': 751 colorConfig.gridColor = value; 752 break; 753 case '--tw-arrow-color': 754 colorConfig.twArrowColor = value; 755 break; 756 case '--bsp-arrow-color': 757 colorConfig.bspArrowColor = value; 758 break; 759 case '--cmg-arrow-color': 760 colorConfig.cmgArrowColor = value; 761 break; 762 case '--aw-arrow-color': 763 colorConfig.awArrowColor = value; 764 break; 765 case '--gps-ws-arrow-color': 766 colorConfig.gpsWsArrowColor = value; 767 break; 768 case '--vmg-arrow-color': 769 colorConfig.vmgArrowColor = value; 770 break; 771 case '--variation-arrow-color': 772 colorConfig.dDWArrowColor = value; 773 break; 774 case '--current-arrow-color': 775 colorConfig.currentArrowColor = value; 776 break; 777 case '--boat-fill-color': 778 colorConfig.boatFillColor = value; 779 break; 780 case '--boat-outline-color': 781 colorConfig.boatOutlineColor = value; 782 break; 783 case '--nmea-display-color': 784 colorConfig.nmeaDataDisplayColor = value; 785 break; 786 case '--calculated-color': 787 colorConfig.calculatedDataDisplayColor = value; 788 break; 789 case '--variation-display-color': 790 colorConfig.dDWDataDisplayColor = value; 791 break; 792 case '--vmg-display-color': 793 colorConfig.vmgDataDisplayColor = value; 794 break; 795 default: 796 break; 797 } 798 } 799 }); 800 } 801 } 802 } catch (err) { 803 // Absorb 804 } 805 } 806 } 807 return colorConfig; 808 } 809 810 repaint() { 811 this.drawBoatOverview(); 812 } 813 814 getCanvasCenter() { 815 let cw = this._width; 816 let ch = this._height; 817 let distFromRight = Math.min(cw, ch) / 2; 818 819 return { x: cw - distFromRight, y: ch / 2}; 820 } 821 822 drawTrueWind(context) { 823 let cWidth = this._width; 824 let cHeight = this._height; 825 826 let _twd = Math.toRadians(this._twd); 827 context.beginPath(); 828 let center = this.getCanvasCenter(); 829 let x = center.x; 830 let y = center.y; 831 832 let windLength = this._zoom * this._tws * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 833 let dX = windLength * Math.sin(_twd); 834 let dY = - windLength * Math.cos(_twd); 835 // create a new line object 836 let line = new Line(x + dX, y + dY, x, y); 837 // draw the line 838 context.strokeStyle = this.boatOverviewColorConfig.twArrowColor; 839 context.fillStyle = this.boatOverviewColorConfig.twArrowColor; 840 context.lineWidth = 5; 841 line.drawWithAnemoArrowheads(context); 842 context.closePath(); 843 if (this._withLabels) { 844 context.font= "bold 12px Arial"; 845 context.fillStyle = this.boatOverviewColorConfig.twArrowColor; 846 context.fillText("TWS:" + this._tws.toFixed(2) + " kts", x + dX, y + dY); 847 context.fillText("TWA:" + this._twa + "°", x + dX, y + dY + 14); 848 } 849 } 850 851 drawAppWind(context) { 852 let cWidth = this._width; 853 let cHeight = this._height; 854 855 let wd = this._hdg + this._awa; // Direction the wind is blowing TO 856 while (wd > 360) {
857 wd -= 360; 858 } 859 let _awd = Math.toRadians(wd); 860 context.beginPath(); 861 let center = this.getCanvasCenter(); 862 let x = center.x; 863 let y = center.y; 864 865 let windLength = this._zoom * this._aws * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 866 let dX = windLength * Math.sin(_awd); 867 let dY = - windLength * Math.cos(_awd); 868 // create a new line object 869 let line = new Line(x + dX, y + dY, x, y); 870 // draw the line 871 context.strokeStyle = this.boatOverviewColorConfig.awArrowColor; 872 context.fillStyle = this.boatOverviewColorConfig.awArrowColor; 873 context.lineWidth = 5; 874 line.drawWithAnemoArrowheads(context); 875 context.closePath(); 876 if (this._withLabels) { 877 context.font= "bold 12px Arial"; 878 context.fillStyle = this.boatOverviewColorConfig.awArrowColor; 879 context.fillText("AWS:" + this._aws + " kts", x + dX, y + dY); 880 context.fillText("AWA:" + this._awa + "°", x + dX, y + dY + 14); 881 } 882 } 883 884 drawBSP(context) { 885 if (this._bsp === 0) { 886 return; 887 } 888 889 let cWidth = this._width; 890 let cHeight = this._height; 891 892 let _hdg = Math.toRadians(this._hdg); 893 context.beginPath(); 894 let center = this.getCanvasCenter(); 895 let x = center.x; 896 let y = center.y; 897 898 let bspLength = this._zoom * this._bsp * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 899 let dX = bspLength * Math.sin(_hdg); 900 let dY = - bspLength * Math.cos(_hdg); 901 // create a new line object 902 let line = new Line(x, y, x + dX, y + dY); 903 // draw the line 904 context.strokeStyle = this.boatOverviewColorConfig.bspArrowColor; 905 context.lineWidth = 3; 906 line.drawHollowArrow(context); 907 context.closePath(); 908// let metrics = context.measureText(valueToDisplay); 909// len = metrics.width; 910 if (this._withLabels) { 911 context.font= "bold 12px Arial"; 912 context.fillStyle = this.boatOverviewColorConfig.bspArrowColor; 913 context.fillText("BSP:" + this._bsp.toFixed(2) + " kts", x + dX, y + dY); 914 context.fillText("HDG:" + this._hdg.toFixed(0) + "°", x + dX, y + dY + 14); 915 } 916 } 917 918 drawCMG(context) { 919 if (this._bsp === 0 || this._lwy === 0) { 920 return; 921 } 922 923 let cWidth = this._width; 924 let cHeight = this._height; 925 926 let _hdg = Math.toRadians(this._cmg); 927 context.beginPath(); 928 let center = this.getCanvasCenter(); 929 let x = center.x; 930 let y = center.y; 931 932 let bspLength = this._zoom * this._bsp * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 933 let dX = bspLength * Math.sin(_hdg); 934 let dY = - bspLength * Math.cos(_hdg); 935 // create a new line object 936 let line = new Line(x, y, x + dX, y + dY); 937 // draw the line 938 context.strokeStyle = this.boatOverviewColorConfig.cmgArrowColor; 939 context.fillStyle = this.boatOverviewColorConfig.cmgArrowColor; 940 context.lineWidth = 5; 941 line.drawWithArrowhead(context); 942 context.closePath(); 943 if (this._withLabels) { 944 context.font= "bold 12px Arial"; 945 context.fillStyle = this.boatOverviewColorConfig.calculatedDataDisplayColor; 946 context.fillText("CMG:" + this._cmg.toFixed(0) + "°", x + dX, y + dY); 947 } 948 } 949 950 drawNorths(context) { 951 if (this._bsp === 0 || (this._Decl === 0 && this._dev === 0)) { 952 return; 953 } 954 955 let cWidth = this._width; 956 let cHeight = this._height; 957 // Warning: Represent the Norths, not the headings!!! 958 let magNorth = this._Decl; 959 let compassNorth = magNorth + this._dev; 960 961 while (magNorth < 0) { 962 magNorth += 360; 963 } 964 while (compassNorth < 0) { 965 compassNorth += 360; 966 } 967 968 let _magNorth = Math.toRadians(magNorth); 969 let center = this.getCanvasCenter(); 970 let x = center.x; 971 let y = center.y; 972 973 let bspLength = this._zoom * this._bsp * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 974 // True North first 975 let line = new Line(x, y, x, y - (bspLength * 1.1)); 976 context.beginPath(); 977 context.strokeStyle = this.boatOverviewColorConfig.dDWArrowColor; 978 context.fillStyle = this.boatOverviewColorConfig.dDWArrowColor; 979 context.lineWidth = 1; 980 line.drawWithArrowhead(context); 981 context.closePath(); 982 if (this._withLabels) { 983 context.font= "12px Arial"; 984 context.fillStyle = this.boatOverviewColorConfig.dDWDataDisplayColor; 985 context.fillText("N", x, y - (bspLength * 1.1)); 986 } 987 988 context.beginPath(); 989 let dX = bspLength * Math.sin(_magNorth); 990 let dY = - bspLength * Math.cos(_magNorth); 991 // create a new line object 992 line = new Line(x, y, x + dX, y + dY); 993 // draw the line 994 context.lineWidth = 5; 995 line.drawWithArrowhead(context); 996 context.closePath(); 997 if (this._withLabels) { 998 context.font= "bold 12px Arial";
999 context.fillStyle = this.boatOverviewColorConfig.dDWDataDisplayColor; 1000 context.fillText("Mag N:" + magNorth.toFixed(0) + "°", x + dX, y + dY); 1001 } 1002 let _compassNorth = Math.toRadians(compassNorth); 1003 context.beginPath(); 1004 1005 dX = bspLength * 0.8 * Math.sin(_compassNorth); 1006 dY = - bspLength * 0.8 * Math.cos(_compassNorth); 1007 // create a new line object 1008 line = new Line(x, y, x + dX, y + dY); 1009 // draw the line 1010 context.strokeStyle = this.boatOverviewColorConfig.dDWArrowColor; 1011 context.fillStyle = this.boatOverviewColorConfig.dDWArrowColor; 1012 context.lineWidth = 5; 1013 line.drawWithArrowhead(context); 1014 context.closePath(); 1015 if (this._withLabels) { 1016 context.font= "bold 12px Arial"; 1017 context.fillStyle = this.boatOverviewColorConfig.dDWDataDisplayColor; 1018 context.fillText("Compass N:" + compassNorth.toFixed(0) + "°", x + dX, y + dY); 1019 } 1020 } 1021 drawSOG(context) { 1022 if (this._sog === 0) { 1023 return; 1024 } 1025 1026 let cWidth = this._width; 1027 let cHeight = this._height; 1028 1029 let _hdg = Math.toRadians(this._cog); 1030 context.beginPath(); 1031 let center = this.getCanvasCenter(); 1032 let x = center.x; 1033 let y = center.y; 1034 1035 let bspLength = this._zoom * this._sog * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1036 let dX = bspLength * Math.sin(_hdg); 1037 let dY = - bspLength * Math.cos(_hdg); 1038 // create a new line object 1039 let line = new Line(x, y, x + dX, y + dY); 1040 // draw the line 1041 context.strokeStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1042 context.fillStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1043 context.lineWidth = 5; 1044 line.drawWithArrowhead(context); 1045 context.closePath(); 1046 if (this._withLabels) { 1047 context.font= "bold 12px Arial"; 1048 context.fillStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1049 context.fillText("SOG:" + this._sog + " kts", x + dX, y + dY); 1050 context.fillText("COG:" + this._cog + "°", x + dX, y + dY + 14); 1051 context.lineWidth = 1; 1052 } 1053 } 1054 1055 drawVMG(context) { 1056 let cWidth = this._width; 1057 let cHeight = this._height; 1058 1059 let _hdg = 0; 1060 context.beginPath(); 1061 let center = this.getCanvasCenter(); 1062 let x = center.x; 1063 let y = center.y; 1064 1065 if (this.vmgOnWind) { 1066 _hdg = Math.toRadians(this._twd); 1067 } else { 1068 _hdg = Math.toRadians(this._b2wp); 1069 // Display WP direction 1070 context.strokeStyle = this.boatOverviewColorConfig.vmgArrowColor; 1071 context.fillStyle = this.boatOverviewColorConfig.vmgArrowColor; 1072 context.lineWidth = 1; 1073 let len = 0.75 * Math.min(cHeight, cWidth) / 2; 1074 let _dX = len * Math.sin(_hdg); 1075 let _dY = - len * Math.cos(_hdg); 1076 let wpLine = new Line(x, y, x + _dX, y + _dY); 1077 wpLine.drawWithArrowhead(context); 1078 context.fillText(this._wpName, x + _dX, y + _dY); 1079 } 1080 1081 let bspLength = this._zoom * this._vmg * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1082 let dX = bspLength * Math.sin(_hdg); 1083 let dY = - bspLength * Math.cos(_hdg); 1084 // create a new line object 1085 let line = new Line(x, y, x + dX, y + dY); 1086 // draw the line 1087 context.strokeStyle = this.boatOverviewColorConfig.vmgArrowColor; 1088 context.fillStyle = this.boatOverviewColorConfig.vmgArrowColor; 1089 context.lineWidth = 5; 1090 line.drawWithArrowhead(context); 1091 context.closePath(); 1092 if (this._withLabels) { 1093 context.save(); 1094 context.font= "bold 12px Arial"; 1095 context.fillStyle = this.boatOverviewColorConfig.vmgArrowColor; 1096 context.fillText("VMG:" + this._vmg.toFixed(2) + " kts", x + dX, y + dY); 1097 context.restore(); 1098 } 1099 if (context.setLineDash !== undefined) { 1100 context.setLineDash([5]); 1101 context.moveTo(x + dX, y + dY); 1102 let _cog = Math.toRadians(this._cog); 1103 let sogLength = this._zoom * this._sog * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1104 dX = sogLength * Math.sin(_cog); 1105 dY = - sogLength * Math.cos(_cog); 1106 context.lineTo(x + dX, y + dY); 1107 context.lineWidth = 1; 1108 context.stroke(); 1109 // Reset 1110 context.setLineDash([]); 1111 } 1112 } 1113 1114 drawCurrent(context) { 1115 if (this._csp === 0) { 1116 return; 1117 } 1118 1119 let cWidth = this._width; 1120 let cHeight = this._height; 1121 1122 let center = this.getCanvasCenter(); 1123 let x = center.x; 1124 let y = center.y; 1125 1126 let _cmg = Math.toRadians(this._cmg); 1127 let bspLength = this._zoom * this._bsp * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1128 let dXcmg = bspLength * Math.sin(_cmg); 1129 let dYcmg = - bspLength * Math.cos(_cmg); 1130 1131 let _cog = Math.toRadians(this._cog); 1132 let sogLength = this._zoom * this._sog * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1133 let dXcog = sogLength * Math.sin(_cog); 1134 let dYcog = - sogLength * Math.cos(_cog); 1135 1136 context.beginPath(); 1137 // create a new line object 1138 let line = new Line(x + dXcmg, y + dYcmg, x + dXcog, y + dYcog); 1139 // draw the line 1140 context.strokeStyle = this.boatOverviewColorConfig.currentArrowColor; 1141 context.fillStyle = this.boatOverviewColorConfig.currentArrowColor; 1142 context.lineWidth = 5; 1143 line.drawWithArrowhead(context); 1144 context.closePath(); 1145 if (this._withLabels) { 1146 context.font= "bold 12px Arial";
1147 context.fillStyle = this.boatOverviewColorConfig.currentArrowColor; 1148 context.fillText("CSP:" + this._csp.toFixed(2) + " kts", x + dXcog, y + dYcog + 28); // + 14 not to overlap the SOG/COG 1149 context.fillText("CDR:" + this._cdr.toFixed(0) + "°", x + dXcog, y + dYcog + 42); 1150 } 1151 } 1152 1153 drawVW(context) { // Velocity Wind 1154 if (this._sog === 0) { 1155 return; 1156 } 1157 1158 let cWidth = this._width; 1159 let cHeight = this._height; 1160 1161 let center = this.getCanvasCenter(); 1162 let x = center.x; 1163 let y = center.y; 1164 1165 let wd = this._hdg + this._awa; // Direction the wind is blowing TO 1166 while (wd > 360) { 1167 wd -= 360; 1168 } 1169 let _awd = Math.toRadians(wd); 1170 context.beginPath(); 1171 let awLength = this._zoom * this._aws * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1172 let dXaw = awLength * Math.sin(_awd); 1173 let dYaw = - awLength * Math.cos(_awd); 1174 1175 let _twd = Math.toRadians(this._twd); 1176 let twLength = this._zoom * this._tws * ((Math.min(cHeight, cWidth) / 2) / this.speedScale); 1177 let dXtw = twLength * Math.sin(_twd); 1178 let dYtw = - twLength * Math.cos(_twd); 1179 1180 context.beginPath(); 1181 // create a new line object 1182 let line = new Line(x + dXaw, y + dYaw, x + dXtw, y + dYtw); 1183 // draw the line 1184 context.strokeStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1185 context.fillStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1186 context.lineWidth = 5; 1187 line.drawWithAnemoArrowheads(context); 1188 context.closePath(); 1189 } 1190 1191 drawBoat(context, trueHeading) { 1192 let x = []; 1193 let y = [];// Half, length 1194 1195 let boatLength = this.BOAT_LENGTH * this._zoom; 1196 1197 if (this._boatShape === 'MONO') { 1198 // Width 1199 x.push(this.WL_RATIO_COEFF * 0); // Bow 1200 // Starboard 1201 x.push(this.WL_RATIO_COEFF * ( 1 * boatLength) / 7); 1202 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1203 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1204 x.push(this.WL_RATIO_COEFF * ( 1.5 * boatLength) / 7); // Transom, starboard 1205 // Port 1206 x.push(this.WL_RATIO_COEFF * (-1.5 * boatLength) / 7); // Transom, port 1207 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1208 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1209 x.push(this.WL_RATIO_COEFF * ( -1 * boatLength) / 7); 1210 1211 // Length 1212 y.push((-4 * boatLength) / 7); // Bow 1213 // Starboard 1214 y.push((-3 * boatLength) / 7); 1215 y.push((-1 * boatLength) / 7); 1216 y.push( (1 * boatLength) / 7); 1217 y.push( (3 * boatLength) / 7); 1218 // Port 1219 y.push( (3 * boatLength) / 7); 1220 y.push( (1 * boatLength) / 7); 1221 y.push((-1 * boatLength) / 7); 1222 y.push((-3 * boatLength) / 7); 1223 1224 } else if (this._boatShape === 'CATA') { 1225 x.push(this.WL_RATIO_COEFF * 0); // Arm, front, center 1226 // Starboard 1227 x.push(this.WL_RATIO_COEFF * ( 1 * boatLength) / 7); // Arm starboard, hull side 1228 x.push(this.WL_RATIO_COEFF * ( 1.5 * boatLength) / 7); // Starboard bow 1229 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1230 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1231 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1232 x.push(this.WL_RATIO_COEFF * ( 1.8 * boatLength) / 7); // Starboard transform, ext 1233 x.push(this.WL_RATIO_COEFF * ( 1.2 * boatLength) / 7); // Starboard transform, int 1234 x.push(this.WL_RATIO_COEFF * ( 1 * boatLength) / 7); // Arm, back, starboard, hull side 1235 x.push(this.WL_RATIO_COEFF * ( 0 * boatLength) / 7); // Arm, back, starboard, center 1236 // Port 1237 x.push(this.WL_RATIO_COEFF * ( -0 * boatLength) / 7); 1238 x.push(this.WL_RATIO_COEFF * ( -1 * boatLength) / 7); 1239 x.push(this.WL_RATIO_COEFF * (-1.2 * boatLength) / 7); 1240 x.push(this.WL_RATIO_COEFF * (-1.8 * boatLength) / 7); 1241 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1242 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1243 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1244 x.push(this.WL_RATIO_COEFF * (-1.5 * boatLength) / 7); 1245 x.push(this.WL_RATIO_COEFF * ( -1 * boatLength) / 7); 1246 1247 // Length 1248 y.push((-1 * boatLength) / 7); 1249 // Starboard 1250 y.push((-1 * boatLength) / 7); 1251 y.push((-4 * boatLength) / 7); // Bow 1252 y.push((-1 * boatLength) / 7); 1253 y.push((0 * boatLength) / 7); 1254 y.push((1 * boatLength) / 7); 1255 y.push((3 * boatLength) / 7); 1256 y.push((3 * boatLength) / 7); 1257 y.push((1 * boatLength) / 7); 1258 y.push((1 * boatLength) / 7); 1259 // Port 1260 y.push((1 * boatLength) / 7); 1261 y.push((1 * boatLength) / 7); // Bow 1262 y.push((3 * boatLength) / 7); 1263 y.push((3 * boatLength) / 7); 1264 y.push((1 * boatLength) / 7); 1265 y.push((0 * boatLength) / 7); 1266 y.push((-1 * boatLength) / 7); 1267 y.push((-4 * boatLength) / 7); 1268 y.push((-1 * boatLength) / 7); 1269 1270 } else if (this._boatShape === 'TRI') { 1271 // Width 1272 x.push(this.WL_RATIO_COEFF * 0); // Bow, center hull 1273 // Starboard 1274 x.push(this.WL_RATIO_COEFF * ( 0.3 * boatLength) / 7); 1275 x.push(this.WL_RATIO_COEFF * ( 0.6 * boatLength) / 7); // Arm, front, starboard, inside 1276 x.push(this.WL_RATIO_COEFF * ( 1.6 * boatLength) / 7); // Arm, front, starboard, outside 1277 x.push(this.WL_RATIO_COEFF * ( 1.8 * boatLength) / 7); // Outrigger bow 1278 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1279 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); 1280 x.push(this.WL_RATIO_COEFF * ( 1.9 * boatLength) / 7); // Outrigger transom, ext 1281 x.push(this.WL_RATIO_COEFF * ( 1.7 * boatLength) / 7); // Outrigger transom, int 1282 x.push(this.WL_RATIO_COEFF * ( 1.6 * boatLength) / 7); // Arm, back, starboard, outside 1283 x.push(this.WL_RATIO_COEFF * ( 0.6 * boatLength) / 7); // Arm, back, starboard, inside 1284 x.push(this.WL_RATIO_COEFF * ( 0.3 * boatLength) / 7); // Main hull, transom starboard, 1285 // Port 1286 x.push(this.WL_RATIO_COEFF * ( -0.3 * boatLength) / 7); 1287 x.push(this.WL_RATIO_COEFF * ( -0.6 * boatLength) / 7); 1288 x.push(this.WL_RATIO_COEFF * ( -1.6 * boatLength) / 7); 1289 x.push(this.WL_RATIO_COEFF * ( -1.7 * boatLength) / 7); // Outrigger transom, int 1290 x.push(this.WL_RATIO_COEFF * ( -1.9 * boatLength) / 7); // Outrigger transom, ext 1291 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1292 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); 1293 x.push(this.WL_RATIO_COEFF * ( -1.8 * boatLength) / 7); // Outrigger bow 1294 x.push(this.WL_RATIO_COEFF * ( -1.6 * boatLength) / 7); // Arm, front, starboard, outside 1295 x.push(this.WL_RATIO_COEFF * ( -0.6 * boatLength) / 7); // Arm, front, starboard, inside 1296 x.push(this.WL_RATIO_COEFF * ( -0.3 * boatLength) / 7); 1297 1298 // Length 1299 y.push((-4 * boatLength) / 7); // Bow 1300 // Starboard 1301 y.push((-3 * boatLength) / 7); 1302 y.push((-1 * boatLength) / 7); // Starboard arm, front 1303 y.push((-1 * boatLength) / 7); // Starboard arm, front, outrigger 1304 y.push((-2.6 * boatLength) / 7); // Starboard outrigger bow 1305 y.push((-1.5 * boatLength) / 7); 1306 y.push(( 1.5 * boatLength) / 7); 1307 y.push(( 2.5 * boatLength) / 7); // Starboard transom, ext 1308 y.push(( 2.5 * boatLength) / 7); // Starboard transom, ext 1309 y.push(( 1 * boatLength) / 7); // Starboard arm, back, outrigger 1310 y.push(( 1 * boatLength) / 7); // Starboard arm, hull 1311 y.push(( 3 * boatLength) / 7); 1312 // Port 1313 y.push(( 3 * boatLength) / 7); 1314 y.push(( 1 * boatLength) / 7); 1315 y.push(( 1 * boatLength) / 7); 1316 y.push(( 2.5 * boatLength) / 7); 1317 y.push(( 2.5 * boatLength) / 7); 1318 y.push(( 1.5 * boatLength) / 7); 1319 y.push((-1.5 * boatLength) / 7); 1320 y.push((-2.6 * boatLength) / 7); 1321 y.push((-1 * boatLength) / 7); 1322 y.push((-1 * boatLength) / 7); 1323 y.push((-3 * boatLength) / 7); 1324 } else if (this._boatShape === 'PLANE') { 1325 // Width 1326 x.push(this.WL_RATIO_COEFF * 0); // Nose 1327 // Starboard 1328 x.push(this.WL_RATIO_COEFF * ( 0.3 * boatLength) / 7); 1329 x.push(this.WL_RATIO_COEFF * ( 0.6 * boatLength) / 7); // Wing, front, starboard, inside 1330 x.push(this.WL_RATIO_COEFF * ( 4 * boatLength) / 7); // Wing, front, starboard, outside 1331 x.push(this.WL_RATIO_COEFF * ( 4 * boatLength) / 7); // Wing, outside, back 1332 x.push(this.WL_RATIO_COEFF * ( 0.6 * boatLength) / 7); // Wing, back, starboard, inside 1333 x.push(this.WL_RATIO_COEFF * ( 0.3 * boatLength) / 7); // Main hull, transom starboard, 1334 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); // Main hull, back wing, front 1335 x.push(this.WL_RATIO_COEFF * ( 2 * boatLength) / 7); // Main hull, back wing, back, ext 1336 x.push(this.WL_RATIO_COEFF * ( 0.1 * boatLength) / 7); // Main hull, back wing, back, int 1337 // Port 1338 x.push(this.WL_RATIO_COEFF * ( -0.1 * boatLength) / 7); // Main hull, back wing, back, int 1339 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); // Main hull, back wing, back, ext 1340 x.push(this.WL_RATIO_COEFF * ( -2 * boatLength) / 7); // Main hull, back wing, front 1341 x.push(this.WL_RATIO_COEFF * ( -0.3 * boatLength) / 7); // Main hull, transom starboard, 1342 x.push(this.WL_RATIO_COEFF * ( -0.6 * boatLength) / 7); // Wing, back, starboard, inside 1343 x.push(this.WL_RATIO_COEFF * ( -4 * boatLength) / 7); // Outrigger bow 1344 x.push(this.WL_RATIO_COEFF * ( -4 * boatLength) / 7); // Arm, front, starboard, outside 1345 x.push(this.WL_RATIO_COEFF * ( -0.6 * boatLength) / 7); // Arm, front, starboard, inside 1346 x.push(this.WL_RATIO_COEFF * ( -0.3 * boatLength) / 7); 1347 1348 // Length 1349 y.push((-4 * boatLength) / 7); // Nose 1350 // Starboard 1351 y.push((-3 * boatLength) / 7); 1352 y.push((-2 * boatLength) / 7); 1353 y.push((-1 * boatLength) / 7); 1354 y.push(( 0 * boatLength) / 7); 1355 y.push((-0.5 * boatLength) / 7); 1356 y.push(( 1.8 * boatLength) / 7); 1357 y.push(( 2.5 * boatLength) / 7); 1358 y.push(( 3 * boatLength) / 7); 1359 y.push(( 2.8 * boatLength) / 7); 1360 // Port 1361 y.push(( 2.8 * boatLength) / 7); 1362 y.push(( 3 * boatLength) / 7); 1363 y.push(( 2.5 * boatLength) / 7); 1364 y.push(( 1.8 * boatLength) / 7); 1365 y.push((-0.5 * boatLength) / 7); 1366 y.push(( 0 * boatLength) / 7); 1367 y.push((-1 * boatLength) / 7); 1368 y.push((-2 * boatLength) / 7); 1369 y.push((-3 * boatLength) / 7); 1370 } 1371 let xPoints = []; 1372 let yPoints = []; 1373 1374 // Rotation matrix: 1375 // | cos(alpha) -sin(alpha) | 1376 // | sin(alpha) cos(alpha) | 1377 // The center happens to be the middle of the boat. 1378 1379 let center = this.getCanvasCenter(); 1380 let ptX = center.x; 1381 let ptY = center.y; 1382 1383 for (let i=0; i<x.length; i++) { // Rotation 1384 let dx = x[i] * Math.cos(Math.toRadians(trueHeading)) + (y[i] * (-Math.sin(Math.toRadians(trueHeading)))); 1385 let dy = x[i] * Math.sin(Math.toRadians(trueHeading)) + (y[i] * Math.cos(Math.toRadians(trueHeading))); 1386 xPoints.push(Math.round(ptX + dx)); 1387 yPoints.push(Math.round(ptY + dy)); 1388 } 1389 context.fillStyle = this.boatOverviewColorConfig.boatFillColor; 1390 context.beginPath(); 1391 context.moveTo(xPoints[0], yPoints[0]); 1392 for (let i=1; i<xPoints.length; i++) { 1393 context.lineTo(xPoints[i], yPoints[i]); 1394 } 1395 context.closePath(); 1396 context.fill(); 1397 context.strokeStyle = this.boatOverviewColorConfig.boatOutlineColor; 1398 context.lineWidth = 2; 1399 context.stroke(); 1400 } 1401 1402 drawBoatOverview() { 1403 1404 let currentStyle = this.className; 1405 if (this._previousClassName !== currentStyle || true) { 1406 // Reload 1407 // console.log("Reloading CSS"); 1408 try { 1409 this.boatOverviewColorConfig = this.getColorConfig(currentStyle); 1410 } catch (err) { 1411 // Absorb? 1412 console.log(err); 1413 } 1414 this._previousClassName = currentStyle; 1415 } 1416 1417 let context = this.canvas.getContext('2d'); 1418 1419 if (this._width === 0 || this._height === 0) { // Not visible 1420 return; 1421 } 1422 // Set the canvas size from its container. 1423 this.canvas.width = this._width; 1424 this.canvas.height = this._height; 1425
1426 // Background 1427 let grd = context.createLinearGradient(0, 5, 0, this._height); 1428 grd.addColorStop(0, this.boatOverviewColorConfig.displayBackgroundGradient.from); // 0 Beginning 1429 grd.addColorStop(1, this.boatOverviewColorConfig.displayBackgroundGradient.to); // 1 End 1430 context.fillStyle = grd; 1431 context.fillRect(0, 0, this._width, this._height); 1432 1433 // The actual Graph: 1434 let maxSpeed = 5; 1435 if (this._withGPS) { 1436 maxSpeed = Math.max(maxSpeed, this._sog); 1437 } 1438 maxSpeed = Math.max(maxSpeed, this._bsp); 1439 if (this._withGPS && this._withWind && this._withTrueWind) { 1440 maxSpeed = Math.max(maxSpeed, this._tws); 1441 } 1442 if (this._withWind) { 1443 maxSpeed = Math.max(maxSpeed, this._aws); 1444 } 1445 this.speedScale = 5 * (Math.ceil(maxSpeed / 5)); 1446 1447 let cWidth = this._width; 1448 let cHeight = this._height; 1449 1450 // Circles 1451 let center = this.getCanvasCenter(); 1452 let x = center.x; 1453 let y = center.y; 1454 1455 context.strokeStyle = this.boatOverviewColorConfig.gridColor; 1456 for (let circ=1; circ<=this.speedScale; circ++) { 1457 let radius = this._zoom * Math.round(circ * ((Math.min(cHeight, cWidth) / 2) / this.speedScale)); 1458 context.beginPath(); 1459 if (circ % 5 === 0) { 1460 context.lineWidth = 3; 1461 } else { 1462 context.lineWidth = 1; 1463 } 1464 context.arc(x, y, radius, 0, 2 * Math.PI); 1465 context.closePath(); 1466 context.stroke(); 1467 } 1468 1469 this.drawBoat(context, this._hdg); 1470 if (this._withWind && this._withTrueWind) { 1471 if (this._withGPS) { 1472 this.drawTrueWind(context); 1473 this.drawVW(context); // Speed Wind (Velocity) 1474 } 1475 } 1476 if (this._withWind) { 1477 this.drawAppWind(context); 1478 } 1479 this.drawBSP(context); 1480 if (this._withW) { 1481 this.drawNorths(context); 1482 } 1483 this.drawCMG(context); 1484 if (this._withGPS) { 1485 this.drawSOG(context); 1486 } 1487 if (this._withCurrent && this._withGPS) { 1488 this.drawCurrent(context); 1489 } 1490 if (this._withVMG && this._withGPS) { 1491 this.drawVMG(context); 1492 } 1493 // Display values 1494 // See http://www.w3schools.com/tags/ref_entities.asp, ° = ° 1495 context.fillStyle = this.boatOverviewColorConfig.nmeaDataDisplayColor; 1496 context.font="bold 16px Courier New"; 1497 let txtY = 20; 1498 let space = 18; 1499 let col1 = 10, col2 = 90; 1500 context.fillText("BSP", col1, txtY); 1501 context.fillText(this._bsp + " kts", col2, txtY); 1502 txtY += space; 1503 context.fillText("HDG", col1, txtY); 1504 context.fillText(this._hdg.toFixed(0) + "° True", col2, txtY); 1505 if (this._withWind) { 1506 txtY += space; 1507 context.fillText("AWS", col1, txtY); 1508 context.fillText(this._aws + " kts", col2, txtY); 1509 txtY += space; 1510 context.fillText("AWA", col1, txtY); 1511 context.fillText(this._awa + "°", col2, txtY); 1512 } 1513 1514 if (this._withGPS) { 1515 context.fillStyle = this.boatOverviewColorConfig.gpsWsArrowColor; 1516 txtY += space; 1517 context.fillText("COG", col1, txtY); 1518 context.fillText(this._cog.toFixed(0) + "°", col2, txtY); 1519 txtY += space; 1520 context.fillText("SOG", col1, txtY); 1521 context.fillText(this._sog.toFixed(2) + " kts", col2, txtY); 1522 } 1523 1524 context.fillStyle = this.boatOverviewColorConfig.calculatedDataDisplayColor; 1525 if (this._withWind && this._withTrueWind && this._withGPS) { 1526 txtY += space; 1527 context.fillText("TWS", col1, txtY); 1528 context.fillText(this._tws.toFixed(2) + " kts", col2, txtY); 1529 txtY += space; 1530 context.fillText("TWA", col1, txtY); 1531 context.fillText(this._twa + "°", col2, txtY); 1532 txtY += space; 1533 context.fillText("TWD", col1, txtY); 1534 context.fillText(this._twd + "°", col2, txtY); 1535 } 1536 if (this._withCurrent && this._withGPS) { 1537 txtY += space; 1538 context.fillText("CDR", col1, txtY); 1539 context.fillText(this._cdr.toFixed(0) + "°", col2, txtY); 1540 txtY += space; 1541 context.fillText("CSP", col1, txtY); 1542 context.fillText(this._csp.toFixed(2) + " kts", col2, txtY); 1543 } 1544 txtY += space; 1545 context.fillText("leeway", col1, txtY); 1546 context.fillText(this._lwy.toFixed(2) + "°", col2, txtY); 1547 txtY += space; 1548 context.fillText("CMG", col1, txtY); 1549 context.fillText(this._cmg.toFixed(0) + "°", col2, txtY); 1550 1551 if (this._withVMG && this._withGPS) { 1552 let mess = ", "; 1553 if (this._vmgOnWind) { 1554 mess += "on wind"; 1555 } else { 1556 mess += ("on WP [" + this._wpName + "]"); 1557 } 1558 context.fillStyle = this.boatOverviewColorConfig.vmgDataDisplayColor; 1559 txtY += space;
1560 context.fillText("VMG", col1, txtY); 1561 context.fillText(this._vmg.toFixed(2) + " kts" + mess, col2, txtY); 1562 } 1563 1564 if (this._withW) { 1565 let hdm = this._hdg - this._Decl; 1566 let hdc = hdm - this._dev; 1567 while (hdm < 0) { 1568 hdm += 360; 1569 } 1570 while (hdc < 0) { 1571 hdc += 360; 1572 } 1573 1574 context.fillStyle = this.boatOverviewColorConfig.dDWDataDisplayColor; 1575 txtY += space; 1576 context.fillText("D", col1, txtY); 1577 context.fillText(this._Decl.toFixed(1) + "°", col2, txtY); 1578 txtY += space; 1579 context.fillText("d", col1, txtY); 1580 context.fillText(this._dev.toFixed(1) + "°", col2, txtY); 1581 txtY += space; 1582 context.fillText("W", col1, txtY); 1583 context.fillText((this._Decl + this._dev).toFixed(1) + "°", col2, txtY); 1584 txtY += space; 1585 context.fillText("HDM", col1, txtY); 1586 context.fillText(hdm.toFixed(1) + "°", col2, txtY); 1587 txtY += space; 1588 context.fillText("HDC", col1, txtY); 1589 context.fillText(hdc.toFixed(1) + "°", col2, txtY); 1590 } 1591 1592 } 1593} 1594 1595class Point { 1596 constructor(x, y) { 1597 this.x = x; 1598 this.y = y; 1599 } 1600} 1601 1602 1603class Line { 1604 1605// Line with arrow head 1606 constructor(x1, y1, x2, y2) { 1607 this.x1 = x1; 1608 this.y1 = y1; 1609 this.x2 = x2; 1610 this.y2 = y2; 1611 1612 this.HEAD_LENGTH = 20; 1613 this.HEAD_WIDTH = 6; 1614 } 1615 1616 1617 static rotate(p, angle) { 1618 return new Point(Math.round((p.x * Math.cos(Math.toRadians(angle))) + (p.y * Math.sin(Math.toRadians(angle)))), 1619 Math.round((p.x * -Math.sin(Math.toRadians(angle))) + (p.y * Math.cos(Math.toRadians(angle))))); 1620 } 1621 1622 drawWithArrowhead(ctx) { 1623 this.drawWithArrowheads(ctx, false); 1624 } 1625 1626 drawWithArrowheads(ctx, both) { 1627 if (both === undefined) { 1628 both = true; 1629 } 1630 // draw the line 1631 ctx.beginPath(); 1632 ctx.moveTo(this.x1, this.y1); 1633 ctx.lineTo(this.x2, this.y2); 1634 ctx.stroke(); 1635 1636 if (both) { 1637 // draw the starting arrowhead 1638 let startRadians = Math.atan((this.y2 - this.y1) / (this.x2 - this.x1)); 1639 startRadians += ((this.x2 > this.x1) ? -90 : 90) * Math.PI / 180; 1640 this.drawArrowhead(ctx, this.x1, this.y1, startRadians); 1641 } 1642 // draw the ending arrowhead 1643 let endRadians = Math.atan((this.y2 - this.y1) / (this.x2 - this.x1)); 1644 endRadians += ((this.x2 > this.x1) ? 90 : -90) * Math.PI / 180; 1645 this.drawArrowhead(ctx, this.x2, this.y2, endRadians); 1646 } 1647 1648 drawWithAnemoArrowheads(ctx) { 1649 // draw the line 1650 ctx.beginPath(); 1651 ctx.moveTo(this.x1, this.y1); 1652 ctx.lineTo(this.x2, this.y2); 1653 ctx.stroke(); 1654 1655 let endRadians = Math.atan((this.y2 - this.y1) / (this.x2 - this.x1)); 1656 endRadians += ((this.x2 > this.x1) ? 90 : -90) * Math.PI / 180; 1657 this.drawArrowhead(ctx, this.x2 - (this.x2 - this.x1) / 2, this.y2 - (this.y2 - this.y1) / 2, endRadians); 1658 } 1659 1660 drawHollowArrow(ctx) { 1661 let headLength = 30; 1662 let arrowWidth = 10; 1663 let headWidth = 20; 1664 1665 let dir = Utilities.getDir((this.x1 - this.x2), (this.y2 - this.y1)); 1666 let len = Math.sqrt(((this.x1 - this.x2) * (this.x1 - this.x2)) + ((this.y2 - this.y1) * (this.y2 - this.y1))); 1667 1668 let one, two, three, four, five, six, seven, eight; 1669 one = new Point(0, 0); 1670 two = new Point(-arrowWidth / 2, 0); 1671 three = new Point(-arrowWidth / 2, -(Math.round(len - headLength))); 1672 four = new Point(-headWidth / 2, -(Math.round(len - headLength))); 1673 five = new Point(0, -Math.round(len)); // to 1674 six = new Point(headWidth / 2, -(Math.round(len - headLength))); 1675 seven = new Point(arrowWidth / 2, -(Math.round(len - headLength))); 1676 eight = new Point(arrowWidth / 2, 0); 1677 one = Line.rotate(one, -dir); 1678 two = Line.rotate(two, -dir); 1679 three = Line.rotate(three, -dir); 1680 four = Line.rotate(four, -dir); 1681 five = Line.rotate(five, -dir); 1682 six = Line.rotate(six, -dir); 1683 seven = Line.rotate(seven, -dir); 1684 eight = Line.rotate(eight, -dir); 1685 1686 let x = []; 1687 let y = []; 1688 1689 x.push(this.x1 + one.x); 1690 x.push(this.x1 + two.x); 1691 x.push(this.x1 + three.x); 1692 x.push(this.x1 + four.x); 1693 x.push(this.x1 + five.x); 1694 x.push(this.x1 + six.x); 1695 x.push(this.x1 + seven.x); 1696 x.push(this.x1 + eight.x); 1697 1698 y.push(this.y1 + one.y); 1699 y.push(this.y1 + two.y); 1700 y.push(this.y1 + three.y); 1701 y.push(this.y1 + four.y); 1702 y.push(this.y1 + five.y); 1703 y.push(this.y1 + six.y); 1704 y.push(this.y1 + seven.y); 1705 y.push(this.y1 + eight.y); 1706 1707 ctx.beginPath(); 1708 ctx.moveTo(x[0], y[0]); 1709 for (let i = 1; i < x.length; i++) { 1710 ctx.lineTo(x[i], y[i]); 1711 } 1712 ctx.closePath(); 1713 ctx.stroke(); 1714 } 1715 1716 drawArrowhead(ctx, x, y, radians) { 1717 ctx.save(); 1718 ctx.beginPath(); 1719 ctx.translate(x, y); 1720 ctx.rotate(radians); 1721 ctx.moveTo(0, 0); 1722 ctx.lineTo(this.HEAD_WIDTH, this.HEAD_LENGTH); 1723 ctx.lineTo(-this.HEAD_WIDTH, this.HEAD_LENGTH); 1724 ctx.closePath();
1725 ctx.restore(); 1726 ctx.fill(); 1727 } 1728} 1729 1730// Associate the tag and the class 1731window.customElements.define(BOAT_OVERVIEW_TAG_NAME, BoatOverview);
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.