From 03377a9f5dca9aa4e21f9cdc1eed7c677084cbcb Mon Sep 17 00:00:00 2001
From: panaaj <38519157+panaaj@users.noreply.github.com>
Date: Fri, 12 Jul 2024 10:39:46 +0930
Subject: [PATCH] standalone alignment
---
.gitignore | 1 +
CHANGELOG.md | 5 +-
angular.json | 19 +-
package.json | 32 +--
src/app-theme.scss | 39 +--
src/app/app.info.ts | 13 +-
src/app/app.module.ts | 14 +-
.../dialogs/common/dialogs.component.ts | 4 +-
src/app/lib/components/index.ts | 1 -
src/app/lib/components/wakelock.component.ts | 1 -
src/app/modules/map/fb-map.component.html | 6 +-
src/app/modules/map/fb-map.component.ts | 239 ++++++++++++++----
src/app/modules/map/mapconfig.ts | 7 +-
src/app/modules/map/ol/index.ts | 2 +-
.../lib/navigation/layer-layline.component.ts | 2 +-
.../lib/resources/layer-charts.component.ts | 4 +-
.../map/ol/lib/vectorLayerStyleFactory.ts | 26 +-
.../popovers/aircraft-popover.component.ts | 18 +-
.../map/popovers/alarm-popover.component.ts | 18 +-
.../map/popovers/aton-popover.component.ts | 17 +-
.../modules/map/popovers/compass.component.ts | 1 +
.../popovers/featurelist-popover.component.ts | 11 +-
src/app/modules/map/popovers/index.ts | 6 +-
.../modules/map/popovers/popover.component.ts | 2 +-
.../popovers/resource-popover.component.ts | 3 +-
.../map/popovers/vessel-popover.component.ts | 1 -
src/app/modules/map/vessel-calcs.types.ts | 23 --
src/app/modules/map/vessel-calcs.worker.ts | 165 ------------
.../skresources/components/ais/aislist.ts | 3 +-
.../components/charts/chartlist.html | 4 +-
.../modules/skresources/resource-classes.ts | 3 +-
src/app/modules/skstream/skstream.worker.ts | 2 +
src/assets/help/img/ais_shiptypes.png | Bin 31928 -> 32439 bytes
src/assets/help/index.html | 2 +-
src/assets/img/ais_other.png | Bin 0 -> 237 bytes
src/assets/img/ais_passenger.png | Bin 269 -> 252 bytes
src/assets/img/ais_special.png | Bin 237 -> 231 bytes
src/index.html | 6 +
tsconfig.json | 2 +-
39 files changed, 307 insertions(+), 395 deletions(-)
delete mode 100644 src/app/modules/map/vessel-calcs.types.ts
delete mode 100644 src/app/modules/map/vessel-calcs.worker.ts
create mode 100644 src/assets/img/ais_other.png
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) {
}
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 }}
-
-
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 }}
-
-
-
-
-
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 fccdf2ab5dd4d08e0ef942b399ba2da4b381e088..17d1fe5fd205e6f48db27664a719b89aa11cf107 100644
GIT binary patch
literal 32439
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