diff --git a/.gitignore b/.gitignore index f2bf0fc1..38f1f4ab 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /node_modules package-lock.json .angular +node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3d617d..f579b3f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,13 @@ # CHANGELOG: Freeboard -### v2.9.0 +### v2.9.1 - **Added**: Ability to filter vessels by `AIS Ship Type` (#163). -- **Added**: Display `performance.beatAngle` vectors on the map. +- **Added**: Display `performance.beatAngle` and `performance.gybeAngle` vectors on the map. - **Fixed**: Meteo properties `environment.water.waves` display formatting. - **Fixed**: Anchor watch not available when plugin is installed and enabled. - **Fixed**: Laylines not displayed if performance paths do not contain values. - **Updated**: Don't show internet map service dialog in kiosk mode. (#166) -- **Updated**: Angular framework to v18 ### v2.8.4 diff --git a/angular.json b/angular.json index 234f6ae8..f02497b0 100644 --- a/angular.json +++ b/angular.json @@ -15,16 +15,12 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:application", + "builder": "@angular-devkit/build-angular:browser", "options": { - "outputPath": { - "base": "public", - "browser": "" - }, + "outputPath": "public", "index": "src/index.html", - "polyfills": [ - "src/polyfills.ts" - ], + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "assets": [ "src/favicon.ico", @@ -37,7 +33,9 @@ "src/styles.scss" ], "scripts": [], + "vendorChunk": true, "extractLicenses": false, + "buildOptimizer": false, "sourceMap": true, "optimization": false, "namedChunks": true, @@ -45,8 +43,7 @@ "allowedCommonJsDependencies": [ "geolib", "simplify-ts" - ], - "browser": "src/main.ts" + ] }, "configurations": { "production": { @@ -61,6 +58,8 @@ "sourceMap": false, "namedChunks": false, "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, "budgets": [ { "type": "initial", diff --git a/package.json b/package.json index e87a848f..55def847 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@signalk/freeboard-sk", - "version": "2.9.0", + "version": "2.9.1", "description": "Openlayers chart plotter implementation for Signal K", "keywords": [ "signalk-webapp", @@ -43,19 +43,19 @@ "tslib": "^2.0.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.0.5", - "@angular/animations": "^18.0.4", - "@angular/cdk": "^18.0.4", - "@angular/cli": "^18.0.5", - "@angular/common": "^18.0.4", - "@angular/compiler": "^18.0.4", - "@angular/compiler-cli": "^18.0.4", - "@angular/core": "^18.0.4", - "@angular/forms": "^18.0.4", - "@angular/language-service": "^18.0.4", - "@angular/material": "^18.0.4", - "@angular/platform-browser": "^18.0.4", - "@angular/platform-browser-dynamic": "^18.0.4", + "@angular-devkit/build-angular": "^17.3.0", + "@angular/animations": "^17.3.0", + "@angular/cdk": "^17.3.0", + "@angular/cli": "^17.3.0", + "@angular/common": "^17.3.0", + "@angular/compiler": "^17.3.0", + "@angular/compiler-cli": "^17.3.0", + "@angular/core": "^17.3.0", + "@angular/forms": "^17.3.0", + "@angular/language-service": "^17.3.0", + "@angular/material": "^17.3.0", + "@angular/platform-browser": "^17.3.0", + "@angular/platform-browser-dynamic": "^17.3.0", "@kolkov/angular-editor": "^2.1.0", "@types/arcgis-rest-api": "^10.4.5", "@types/express": "^4.17.17", @@ -77,7 +77,7 @@ "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", - "ng-packagr": "^18.0.0", + "ng-packagr": "^17.3.0", "ngeohash": "^0.6.3", "ol": "^9.0.0", "ol-mapbox-style": "^12.2.1", @@ -98,4 +98,4 @@ "xml2js": "^0.6.2", "zone.js": "~0.14.4" } -} \ No newline at end of file +} diff --git a/src/app-theme.scss b/src/app-theme.scss index 146f9c46..bcffae9f 100644 --- a/src/app-theme.scss +++ b/src/app-theme.scss @@ -4,33 +4,33 @@ @include mat.core(); // Define a theme. -$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500,700,200); -$my-accent: mat.m2-define-palette(mat.$m2-amber-palette, 500,700,200); +$my-primary: mat.define-palette(mat.$indigo-palette, 500,700,200); +$my-accent: mat.define-palette(mat.$amber-palette, 500,700,200); // The "warn" palette is optional and defaults to red if not specified. -$my-warn: mat.m2-define-palette(mat.$m2-red-palette, 500,700,200); +$my-warn: mat.define-palette(mat.$red-palette, 500,700,200); -$my-theme: mat.m2-define-light-theme(( +$my-theme: mat.define-light-theme(( color: ( primary: $my-primary, accent: $my-accent, warn: $my-warn, ), - typography: mat.m2-define-typography-config(), + typography: mat.define-typography-config(), density: 0, )); // Define a dark theme. -$my-dark-primary: mat.m2-define-palette(mat.$m2-light-blue-palette, 200,300,400); -$my-dark-accent: mat.m2-define-palette(mat.$m2-amber-palette, A400, A100, A700); -$my-dark-warn: mat.m2-define-palette(mat.$m2-deep-orange-palette, A400); +$my-dark-primary: mat.define-palette(mat.$light-blue-palette, 200,300,400); +$my-dark-accent: mat.define-palette(mat.$amber-palette, A400, A100, A700); +$my-dark-warn: mat.define-palette(mat.$deep-orange-palette, A400); -$my-dark-theme: mat.m2-define-dark-theme(( +$my-dark-theme: mat.define-dark-theme(( color: ( primary: $my-dark-primary, accent: $my-dark-accent, warn: $my-dark-warn, ), - typography: mat.m2-define-typography-config(), + typography: mat.define-typography-config(), density: 0, )); @@ -38,11 +38,11 @@ $my-dark-theme: mat.m2-define-dark-theme(( @include mat.all-component-colors($my-dark-theme); .about-row .item a { - color: mat.m2-get-color-from-palette($my-dark-accent); + color: mat.get-color-from-palette($my-dark-accent); } .welcome a { - color: mat.m2-get-color-from-palette($my-dark-accent); + color: mat.get-color-from-palette($my-dark-accent); } .popover > .arrow:after { @@ -55,12 +55,12 @@ $my-dark-theme: mat.m2-define-dark-theme(( border-top-color: mat.get-theme-color($my-dark-theme, background, background); } - + /* Track */ ::-webkit-scrollbar-track { background: rgba(0,0,0,.5); } - + /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #999; } @@ -82,26 +82,26 @@ $my-dark-theme: mat.m2-define-dark-theme(( // std elements .about-row .item a { - color: mat.m2-get-color-from-palette($my-primary); + color: mat.get-color-from-palette($my-primary); } - +/* width */ ::-webkit-scrollbar { width: 6px; height: 6px; } - +/* Track */ ::-webkit-scrollbar-track { background: #f1f1f1; } - +/* Handle */ ::-webkit-scrollbar-thumb { background: #888; } - +/* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #555; } @@ -110,3 +110,4 @@ $my-dark-theme: mat.m2-define-dark-theme(( // Include all theme styles for the components. @include mat.all-component-themes($my-theme); + diff --git a/src/app/app.info.ts b/src/app/app.info.ts index 9b0deeaf..64deccc1 100644 --- a/src/app/app.info.ts +++ b/src/app/app.info.ts @@ -276,7 +276,7 @@ export class AppInfo extends Info { this.name = 'Freeboard-SK'; this.shortName = 'Freeboard'; this.description = `Signal K Chart Plotter.`; - this.version = '2.9.0'; + this.version = '2.9.1'; this.url = 'https://github.com/signalk/freeboard-sk'; this.logo = './assets/img/app_logo.png'; @@ -930,15 +930,16 @@ export class AppInfo extends Info { for more details.` }, 'whats-new': [ - { + /*{ type: 'signalk-server-node', - title: 'AIS Vessels', + title: 'OpenWeather 3.0 Support', message: ` - Freeboard-SK now supports filtering the disply of vessels by AIS ship type. + OpenWeather is deprecating support for v2.5 of their API in April 2024!
 
