diff --git a/api/web/package-lock.json b/api/web/package-lock.json index b4ca0c412..3230952b8 100644 --- a/api/web/package-lock.json +++ b/api/web/package-lock.json @@ -18,6 +18,7 @@ "@turf/boolean-within": "^7.1.0", "@turf/envelope": "^7.1.0", "@turf/point-on-feature": "^7.0.0", + "@turf/sector": "^7.1.0", "apexcharts": "^3.0.0", "core-js": "^3.6.4", "cronstrue": "^2.19.0", @@ -3610,7 +3611,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.1.0.tgz", "integrity": "sha512-6qhF1drjwH0Dg3ZB9om1JkWTJfAqBcbtIrAj5UPlrAeHP87hGoCO2ZEsFEAL9Q18vntpivT89Uho/nqQUjJhYw==", - "dev": true, "license": "MIT", "dependencies": { "@turf/destination": "^7.1.0", @@ -3640,7 +3640,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.1.0.tgz", "integrity": "sha512-97XuvB0iaAiMg86hrnZ529WwP44TQAA9mmI5PMlchACiA4LFrEtWjjDzvO6234coieoqhrw6dZYcJvd5O2PwrQ==", - "dev": true, "license": "MIT", "dependencies": { "@turf/helpers": "^7.1.0", @@ -3747,7 +3746,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.1.0.tgz", "integrity": "sha512-9/bM34PozTyJ5FXXPAzl/j0RpcTImgMFJZ0WhH0pZZEZRum6P0rJnENt2E2qI441zeozQ9H6X5DCiJogDmRUEw==", - "dev": true, "license": "MIT", "dependencies": { "@turf/circle": "^7.1.0", @@ -3860,7 +3858,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.1.0.tgz", "integrity": "sha512-2FI2rg//eXpa/l+WJtFfvHaf1NJ7ie2MoJ+RH5dKANtrfoof1Ed+y9dXSyuhem2tp/Srq2GhrjaSofFN5/g5vA==", - "dev": true, "license": "MIT", "dependencies": { "@turf/circle": "^7.1.0", diff --git a/api/web/package.json b/api/web/package.json index 953bc9834..d81be2b4f 100644 --- a/api/web/package.json +++ b/api/web/package.json @@ -21,6 +21,7 @@ "@turf/boolean-within": "^7.1.0", "@turf/envelope": "^7.1.0", "@turf/point-on-feature": "^7.0.0", + "@turf/sector": "^7.1.0", "apexcharts": "^3.0.0", "core-js": "^3.6.4", "cronstrue": "^2.19.0", @@ -54,12 +55,12 @@ "eslint": "^9.0.0", "eslint-plugin-vue": "^9.0.0", "openapi-typescript": "^7.0.0", + "typescript": "5.6.2", "typescript-eslint": "^8.3.0", "vite": "^5.0.0", "vite-plugin-babel": "^1.2.0", "vite-plugin-pwa": "^0.21.0", - "vue-tsc": "2.0.29", - "typescript": "5.6.2" + "vue-tsc": "2.0.29" }, "browserslist": [ "> 1%", diff --git a/api/web/src/stores/base/cot.ts b/api/web/src/stores/base/cot.ts index 2c595080b..ff25e37c0 100644 --- a/api/web/src/stores/base/cot.ts +++ b/api/web/src/stores/base/cot.ts @@ -1,4 +1,5 @@ import { std } from '../../std.ts'; +import { sector } from '@turf/sector'; import { bbox } from '@turf/bbox' import { useCOTStore } from '../cots.ts' import { useMapStore } from '../map.ts'; @@ -45,7 +46,9 @@ export default class COT { _geometry: Feature["geometry"]; _store: ReturnType; - origin: Origin + origin: Origin; + + links: Set; constructor(feat: Feature, origin?: Origin) { feat.properties = COT.style(feat.geometry.type, feat.properties); @@ -58,6 +61,8 @@ export default class COT { this._store = useCOTStore(); this.origin = origin || { mode: OriginMode.CONNECTION }; + this.links = new Set(); + if (!this._properties.archived) { this._properties.archived = false } @@ -69,6 +74,10 @@ export default class COT { if (this.origin.mode === OriginMode.CONNECTION) { this._store.pending.set(this.id, this); } + + if (this._properties.sensor) { + this.link(); + } } set properties(properties: Feature["properties"]) { @@ -82,18 +91,66 @@ export default class COT { set geometry(geometry: Feature["geometry"]) { this.update({ geometry }) } - get geometry() { return this._geometry; } + link(): void { + if (this._properties.sensor) { + const id = `${this.id}-sensor` + + let updated = false; + + console.error('SENSOR RANGE', this._properties.sensor.range) + + if (this.links.has(id)) { + const cot = this._store.get(id); + if (cot) { + updated = true; + + cot.geometry = sector( + this._properties.center, + (this._properties.sensor.range || 10) / 1000, + this._properties.sensor.azimuth, + this._properties.sensor.azimuth + this._properties.sensor.fov, + ).geometry + } + } + + if (!updated) { + // TODO Use NodeCoT & Respect Sensor Style if present + new COT({ + id, + type: 'Feature', + properties: { + callsign: '', + type: 'u-d-p', + stroke: '#ffffff', + 'stroke-width': 1, + 'stroke-opacity': 1, + fill: '#ffffff', + 'fill-opacity': 0.2 + }, + geometry: sector( + this._properties.center, + (this._properties.sensor.range || 10) / 1000, + this._properties.sensor.azimuth, + this._properties.sensor.azimuth + this._properties.sensor.fov, + ).geometry + }); + + this.links.add(id) + } + } + } + /** * Update the COT and return a boolean as to whether the COT needs to be re-rendered */ - async update(update: { + update(update: { properties?: Feature["properties"], geometry?: Feature["geometry"] - }): Promise { + }): boolean { let changed = false; if (update.geometry) { //TODO Detect Geometry changes, use centroid?! @@ -110,6 +167,14 @@ export default class COT { break; } } + + if (update.properties.sensor) { + this.link(); + } else if (this._properties.sensor && !update.properties.sensor) { + // TODO Unlink & cleanup + } + + this._properties = update.properties; } if (!this._properties.center) {