- Select Vessels from the menu and turn on View by Vessel type. + Freeboard-SK now supports the v3.0 API which will require you to supply + a new API Key in the configuration. ` - } + }*/ ] }; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 24c08428..323c84d1 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,9 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { - provideHttpClient, - withInterceptorsFromDi -} from '@angular/common/http'; +import { HttpClientModule } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatBadgeModule } from '@angular/material/badge'; @@ -14,8 +11,8 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatTooltipModule } from '@angular/material/tooltip'; +// *** import { AppComponent } from './app.component'; - import { FBMapComponent, ExperimentsComponent, @@ -42,9 +39,8 @@ import { @NgModule({ declarations: [AppComponent], - exports: [], - bootstrap: [AppComponent], imports: [ + HttpClientModule, MatMenuModule, MatSidenavModule, MatBadgeModule, @@ -74,6 +70,8 @@ import { AlarmsDialog, RouteNextPointComponent ], - providers: [provideHttpClient(withInterceptorsFromDi())] + exports: [], + providers: [], + bootstrap: [AppComponent] }) export class AppModule {} diff --git a/src/app/lib/components/dialogs/common/dialogs.component.ts b/src/app/lib/components/dialogs/common/dialogs.component.ts index 3f7267bf..48fedd40 100644 --- a/src/app/lib/components/dialogs/common/dialogs.component.ts +++ b/src/app/lib/components/dialogs/common/dialogs.component.ts @@ -275,9 +275,7 @@ export class ConfirmDialog implements OnInit { @if(data.url) {
- Visit Website + Visit Website
} diff --git a/src/app/lib/components/index.ts b/src/app/lib/components/index.ts index 7d6b6bc5..41225086 100644 --- a/src/app/lib/components/index.ts +++ b/src/app/lib/components/index.ts @@ -1,7 +1,6 @@ export * from './dial-text'; export * from './file-input.component'; export * from './pip.component'; -export * from '../../modules/skresources/components/signalk-details.component'; export * from './wakelock.component'; export * from './measurements.component'; export * from './dialogs'; diff --git a/src/app/lib/components/wakelock.component.ts b/src/app/lib/components/wakelock.component.ts index f89aa322..09fdcd42 100644 --- a/src/app/lib/components/wakelock.component.ts +++ b/src/app/lib/components/wakelock.component.ts @@ -27,7 +27,6 @@ import { MatTooltipModule } from '@angular/material/tooltip'; @@ -580,7 +580,7 @@ (click)="onContextMenuAction('add_note', item)" (contextmenu)="$event.preventDefault()" > - local_offer + local_offer Add Note here } diff --git a/src/app/modules/map/fb-map.component.ts b/src/app/modules/map/fb-map.component.ts index 0689f2c0..5ac94885 100644 --- a/src/app/modules/map/fb-map.component.ts +++ b/src/app/modules/map/fb-map.component.ts @@ -8,7 +8,6 @@ import { ViewChild, SimpleChanges } from '@angular/core'; - import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -28,18 +27,17 @@ import { ResourceSetPopoverComponent, VesselPopoverComponent } from './popovers'; - import { FreeboardOpenlayersModule } from 'src/app/modules/map/ol'; import { PipesModule } from 'src/app/lib/pipes'; -import { getGreatCircleBearing } from 'geolib'; +import { computeDestinationPoint, getGreatCircleBearing } from 'geolib'; import { toLonLat } from 'ol/proj'; import { Style, Stroke, Fill } from 'ol/style'; import { Collection, Feature } from 'ol'; import { Feature as GeoJsonFeature } from 'geojson'; import { Convert } from 'src/app/lib/convert'; -import { GeoUtils } from 'src/app/lib/geoutils'; +import { GeoUtils, Angle } from 'src/app/lib/geoutils'; import { Position } from 'src/app/types'; import { AppInfo } from 'src/app/app.info'; @@ -92,7 +90,6 @@ import { SKNotification } from 'src/app/types'; import { S57Service } from './ol/lib/s57.service'; -import { VesselLinesResult } from './vessel-calcs.types'; interface IResource { id: string; @@ -163,20 +160,6 @@ interface IMeasureInfo { coords: Position[]; } -interface VesselLines { - twd: Array; - awa: Array; - cog: Array; - bearing: Array; - heading: Array; - anchor: Array; - trail: Array; - cpa: Array; - xtePath: Array; - laylines: { port: number[][][]; starboard: number[][][] }; - targetAngle: Array; -} - enum INTERACTION_MODE { MEASURE, DRAW, @@ -258,7 +241,7 @@ export class FBMapComponent implements OnInit, OnDestroy { coords: [] }; - public vesselLines: VesselLines = { + public vesselLines = { twd: [], awa: [], bearing: [], @@ -266,7 +249,6 @@ export class FBMapComponent implements OnInit, OnDestroy { anchor: [], trail: [], cpa: [], - cog: [], xtePath: [], laylines: { port: [], starboard: [] }, targetAngle: [] @@ -369,8 +351,6 @@ export class FBMapComponent implements OnInit, OnDestroy { private obsList = []; - private worker: Worker; - constructor( public app: AppInfo, public s57Service: S57Service, @@ -378,12 +358,7 @@ export class FBMapComponent implements OnInit, OnDestroy { public skresOther: SKOtherResources, private skstream: SKStreamFacade, private alarmsFacade: AlarmsFacade - ) { - this.worker = new Worker(new URL('./vessel-calcs.worker', import.meta.url)); - this.worker.onmessage = ({ data }) => { - this.drawVesselLinesResult(data); - }; - } + ) {} ngAfterViewInit() { // ** set map focus ** @@ -430,11 +405,6 @@ export class FBMapComponent implements OnInit, OnDestroy { ngOnDestroy() { this.stopSaveTimer(); this.obsList.forEach((i) => i.unsubscribe()); - if (this.worker) { - console.log('Terminating Vessel-Calcs Worker....'); - this.worker.terminate(); - this.worker = undefined; - } } ngOnChanges(changes: SimpleChanges) { @@ -1119,31 +1089,30 @@ export class FBMapComponent implements OnInit, OnDestroy { } } - // center map to active vessel position + // ** center map to active vessel position public centerVessel() { const t = this.dfeat.active.position; t[0] += 0.0000000000001; this.fbMap.center = t; } - // trigger line calcs worker public drawVesselLines(vesselUpdate = false) { - this.worker.postMessage({ - vesselConfig: this.app.config.vessel, - vesselSelf: this.app.data.vessels.self, - vesselActive: this.app.data.vessels.active, - navData: this.app.data.navData, - mapZoomLevel: this.fbMap.zoomLevel, - zoomOffsetLevel: this.zoomOffsetLevel, - units: this.app.config.units - }); + const z = this.fbMap.zoomLevel; + const offset = z < 29 ? this.zoomOffsetLevel[Math.floor(z)] : 60; + const wMax = 10; // ** max line length const vl = { trail: [], xtePath: [], bearing: [], anchor: [], - cpa: [] + cpa: [], + heading: [], + awa: [], + twd: [], + cog: [], + laylines: { port: [], starboard: [] }, + targetAngle: [] }; // vessel trail @@ -1177,6 +1146,117 @@ export class FBMapComponent implements OnInit, OnDestroy { : this.dfeat.active.position; vl.bearing = [this.dfeat.active.position, bpos]; + // laylines (active) + if ( + this.app.config.vessel.laylines && + Array.isArray(this.dfeat.navData.position) && + typeof this.dfeat.navData.position[0] === 'number' && + typeof this.app.data.vessels.active.heading === 'number' + ) { + const twd_deg = Convert.radiansToDegrees( + this.app.data.vessels.self.wind.twd + ); + const destUpwind = + Math.abs( + Angle.difference(this.app.data.navData.bearing.value, twd_deg) + ) < 90; + const ba_deg = Convert.radiansToDegrees( + this.app.data.vessels.self.performance.beatAngle ?? Math.PI / 4 + ); + const destInTarget = + Math.abs( + Angle.difference(this.app.data.navData.bearing.value, twd_deg) + ) < ba_deg; + const dtg = + this.app.config.units.distance === 'm' + ? this.app.data.navData.dtg * 1000 + : Convert.nauticalMilesToKm(this.app.data.navData.dtg * 1000); + + if (destInTarget) { + const bta = Angle.add(twd_deg, 90); // tack angle = 90 + + const hbd_rad = Convert.degreesToRadians( + Angle.difference(twd_deg, this.app.data.navData.bearing.value) + ); + const dist1 = Math.sin(hbd_rad) * dtg; + const dist2 = Math.cos(hbd_rad) * dtg; + const pt1 = computeDestinationPoint( + this.app.data.vessels.active.position, + dist1, + bta + ); + const pt2 = computeDestinationPoint( + this.app.data.vessels.active.position, + dist2, + twd_deg + ); + const p1a = [ + this.app.data.vessels.active.position, + [pt1.longitude, pt1.latitude] + ]; + const p1b = [ + [pt1.longitude, pt1.latitude], + this.dfeat.navData.position + ]; + const l1 = hbd_rad < 0 ? [p1a, p1b] : [p1b, p1a]; + + const p2a = [ + [pt2.longitude, pt2.latitude], + this.dfeat.navData.position + ]; + const p2b = [ + this.app.data.vessels.active.position, + [pt2.longitude, pt2.latitude] + ]; + const l2 = hbd_rad < 0 ? [p2a, p2b] : [p2b, p2a]; + + vl.laylines = { + port: hbd_rad < 0 ? l2 : l1, + starboard: hbd_rad < 0 ? l1 : l2 + }; + } + // target angle lines + if (destUpwind && destInTarget) { + const bapt1 = computeDestinationPoint( + this.app.data.vessels.active.position, + dtg, + Angle.add(twd_deg, ba_deg) + ); + const bapt2 = computeDestinationPoint( + this.app.data.vessels.active.position, + dtg, + Angle.add(twd_deg, 0 - ba_deg) + ); + vl.targetAngle = [ + [bapt1.longitude, bapt1.latitude], + this.app.data.vessels.active.position, + [bapt2.longitude, bapt2.latitude] + ]; + } else if ( + !destUpwind && + typeof this.app.data.vessels.self.performance.gybeAngle === 'number' + ) { + const ga_deg = Convert.radiansToDegrees( + this.app.data.vessels.self.performance.gybeAngle + ); + const gapt1 = computeDestinationPoint( + this.app.data.vessels.active.position, + dtg, + Angle.add(this.app.data.navData.bearing.value, ga_deg) + ); + const gapt2 = computeDestinationPoint( + this.app.data.vessels.active.position, + dtg, + Angle.add(this.app.data.navData.bearing.value, 0 - ga_deg) + ); + vl.targetAngle = [ + [gapt1.longitude, gapt1.latitude], + this.app.data.vessels.active.position, + [gapt2.longitude, gapt2.latitude] + ]; + } + } + // ** anchor line (active) ** if (!this.app.data.anchor.raised) { vl.anchor = [this.app.data.anchor.position, this.dfeat.active.position]; @@ -1185,12 +1265,69 @@ export class FBMapComponent implements OnInit, OnDestroy { // ** CPA line ** vl.cpa = [this.dfeat.closest.position, this.dfeat.self.position]; - this.vesselLines = Object.assign({}, this.vesselLines, vl); - } + const sog = this.dfeat.active.sog || 0; + + // ** cog line (active) ** + const cl = sog * (this.app.config.vessel.cogLine * 60); + if (this.dfeat.active.cog) { + vl.cog = [ + this.dfeat.active.position, + GeoUtils.destCoordinate( + this.dfeat.active.position, + this.dfeat.active.cog, + cl + ) + ]; + } + + // ** heading line (active) ** + let hl = 0; + if (this.app.config.vessel.headingLineSize === -1) { + hl = (sog > wMax ? wMax : sog) * offset; + } else { + hl = + Convert.nauticalMilesToKm(this.app.config.vessel.headingLineSize) * + 1000; + } + vl.heading = [ + this.dfeat.active.position, + GeoUtils.destCoordinate( + this.dfeat.active.position, + this.dfeat.active.orientation, + hl + ) + ]; + + // ** awa (focused) ** + let aws = this.dfeat.active.wind.aws || 0; + if (aws > wMax) { + aws = wMax; + } - // worker call back - private drawVesselLinesResult(data: VesselLinesResult) { - this.vesselLines = Object.assign({}, this.vesselLines, data); + vl.awa = [ + this.dfeat.active.position, + GeoUtils.destCoordinate( + this.dfeat.active.position, + this.dfeat.active.wind.awa + this.dfeat.active.orientation, + typeof this.dfeat.active.orientation === 'number' ? aws * offset : 0 + ) + ]; + + // ** twd (focused) ** + let tws = this.dfeat.active.wind.tws || 0; + if (tws > wMax) { + tws = wMax; + } + vl.twd = [ + this.dfeat.active.position, + GeoUtils.destCoordinate( + this.dfeat.active.position, + this.dfeat.active.wind.direction || 0, + typeof this.dfeat.active.orientation === 'number' ? tws * offset : 0 + ) + ]; + + this.vesselLines = vl; } // ******** OVERLAY ACTIONS ************ diff --git a/src/app/modules/map/mapconfig.ts b/src/app/modules/map/mapconfig.ts index bf9094ec..808be1a5 100644 --- a/src/app/modules/map/mapconfig.ts +++ b/src/app/modules/map/mapconfig.ts @@ -313,6 +313,11 @@ const ais80 = new Icon({ rotateWithView: true, rotation: 0 }); +const ais90 = new Icon({ + src: './assets/img/ais_other.png', + rotateWithView: true, + rotation: 0 +}); const aisBuddy = new Icon({ src: './assets/img/ais_buddy.png', rotateWithView: true, @@ -506,7 +511,7 @@ export const aisVesselStyles = { 90: { // other default: new Style({ - image: ais50, + image: ais90, text: new Text({ text: '', offsetY: -12 diff --git a/src/app/modules/map/ol/index.ts b/src/app/modules/map/ol/index.ts index 6cf63750..62a5ad26 100644 --- a/src/app/modules/map/ol/index.ts +++ b/src/app/modules/map/ol/index.ts @@ -46,7 +46,6 @@ import { ArrivalCircleComponent } from './lib/navigation/layer-arrival-circle.co import { XTEPathComponent } from './lib/navigation/layer-xte-path.component'; import { BearingLineComponent } from './lib/navigation/layer-bearing-line.component'; import { LaylineComponent } from './lib/navigation/layer-layline.component'; -import { TargetAngleComponent } from './lib/navigation/layer-target-angle.component'; import { DirectionOfTravelComponent } from './lib/navigation/layer-dot.component'; import { VesselComponent } from './lib/vessel/layer-vessel.component'; import { VesselTrailComponent } from './lib/vessel/layer-vessel-trail.component'; @@ -99,6 +98,7 @@ export { XTEPathComponent } from './lib/navigation/layer-xte-path.component'; export { BearingLineComponent } from './lib/navigation/layer-bearing-line.component'; export { LaylineComponent } from './lib/navigation/layer-layline.component'; export { TargetAngleComponent } from './lib/navigation/layer-target-angle.component'; +import { TargetAngleComponent } from './lib/navigation/layer-target-angle.component'; export { DirectionOfTravelComponent } from './lib/navigation/layer-dot.component'; export { VesselComponent } from './lib/vessel/layer-vessel.component'; export { VesselTrailComponent } from './lib/vessel/layer-vessel-trail.component'; diff --git a/src/app/modules/map/ol/lib/navigation/layer-layline.component.ts b/src/app/modules/map/ol/lib/navigation/layer-layline.component.ts index b26537a4..e40d42e6 100644 --- a/src/app/modules/map/ol/lib/navigation/layer-layline.component.ts +++ b/src/app/modules/map/ol/lib/navigation/layer-layline.component.ts @@ -84,7 +84,7 @@ export class LaylineComponent implements OnInit, OnDestroy, OnChanges { const properties: { [index: string]: any } = {}; for (const key in changes) { - if (key === 'lines' || key === 'twd' || key === 'bearing') { + if (key === 'lines' || key === 'twd' || key === 'position') { this.parseValues(); if (this.source) { this.source.clear(); diff --git a/src/app/modules/map/ol/lib/resources/layer-charts.component.ts b/src/app/modules/map/ol/lib/resources/layer-charts.component.ts index bf8f85f9..c5adde93 100644 --- a/src/app/modules/map/ol/lib/resources/layer-charts.component.ts +++ b/src/app/modules/map/ol/lib/resources/layer-charts.component.ts @@ -10,6 +10,7 @@ import { } from '@angular/core'; import TileLayer from 'ol/layer/Tile'; +import VectorTileLayer from 'ol/layer/VectorTile'; import { TileWMS, XYZ, TileJSON, WMTS } from 'ol/source'; import { optionsFromCapabilities } from 'ol/source/WMTS'; import WMTSCapabilities from 'ol/format/WMTSCapabilities'; @@ -25,6 +26,7 @@ import * as pmtiles from 'pmtiles'; import { SKChart } from 'src/app/modules'; import LayerGroup from 'ol/layer/Group'; import { apply } from 'ol-mapbox-style'; +import { FeatureLike } from 'ol/Feature'; // ** Freeboard resource collection format ** @Component({ @@ -206,7 +208,7 @@ export class FreeboardChartLayerComponent charts[i][1] ); layer = styleFactory.CreateLayer(); - styleFactory.ApplyStyle(layer); + styleFactory.ApplyStyle(layer as VectorTileLayer); layer.setZIndex(this.zIndex + parseInt(i)); } else { // raster tile diff --git a/src/app/modules/map/ol/lib/vectorLayerStyleFactory.ts b/src/app/modules/map/ol/lib/vectorLayerStyleFactory.ts index cd7da311..d0362ee6 100644 --- a/src/app/modules/map/ol/lib/vectorLayerStyleFactory.ts +++ b/src/app/modules/map/ol/lib/vectorLayerStyleFactory.ts @@ -6,6 +6,7 @@ import VectorTileSource from 'ol/source/VectorTile'; import { MVT } from 'ol/format'; import { Style, Fill, Stroke } from 'ol/style'; import * as pmtiles from 'pmtiles'; +import { FeatureLike } from 'ol/Feature'; export abstract class VectorLayerStyler { public MinZ: number; @@ -18,10 +19,9 @@ export abstract class VectorLayerStyler { : chart.minZoom; this.MaxZ = chart.maxZoom; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public abstract ApplyStyle(vectorLayer: any); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public abstract CreateLayer(): VectorTileLayer; + + public abstract ApplyStyle(vectorLayer: VectorTileLayer); + public abstract CreateLayer(): VectorTileLayer; } class S57LayerStyler extends VectorLayerStyler { @@ -29,13 +29,11 @@ class S57LayerStyler extends VectorLayerStyler { super(chart); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public CreateLayer(): VectorTileLayer { + public CreateLayer(): VectorTileLayer { return new VectorTileLayer(); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public ApplyStyle(vectorLayer: VectorTileLayer) { + public ApplyStyle(vectorLayer: VectorTileLayer) { vectorLayer.set('declutter', true); const source = new VectorTileSource({ url: this.chart.url, @@ -62,8 +60,7 @@ class DefaultLayerStyler extends VectorLayerStyler { super(chart); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public CreateLayer(): VectorTileLayer { + public CreateLayer(): VectorTileLayer { return new VectorTileLayer(); } @@ -80,8 +77,7 @@ class DefaultLayerStyler extends VectorLayerStyler { }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public ApplyStyle(vectorLayer: VectorTileLayer) { + public ApplyStyle(vectorLayer: VectorTileLayer) { // mbtiles source const source = new VectorTileSource({ url: this.chart.url, @@ -106,13 +102,11 @@ class PMLayerStyler extends DefaultLayerStyler { super(chart); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public CreateLayer(): VectorTileLayer { + public CreateLayer(): VectorTileLayer { return new VectorTileLayer({ declutter: true }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public ApplyStyle(vectorLayer: VectorTileLayer) { + public ApplyStyle(vectorLayer: VectorTileLayer) { vectorLayer.set('declutter', true); const tiles = new pmtiles.PMTiles(this.chart.url); diff --git a/src/app/modules/map/popovers/aircraft-popover.component.ts b/src/app/modules/map/popovers/aircraft-popover.component.ts index b6f95ed6..8ab24661 100644 --- a/src/app/modules/map/popovers/aircraft-popover.component.ts +++ b/src/app/modules/map/popovers/aircraft-popover.component.ts @@ -5,20 +5,17 @@ import { EventEmitter, ChangeDetectionStrategy } from '@angular/core'; - import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; import { PopoverComponent } from './popover.component'; - import { AppInfo } from 'src/app/app.info'; import { SKAircraft } from 'src/app/modules'; - /*********** Aircraft Popover *************** -title: string - title text, -aircraft: SKAircraft - aircraft data -*************************************************/ + title: string - title text, + aircraft: SKAircraft - aircraft data + *************************************************/ @Component({ selector: 'aircraft-popover', changeDetection: ChangeDetectionStrategy.OnPush, @@ -36,14 +33,12 @@ aircraft: SKAircraft - aircraft data
Name:
{{ aircraft.name }}
-
Call sign:
{{ aircraft.callsign }}
-
Latitude:
-
Last Update:
@@ -93,13 +87,10 @@ export class AircraftPopoverComponent { @Input() canClose: boolean; @Output() info: EventEmitter = new EventEmitter(); @Output() closed: EventEmitter = new EventEmitter(); - _title: string; timeLastUpdate: string; timeAgo: string; // last update in minutes ago - constructor(public app: AppInfo) {} - ngOnInit() { if (!this.aircraft) { this.handleClose(); @@ -108,7 +99,6 @@ export class AircraftPopoverComponent { this.title || this.aircraft.name || this.aircraft.mmsi || 'Aircraft:'; } } - ngOnChanges() { if (!this.aircraft) { this.handleClose(); @@ -121,11 +111,9 @@ export class AircraftPopoverComponent { (new Date().valueOf() - this.aircraft.lastUpdated.valueOf()) / 1000; this.timeAgo = td < 60 ? '' : `(${Math.floor(td / 60)} min ago)`; } - handleInfo() { this.info.emit(this.aircraft.id); } - handleClose() { this.closed.emit(); } diff --git a/src/app/modules/map/popovers/alarm-popover.component.ts b/src/app/modules/map/popovers/alarm-popover.component.ts index 2e798f71..662bb3ff 100644 --- a/src/app/modules/map/popovers/alarm-popover.component.ts +++ b/src/app/modules/map/popovers/alarm-popover.component.ts @@ -5,20 +5,17 @@ import { EventEmitter, ChangeDetectionStrategy } from '@angular/core'; - import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; import { PopoverComponent } from './popover.component'; - import { AppInfo } from 'src/app/app.info'; import { SKNotification } from 'src/app/types'; - /*********** Alarm Popover *************** -title: string - title text, -aton: SKNotification - alarm data -*************************************************/ + title: string - title text, + aton: SKNotification - alarm data + *************************************************/ @Component({ selector: 'alarm-popover', changeDetection: ChangeDetectionStrategy.OnPush, @@ -36,12 +33,10 @@ aton: SKNotification - alarm data
Message:
{{ alarm.message }}
-
Type:
{{ id }}
-
Latitude:
-
 
@@ -87,11 +81,8 @@ export class AlarmPopoverComponent { @Input() canClose: boolean; @Output() info: EventEmitter = new EventEmitter(); @Output() closed: EventEmitter = new EventEmitter(); - _title: string; - constructor(public app: AppInfo) {} - ngOnInit() { if (!this.alarm) { this.handleClose(); @@ -99,18 +90,15 @@ export class AlarmPopoverComponent { this._title = this.title || 'Alarm:'; } } - ngOnChanges() { if (!this.alarm) { this.handleClose(); return; } } - handleInfo() { this.info.emit(this.alarm.id); } - handleClose() { this.closed.emit(); } diff --git a/src/app/modules/map/popovers/aton-popover.component.ts b/src/app/modules/map/popovers/aton-popover.component.ts index d9b6cd07..041f5902 100644 --- a/src/app/modules/map/popovers/aton-popover.component.ts +++ b/src/app/modules/map/popovers/aton-popover.component.ts @@ -5,22 +5,19 @@ import { EventEmitter, ChangeDetectionStrategy } from '@angular/core'; - import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; import { PopoverComponent } from './popover.component'; import { CompassComponent } from './compass.component'; - import { SKMeteo } from 'src/app/modules'; import { AppInfo } from 'src/app/app.info'; import { Convert } from 'src/app/lib/convert'; - /*********** AtoN Popover *************** -title: string - title text, -aton: SKAtoN - aton data -*************************************************/ + title: string - title text, + aton: SKAtoN - aton data + *************************************************/ @Component({ selector: 'aton-popover', changeDetection: ChangeDetectionStrategy.OnPush, @@ -39,7 +36,6 @@ aton: SKAtoN - aton data
Type:
{{ aton.type.name }}
-
Latitude:
-
Last Update:
@@ -110,15 +105,12 @@ export class AtoNPopoverComponent { @Input() canClose: boolean; @Output() info: EventEmitter = new EventEmitter(); @Output() closed: EventEmitter = new EventEmitter(); - _title: string; timeLastUpdate: string; timeAgo: string; // last update in minutes ago protected convert = Convert; isMeteo: boolean; - constructor(public app: AppInfo) {} - ngOnInit() { if (!this.aton) { this.handleClose(); @@ -126,7 +118,6 @@ export class AtoNPopoverComponent { this.isMeteo = this.aton.id.includes('meteo'); } } - ngOnChanges() { if (!this.aton) { this.handleClose(); @@ -139,11 +130,9 @@ export class AtoNPopoverComponent { const td = (new Date().valueOf() - this.aton.lastUpdated.valueOf()) / 1000; this.timeAgo = td < 60 ? '' : `(${Math.floor(td / 60)} min ago)`; } - handleInfo() { this.info.emit(this.aton.id); } - handleClose() { this.closed.emit(); } diff --git a/src/app/modules/map/popovers/compass.component.ts b/src/app/modules/map/popovers/compass.component.ts index 94da93db..b222cd1a 100644 --- a/src/app/modules/map/popovers/compass.component.ts +++ b/src/app/modules/map/popovers/compass.component.ts @@ -6,6 +6,7 @@ import { ChangeDetectionStrategy, Renderer2 } from '@angular/core'; + import { CommonModule } from '@angular/common'; //** Base class ** diff --git a/src/app/modules/map/popovers/featurelist-popover.component.ts b/src/app/modules/map/popovers/featurelist-popover.component.ts index 978b57e2..95b2b6d0 100644 --- a/src/app/modules/map/popovers/featurelist-popover.component.ts +++ b/src/app/modules/map/popovers/featurelist-popover.component.ts @@ -5,18 +5,16 @@ import { EventEmitter, ChangeDetectionStrategy } from '@angular/core'; - import { MatListModule } from '@angular/material/list'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; import { PopoverComponent } from './popover.component'; - /*********** feature List Popover *************** -title: string - title text, -features: Array - list of features -*************************************************/ + title: string - title text, + features: Array - list of features + *************************************************/ @Component({ selector: 'feature-list-popover', changeDetection: ChangeDetectionStrategy.OnPush, @@ -54,14 +52,11 @@ export class FeatureListPopoverComponent { @Output() closed: EventEmitter = new EventEmitter(); // eslint-disable-next-line @typescript-eslint/no-explicit-any @Output() selected: EventEmitter = new EventEmitter(); - //constructor() {} - // eslint-disable-next-line @typescript-eslint/no-explicit-any handleSelect(item: any) { this.selected.emit(item); } - handleClose() { this.closed.emit(); } diff --git a/src/app/modules/map/popovers/index.ts b/src/app/modules/map/popovers/index.ts index 3db01d72..f0a18fa1 100644 --- a/src/app/modules/map/popovers/index.ts +++ b/src/app/modules/map/popovers/index.ts @@ -1,8 +1,8 @@ export * from './popover.component'; +export * from './resource-popover.component'; +export * from './compass.component'; +export * from './vessel-popover.component'; export * from './featurelist-popover.component'; export * from './aircraft-popover.component'; export * from './alarm-popover.component'; export * from './aton-popover.component'; -export * from './resource-popover.component'; -export * from './compass.component'; -export * from './vessel-popover.component'; diff --git a/src/app/modules/map/popovers/popover.component.ts b/src/app/modules/map/popovers/popover.component.ts index 42f3363c..2e2f39dd 100644 --- a/src/app/modules/map/popovers/popover.component.ts +++ b/src/app/modules/map/popovers/popover.component.ts @@ -8,8 +8,8 @@ import { EventEmitter, ChangeDetectionStrategy } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; diff --git a/src/app/modules/map/popovers/resource-popover.component.ts b/src/app/modules/map/popovers/resource-popover.component.ts index e0f3559e..ec5191e7 100644 --- a/src/app/modules/map/popovers/resource-popover.component.ts +++ b/src/app/modules/map/popovers/resource-popover.component.ts @@ -10,7 +10,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; - import { PopoverComponent } from './popover.component'; import { AppInfo } from 'src/app/app.info'; @@ -25,6 +24,7 @@ id: string - resource id *************************************************/ @Component({ selector: 'resource-popover', + changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ MatButtonModule, @@ -33,7 +33,6 @@ id: string - resource id PipesModule, PopoverComponent ], - changeDetection: ChangeDetectionStrategy.OnPush, template: ` @for(p of properties; track p) { diff --git a/src/app/modules/map/popovers/vessel-popover.component.ts b/src/app/modules/map/popovers/vessel-popover.component.ts index 5f8fd970..59950c59 100644 --- a/src/app/modules/map/popovers/vessel-popover.component.ts +++ b/src/app/modules/map/popovers/vessel-popover.component.ts @@ -13,7 +13,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { PipesModule } from 'src/app/lib/pipes'; - import { PopoverComponent } from './popover.component'; import { CompassComponent } from './compass.component'; diff --git a/src/app/modules/map/vessel-calcs.types.ts b/src/app/modules/map/vessel-calcs.types.ts deleted file mode 100644 index 51b7b7b6..00000000 --- a/src/app/modules/map/vessel-calcs.types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Position } from 'src/app/types'; -import { SKVessel } from '../skresources'; - -export interface VesselLinesData { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - vesselConfig: any; // app.config.vessel - vesselSelf: SKVessel; // dfeat.self === app.data.vessels.self - vesselActive: SKVessel; // app.data.vessels.active - // eslint-disable-next-line @typescript-eslint/no-explicit-any - navData: any; // dfeat.navData.position = app.data.navData.position; - mapZoomLevel: number; // fbMap.zoomLevel - zoomOffsetLevel: number; // this.zoomOffsetLevel - units: { distance: string }; // app.config.units -} - -export interface VesselLinesResult { - laylines: { port: number[][][]; starboard: number[][][] }; - targetAngle: Array; - cog: Array; - heading: Array; - twd: Array; - awa: Array; -} diff --git a/src/app/modules/map/vessel-calcs.worker.ts b/src/app/modules/map/vessel-calcs.worker.ts deleted file mode 100644 index b8f063a2..00000000 --- a/src/app/modules/map/vessel-calcs.worker.ts +++ /dev/null @@ -1,165 +0,0 @@ -/// - -import { VesselLinesData, VesselLinesResult } from './vessel-calcs.types'; - -import { Convert } from 'src/app/lib/convert'; -import { GeoUtils, Angle } from 'src/app/lib/geoutils'; -import { computeDestinationPoint } from 'geolib'; - -// ******** MESSAGE FROM APP ************ -// ** listen for posted messages from APP -addEventListener('message', ({ data }) => { - drawVesselLines(data); -}); - -//** POST message to App** -function drawVesselLines(data: VesselLinesData) { - const vl: VesselLinesResult = { - laylines: { port: [], starboard: [] }, - targetAngle: [], - cog: [], - heading: [], - awa: [], - twd: [] - }; - - // laylines (active) - if ( - data.vesselConfig.laylines && - Array.isArray(data.navData.position) && - typeof data.navData.position[0] === 'number' && - typeof data.vesselActive.heading === 'number' - ) { - const twd_deg = Convert.radiansToDegrees(data.vesselSelf.wind.twd); - const destUpwind = - Math.abs(Angle.difference(data.navData.bearing.value, twd_deg)) < 90; - const ba_deg = Convert.radiansToDegrees( - data.vesselSelf.performance.beatAngle ?? Math.PI / 4 - ); - const destInTarget = - Math.abs(Angle.difference(data.navData.bearing.value, twd_deg)) < ba_deg; - const dtg = - data.units.distance === 'm' - ? data.navData.dtg * 1000 - : Convert.nauticalMilesToKm(data.navData.dtg * 1000); - - if (destInTarget) { - const heading_deg = Convert.radiansToDegrees(data.vesselActive.heading); - - const bta = Angle.add(heading_deg, 90); // tack angle - const hbd_rad = Convert.degreesToRadians( - Angle.difference(heading_deg, data.navData.bearing.value) - ); - const dist1 = Math.sin(hbd_rad) * dtg; - const dist2 = Math.cos(hbd_rad) * dtg; - const pt1 = computeDestinationPoint( - data.vesselActive.position, - dist1, - bta - ); - const pt2 = computeDestinationPoint( - data.vesselActive.position, - dist2, - heading_deg - ); - const p1a = [data.vesselActive.position, [pt1.longitude, pt1.latitude]]; - const p1b = [[pt1.longitude, pt1.latitude], data.navData.position]; - const l1 = hbd_rad < 0 ? [p1a, p1b] : [p1b, p1a]; - - const p2a = [[pt2.longitude, pt2.latitude], data.navData.position]; - const p2b = [data.vesselActive.position, [pt2.longitude, pt2.latitude]]; - const l2 = hbd_rad < 0 ? [p2a, p2b] : [p2b, p2a]; - - vl.laylines = { - port: hbd_rad < 0 ? l2 : l1, - starboard: hbd_rad < 0 ? l1 : l2 - }; - } - - // beat / gybe angle lines - if (destUpwind) { - const ta_deg = ba_deg; - const bapt1 = computeDestinationPoint( - data.vesselActive.position, - dtg, - Angle.add(twd_deg, ta_deg) - ); - const bapt2 = computeDestinationPoint( - data.vesselActive.position, - dtg, - Angle.add(twd_deg, 0 - ta_deg) - ); - - vl.targetAngle = [ - [bapt1.longitude, bapt1.latitude], - data.vesselActive.position, - [bapt2.longitude, bapt2.latitude] - ]; - } - } - - // ** cog line (active) ** - const sog = data.vesselActive.sog || 0; - const cl = sog * (data.vesselConfig.cogLine * 60); - if (data.vesselActive.cog) { - vl.cog = [ - data.vesselActive.position, - GeoUtils.destCoordinate( - data.vesselActive.position, - data.vesselActive.cog, - cl - ) - ]; - } - - // ** heading line (active) ** - const z = data.mapZoomLevel; - const offset = z < 29 ? data.zoomOffsetLevel[Math.floor(z)] : 60; - const wMax = 10; // ** max line length - - let hl = 0; - if (data.vesselConfig.headingLineSize === -1) { - hl = (sog > wMax ? wMax : sog) * offset; - } else { - hl = Convert.nauticalMilesToKm(data.vesselConfig.headingLineSize) * 1000; - } - vl.heading = [ - data.vesselActive.position, - GeoUtils.destCoordinate( - data.vesselActive.position, - data.vesselActive.orientation, - hl - ) - ]; - - // ** awa (focused) ** - let aws = data.vesselActive.wind.aws || 0; - if (aws > wMax) { - aws = wMax; - } - - vl.awa = [ - data.vesselActive.position, - GeoUtils.destCoordinate( - data.vesselActive.position, - data.vesselActive.wind.awa + data.vesselActive.orientation, - typeof data.vesselActive.orientation === 'number' ? aws * offset : 0 - ) - ]; - - // ** twd (focused) ** - let tws = data.vesselActive.wind.tws || 0; - if (tws > wMax) { - tws = wMax; - } - vl.twd = [ - data.vesselActive.position, - GeoUtils.destCoordinate( - data.vesselActive.position, - data.vesselActive.wind.direction || 0, - typeof data.vesselActive.orientation === 'number' ? tws * offset : 0 - ) - ]; - - postMessage(vl); -} diff --git a/src/app/modules/skresources/components/ais/aislist.ts b/src/app/modules/skresources/components/ais/aislist.ts index bf998a79..c4e7037f 100644 --- a/src/app/modules/skresources/components/ais/aislist.ts +++ b/src/app/modules/skresources/components/ais/aislist.ts @@ -107,10 +107,9 @@ export class AISListComponent { id: 90, description: 'Other', selected: false, - icon: './assets/img/ais_special.png' + icon: './assets/img/ais_other.png' } ]; - otherShiptypes = [10, 20, 30, 90]; constructor(public app: AppInfo) {} diff --git a/src/app/modules/skresources/components/charts/chartlist.html b/src/app/modules/skresources/components/charts/chartlist.html index ec6ce879..e9d6dc34 100644 --- a/src/app/modules/skresources/components/charts/chartlist.html +++ b/src/app/modules/skresources/components/charts/chartlist.html @@ -82,7 +82,7 @@ mat-button (click)="toggleChartBoundaries()" matTooltip="Chart boundaries" - matTooltipPosition="top" + matTooltipPosition="right" > import_export Re-order diff --git a/src/app/modules/skresources/resource-classes.ts b/src/app/modules/skresources/resource-classes.ts index 53d044f3..f6fcf488 100644 --- a/src/app/modules/skresources/resource-classes.ts +++ b/src/app/modules/skresources/resource-classes.ts @@ -209,7 +209,8 @@ export class SKVessel { previousPoint: {} }; performance = { - beatAngle: null + beatAngle: null, + gybeAngle: null }; properties = {}; } diff --git a/src/app/modules/skstream/skstream.worker.ts b/src/app/modules/skstream/skstream.worker.ts index 56bbcc02..17101dc5 100644 --- a/src/app/modules/skstream/skstream.worker.ts +++ b/src/app/modules/skstream/skstream.worker.ts @@ -768,6 +768,8 @@ function processVessel(d: SKVessel, v, isSelf = false) { } } else if (v.path === 'performance.beatAngle') { d.performance.beatAngle = v.value; + } else if (v.path === 'performance.gybeAngle') { + d.performance.gybeAngle = v.value; } else if (v.path === 'communication.callsignVhf') { d.callsign = v.value; } else if (v.path === 'design.aisShipType') { diff --git a/src/assets/help/img/ais_shiptypes.png b/src/assets/help/img/ais_shiptypes.png index fccdf2ab..17d1fe5f 100644 Binary files a/src/assets/help/img/ais_shiptypes.png and b/src/assets/help/img/ais_shiptypes.png differ diff --git a/src/assets/help/index.html b/src/assets/help/index.html index 0ddb45a9..79848c80 100644 --- a/src/assets/help/index.html +++ b/src/assets/help/index.html @@ -1578,7 +1578,7 @@

Vessels

- +
Other (AIS type 90-99)
diff --git a/src/assets/img/ais_other.png b/src/assets/img/ais_other.png new file mode 100644 index 00000000..411da1e4 Binary files /dev/null and b/src/assets/img/ais_other.png differ diff --git a/src/assets/img/ais_passenger.png b/src/assets/img/ais_passenger.png index 8c1bc21b..e6f8e50a 100644 Binary files a/src/assets/img/ais_passenger.png and b/src/assets/img/ais_passenger.png differ diff --git a/src/assets/img/ais_special.png b/src/assets/img/ais_special.png index 411da1e4..790a00ba 100644 Binary files a/src/assets/img/ais_special.png and b/src/assets/img/ais_special.png differ diff --git a/src/index.html b/src/index.html index 808c1638..b5274723 100644 --- a/src/index.html +++ b/src/index.html @@ -27,6 +27,12 @@ + diff --git a/tsconfig.json b/tsconfig.json index 713f1b25..f6b73653 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,8 +4,8 @@ "baseUrl": "./", "outDir": "./dist/out-tsc", "sourceMap": true, - "esModuleInterop": true, "declaration": false, + "downlevelIteration": true, "experimentalDecorators": true, "module": "es2022", "moduleResolution": "node",