diff --git a/__tests__/chart/ChartLoader.test.ts b/__tests__/chart/ChartLoader.test.ts new file mode 100644 index 0000000..8ce8ef4 --- /dev/null +++ b/__tests__/chart/ChartLoader.test.ts @@ -0,0 +1,46 @@ +import { ChartLoader } from "../../src/chart"; +import { isPerformanceCalculator, PerformanceCalculator } from "../../src/performance/performance-types"; + +import cruiseAirspeedJson from "./chase/da40-cruise-airspeed.json"; +import cruiseAirspeedProjJson from "./chase/wpd/da40-cruise-airspeed.wpd.json"; + +describe("ChartLoader", () => { + describe("load()", () => { + const instance = ChartLoader.create(new URL("http://localhost"), fetchTestJson); + it("loads the cruise-airspeed chart", async () => { + const chart = await instance.load("http://localhost/cruise-airspeed.json"); + expect(isPerformanceCalculator(chart)).toBe(true); + expect(chart.meta.src.href).toEqual("http://localhost/cruise-airspeed.json"); + expect(chart.meta.image.src.href).toEqual("http://localhost/cruise-airspeed.png"); + expect(chart.meta.image.size).toEqual([978, 692]); + const solution = (chart as unknown as PerformanceCalculator).calculate({ + outsideAirTemperature: { + value: 15, + unit: "degrees celsius", + }, + power: { + value: 55, + unit: "percent", + }, + pressureAltitude: { + value: 5_000, + unit: "feet", + }, + }); + const { vars: { trueAirspeed } } = solution; + expect(trueAirspeed.unit).toEqual("knots"); + expect(trueAirspeed.value).toBeCloseTo(118.46); + }); + }); +}); + +function fetchTestJson(url: URL): Promise { + switch (url.href) { + case "http://localhost/cruise-airspeed.json": + return Promise.resolve(cruiseAirspeedJson) as Promise; + case "http://localhost/cruise-airspeed.wpd.json": + return Promise.resolve(cruiseAirspeedProjJson) as Promise; + default: + throw Error("Unsupported URL."); + } +} diff --git a/__tests__/chart/Flow.test.ts b/__tests__/chart/Flow.test.ts deleted file mode 100644 index 517c436..0000000 --- a/__tests__/chart/Flow.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {Flow} from "../../src/chart/Flow"; - -describe("Flow", () => { - describe("compare()", () => { - it("should compare correctly for flow: DOWN", () => { - expect(Flow.DOWN.compare([3, 1], [1, 3])).toBeLessThan(0); - expect(Flow.DOWN.compare([1, 3], [3, 1])).toBeGreaterThan(0); - expect(Flow.DOWN.compare([1, 3], [3, 3])).toEqual(0); - }); - it("should compare correctly for flow: LEFT", () => { - expect(Flow.LEFT.compare([1, 3], [3, 1])).toBeGreaterThan(0); - expect(Flow.LEFT.compare([3, 1], [1, 3])).toBeLessThan(0); - expect(Flow.LEFT.compare([3, 1], [3, 3])).toEqual(0); - }); - it("should compare correctly for flow: RIGHT", () => { - expect(Flow.RIGHT.compare([1, 3], [3, 1])).toBeLessThan(0); - expect(Flow.RIGHT.compare([3, 1], [1, 3])).toBeGreaterThan(0); - expect(Flow.RIGHT.compare([3, 1], [3, 3])).toEqual(0); - }); - it("should compare correctly for flow: UP", () => { - expect(Flow.UP.compare([3, 1], [1, 3])).toBeGreaterThan(0); - expect(Flow.UP.compare([1, 3], [3, 1])).toBeLessThan(0); - expect(Flow.UP.compare([1, 3], [3, 3])).toEqual(0); - }); - }); - describe("position()", () => { - it("should extract correctly for flow: DOWN", () => { - expect(Flow.DOWN.position([1, 2])).toEqual(2); - }); - it("should extract correctly for flow: LEFT", () => { - expect(Flow.LEFT.position([1, 2])).toEqual(1); - }); - it("should extract correctly for flow: RIGHT", () => { - expect(Flow.RIGHT.position([1, 2])).toEqual(1); - }); - it("should extract correctly for flow: UP", () => { - expect(Flow.UP.position([1, 2])).toEqual(2); - }); - }); - describe("sort()", () => { - it("should sort correctly for flow: DOWN", () => { - expect(Flow.DOWN.sort([[1, 3], [2, 2], [3, 1]])).toEqual([[3, 1], [2, 2], [1, 3]]); - }); - it("should sort correctly for flow: LEFT", () => { - expect(Flow.LEFT.sort([[1, 3], [2, 2], [3, 1]])).toEqual([[3, 1], [2, 2], [1, 3]]); - }); - it("should sort correctly for flow: RIGHT", () => { - expect(Flow.RIGHT.sort([[1, 3], [2, 2], [3, 1]])).toEqual([[1, 3], [2, 2], [3, 1]]); - }); - it("should sort correctly for flow: DOWN", () => { - expect(Flow.UP.sort([[1, 3], [2, 2], [3, 1]])).toEqual([[1, 3], [2, 2], [3, 1]]); - }); - }); - describe("value()", () => { - it("should extract correctly for flow: DOWN", () => { - expect(Flow.DOWN.value([1, 2])).toEqual(1); - }); - it("should extract correctly for flow: LEFT", () => { - expect(Flow.LEFT.value([1, 2])).toEqual(2); - }); - it("should extract correctly for flow: RIGHT", () => { - expect(Flow.RIGHT.value([1, 2])).toEqual(2); - }); - it("should extract correctly for flow: UP", () => { - expect(Flow.UP.value([1, 2])).toEqual(1); - }); - }); -}); diff --git a/__tests__/chart/chase/ChaseChart.test.ts b/__tests__/chart/chase/ChaseChart.test.ts new file mode 100644 index 0000000..8f046e8 --- /dev/null +++ b/__tests__/chart/chase/ChaseChart.test.ts @@ -0,0 +1,196 @@ +import { WpdProject } from "../../../src/chart/chase/wpd"; +import { WpdProjectDef } from "../../../src/chart/chase/wpd"; +import { ChaseChartDef } from "../../../src/chart/chase/chase-types"; + +import cruiseAirspeedJson from "./da40-cruise-airspeed.json"; +import cruiseAirspeedProjJson from "./wpd/da40-cruise-airspeed.wpd.json"; +import landingDistanceFlapsUpJson from "./da40-landing-distance-flaps-up.json"; +import landingDistanceFlapsUpProjJson from "./wpd/da40-landing-distance-flaps-up.wpd.json"; +import landingDistanceJson from "./da40-landing-distance.json"; +import landingDistanceProjJson from "./wpd/da40-landing-distance.wpd.json"; +import takeoffClimbRateJson from "./da40-takeoff-climb-rate.json"; +import takeoffClimbRateProjJson from "./wpd/da40-takeoff-climb-rate.wpd.json"; +import takeoffDistanceJson from "./da40-takeoff-distance.json"; +import takeoffDistanceProjJson from "./wpd/da40-takeoff-distance.wpd.json"; +import { ChaseChart } from "../../../src/chart/chase"; + +describe("ChaseChart", () => { + const src = new URL("https://bogus.com/chart.json"); + describe("create()", () => { + const proj = WpdProject.create(takeoffDistanceProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(takeoffDistanceJson as unknown as ChaseChartDef, proj, src); + it("correctly extracts inputs and outputs", () => { + expect(chart.inputs).toEqual({ + obstacleHeight: { + range: [0, 15], + unit: "meters", + }, + outsideAirTemperature: { + range: [-20, 50], + unit: "degrees celsius", + }, + pressureAltitude: { + range: [0, 10000], + unit: "feet", + }, + weight: { + range: [850, 1200], + unit: "kilograms", + }, + windComponent: { + range: [-5, 20], + unit: "knots", + }, + }); + expect(chart.outputs).toEqual({ + takeoffDistance: { + unit: "meters", + }, + }); + }); + it("throws an error if inputs are missing", () => { + expect(() => chart.calculate({})).toThrow("Missing inputs: obstacleHeight, outsideAirTemperature, pressureAltitude, weight, windComponent"); + }); + }); + describe("calculate()", () => { + describe("da40-cruise-airspeed.json", () => { + const proj = WpdProject.create(cruiseAirspeedProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(cruiseAirspeedJson as unknown as ChaseChartDef, proj, src); + it("calculates cruise airspeed", () => { + const solution = chart.calculate({ + outsideAirTemperature: { + value: 15, + unit: "degrees celsius", + }, + power: { + value: 55, + unit: "percent", + }, + pressureAltitude: { + value: 5_000, + unit: "feet", + }, + }); + const { vars: { trueAirspeed } } = solution; + expect(trueAirspeed.unit).toEqual("knots"); + expect(trueAirspeed.value).toBeCloseTo(118.46); + }); + }); + describe("da40-landing-distance.json", () => { + const proj = WpdProject.create(landingDistanceProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(landingDistanceJson as unknown as ChaseChartDef, proj, src); + it("calculates landing distance", () => { + const solution = chart.calculate({ + obstacleHeight: { + value: 15, + unit: "meters", + }, + outsideAirTemperature: { + value: 15, + unit: "degrees celsius", + }, + pressureAltitude: { + value: 2_000, + unit: "feet", + }, + weight: { + value: 1_000, + unit: "kilograms", + }, + windComponent: { + value: 10, + unit: "knots", + }, + }); + const { vars: { landingDistance } } = solution; + expect(landingDistance.unit).toEqual("meters"); + expect(landingDistance.value).toBeCloseTo(422.70); + }); + }); + describe("da40-landing-distance-flaps-up.json", () => { + const proj = WpdProject.create(landingDistanceFlapsUpProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(landingDistanceFlapsUpJson as unknown as ChaseChartDef, proj, src); + it("calculates flaps-up landing distance", () => { + const solution = chart.calculate({ + obstacleHeight: { + value: 15, + unit: "meters", + }, + outsideAirTemperature: { + value: 10, + unit: "degrees celsius", + }, + pressureAltitude: { + value: 4_000, + unit: "feet", + }, + weight: { + value: 1_000, + unit: "kilograms", + }, + windComponent: { + value: 10, + unit: "knots", + }, + }); + const { vars: { landingDistance } } = solution; + expect(landingDistance.unit).toEqual("meters"); + expect(landingDistance.value).toBeCloseTo(567.51); + }); + }); + describe("da40-takeoff-climb-rate.json", () => { + const proj = WpdProject.create(takeoffClimbRateProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(takeoffClimbRateJson as unknown as ChaseChartDef, proj, src); + it("calculates takeoff climb rate", () => { + const solution = chart.calculate({ + outsideAirTemperature: { + value: 15, + unit: "degrees celsius", + }, + pressureAltitude: { + value: 2_000, + unit: "feet", + }, + weight: { + value: 1_000, + unit: "kilograms", + }, + }); + const { vars: { climbRate } } = solution; + expect(climbRate.unit).toEqual("feet per minute"); + expect(climbRate.value).toBeCloseTo(987.17); + }); + }); + describe("da40-takeoff-distance.json", () => { + const proj = WpdProject.create(takeoffDistanceProjJson as unknown as WpdProjectDef); + const chart = ChaseChart.create(takeoffDistanceJson as unknown as ChaseChartDef, proj, src); + it("calculates takeoff distance", () => { + const solution = chart.calculate({ + obstacleHeight: { + value: 15, + unit: "meters", + }, + outsideAirTemperature: { + value: 15, + unit: "degrees celsius", + }, + pressureAltitude: { + value: 2_000, + unit: "feet", + }, + weight: { + value: 1_000, + unit: "kilograms", + }, + windComponent: { + value: 10, + unit: "knots", + }, + }); + const { vars: { takeoffDistance } } = solution; + expect(takeoffDistance.unit).toEqual("meters"); + expect(takeoffDistance.value).toBeCloseTo(304.48); + }); + }); + }); +}); diff --git a/__tests__/chart/chase/da40-cruise-airspeed.json b/__tests__/chart/chase/da40-cruise-airspeed.json new file mode 100644 index 0000000..81bae65 --- /dev/null +++ b/__tests__/chart/chase/da40-cruise-airspeed.json @@ -0,0 +1,41 @@ +{ + "kind": "chase", + "version": "1.0", + "image": { + "src": "./cruise-airspeed.png", + "size": [978, 692] + }, + "project": { + "src": "./cruise-airspeed.wpd.json" + }, + "steps": [ + { + "chase": "up", + "along": "outsideAirTemperature", + "until": "pressureAltitude", + "unit": "feet", + "advance": false + }, + { + "chase": "right", + "along": "pressureAltitude", + "until": "outsideAirTemperature", + "unit": "degrees celsius" + }, + { + "chase": "right", + "along": "densityAltitudeCorrection" + }, + { + "chase": "right", + "along": "densityAltitude", + "until": "power", + "unit": "percent" + }, + { + "solve": "down", + "using": "trueAirspeed", + "unit": "knots" + } + ] +} diff --git a/__tests__/chart/chase/da40-landing-distance-flaps-up.json b/__tests__/chart/chase/da40-landing-distance-flaps-up.json new file mode 100644 index 0000000..e57acbe --- /dev/null +++ b/__tests__/chart/chase/da40-landing-distance-flaps-up.json @@ -0,0 +1,52 @@ +{ + "kind": "chase", + "version": "1.0", + "image": { + "src": "./landing-distance-flaps-up.png", + "size": [ + 1109, + 592 + ] + }, + "project": { + "src": "./landing-distance-flaps-up.wpd.json" + }, + "steps": [ + { + "chase": "up", + "along": "outsideAirTemperature", + "until": "pressureAltitude", + "unit": "feet", + "advance": false + }, + { + "chase": "right", + "along": "pressureAltitude", + "until": "outsideAirTemperature", + "unit": "degrees celsius" + }, + { + "chase": "right", + "along": "weightCorrection", + "until": "weight", + "unit": "kilograms" + }, + { + "chase": "right", + "along": "windComponentCorrection", + "until": "windComponent", + "unit": "knots" + }, + { + "chase": "right", + "along": "obstacleHeightCorrection", + "until": "obstacleHeight", + "unit": "meters" + }, + { + "solve": "right", + "using": "landingDistance", + "unit": "meters" + } + ] +} diff --git a/__tests__/chart/chase/da40-landing-distance.json b/__tests__/chart/chase/da40-landing-distance.json new file mode 100644 index 0000000..b21ac17 --- /dev/null +++ b/__tests__/chart/chase/da40-landing-distance.json @@ -0,0 +1,52 @@ +{ + "kind": "chase", + "version": "1.0", + "image": { + "src": "./landing-distance.png", + "size": [ + 1154, + 697 + ] + }, + "project": { + "src": "./landing-distance.wpd.json" + }, + "steps": [ + { + "chase": "up", + "along": "outsideAirTemperature", + "until": "pressureAltitude", + "unit": "feet", + "advance": false + }, + { + "chase": "right", + "along": "pressureAltitude", + "until": "outsideAirTemperature", + "unit": "degrees celsius" + }, + { + "chase": "right", + "along": "weightCorrection", + "until": "weight", + "unit": "kilograms" + }, + { + "chase": "right", + "along": "windComponentCorrection", + "until": "windComponent", + "unit": "knots" + }, + { + "chase": "right", + "along": "obstacleHeightCorrection", + "until": "obstacleHeight", + "unit": "meters" + }, + { + "solve": "right", + "using": "landingDistance", + "unit": "meters" + } + ] +} \ No newline at end of file diff --git a/__tests__/chart/chase/da40-takeoff-climb-rate.json b/__tests__/chart/chase/da40-takeoff-climb-rate.json new file mode 100644 index 0000000..5ae159c --- /dev/null +++ b/__tests__/chart/chase/da40-takeoff-climb-rate.json @@ -0,0 +1,41 @@ +{ + "kind": "chase", + "version": "1.0", + "label": "Takeoff Climb Rate", + "image": { + "src": "./takeoff-climb-rate.png", + "size": [ + 1193, + 811 + ] + }, + "project": { + "src": "./takeoff-climb-rate.wpd.json" + }, + "steps": [ + { + "chase": "up", + "along": "outsideAirTemperature", + "until": "pressureAltitude", + "unit": "feet", + "advance": false + }, + { + "chase": "right", + "along": "pressureAltitude", + "until": "outsideAirTemperature", + "unit": "degrees celsius" + }, + { + "chase": "right", + "along": "weightCorrection", + "until": "weight", + "unit": "kilograms" + }, + { + "solve": "right", + "using": "climbRate", + "unit": "feet per minute" + } + ] +} diff --git a/__tests__/chart/chase/da40-takeoff-distance.json b/__tests__/chart/chase/da40-takeoff-distance.json new file mode 100644 index 0000000..144af13 --- /dev/null +++ b/__tests__/chart/chase/da40-takeoff-distance.json @@ -0,0 +1,52 @@ +{ + "kind": "chase", + "version": "1.0", + "image": { + "src": "./da40-takeoff-distance.png", + "size": [1156, 699] + }, + "project": { + "src": "./da40-takeoff-distance.wpd.json" + }, + "steps": [ + { + "chase": "up", + "along": "outsideAirTemperature", + "until": "pressureAltitude", + "unit": "feet", + "advance": false + }, + { + "chase": "right", + "along": "pressureAltitude", + "until": "outsideAirTemperature", + "unit": "degrees celsius" + }, + { + "chase": "right", + "along": "weightCorrection", + "until": "weight", + "unit": "kilograms" + }, + { + "chase": "right", + "along": { + "windComponent >= 0": "headwindComponentCorrection", + "windComponent < 0": "tailwindComponentCorrection" + }, + "until": "windComponent", + "unit": "knots" + }, + { + "chase": "right", + "along": "obstacleHeightCorrection", + "until": "obstacleHeight", + "unit": "meters" + }, + { + "solve": "right", + "using": "takeoffDistance", + "unit": "meters" + } + ] +} diff --git a/__tests__/chart/chase/wpd/WpdProject.test.ts b/__tests__/chart/chase/wpd/WpdProject.test.ts new file mode 100644 index 0000000..3cb05c4 --- /dev/null +++ b/__tests__/chart/chase/wpd/WpdProject.test.ts @@ -0,0 +1,35 @@ +import _ from "lodash"; +import { WpdProject } from "../../../../src/chart/chase/wpd"; +import { WpdProjectDef } from "../../../../src/chart/chase/wpd/wpd-types"; + +import projectJson from "./da40-takeoff-distance.wpd.json"; + +describe("WpdProject", () => { + describe("range()", () => { + const proj = WpdProject.create(projectJson as unknown as WpdProjectDef); + it("correctly extracts guides", () => { + expect(_.keys(proj.guides).sort()).toEqual([ + "headwindComponentCorrection", + "obstacleHeightCorrection", + "tailwindComponentCorrection", + "weightCorrection", + ]); + }); + it("correctly extracts scales", () => { + expect(_.keys(proj.scales).sort()).toEqual([ + "obstacleHeight", + "outsideAirTemperature", + "pressureAltitude", + "takeoffDistance", + "weight", + "windComponent", + ]); + expect(proj.range("obstacleHeight")).toEqual([0, 15]); + expect(proj.range("outsideAirTemperature")).toEqual([-20, 50]); + expect(proj.range("pressureAltitude")).toEqual([0, 10_000]); + expect(proj.range("takeoffDistance")).toEqual([100, 1400]); + expect(proj.range("weight")).toEqual([850, 1200]); + expect(proj.range("windComponent")).toEqual([-5, 20]); + }); + }); +}); diff --git a/__tests__/chart/chase/wpd/da40-cruise-airspeed.wpd.json b/__tests__/chart/chase/wpd/da40-cruise-airspeed.wpd.json new file mode 100644 index 0000000..e38d5ca --- /dev/null +++ b/__tests__/chart/chase/wpd/da40-cruise-airspeed.wpd.json @@ -0,0 +1 @@ +{"version":[4,2],"axesColl":[{"name":"Image","type":"ImageAxes"}],"datasetColl":[{"name":"guide:densityAltitudeCorrection@14000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":130.90000000000012,"y":62.69418604651117,"value":[130.90000000000012,62.69418604651117]},{"x":417,"y":208.67558139535,"value":[417,208.67558139535]}],"autoDetectionData":null},{"name":"guide:power=45","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":421.96747388414053,"y":547.3153846153845,"value":[421.96747388414053,547.3153846153845]},{"x":426.4818121693122,"y":536.3571428571429,"value":[426.4818121693122,536.3571428571429]},{"x":432.5,"y":521.7,"value":[432.5,521.7]},{"x":437.5,"y":509.8,"value":[437.5,509.8]},{"x":442.5,"y":497.9,"value":[442.5,497.9]},{"x":447.0744598765432,"y":486.7766203703704,"value":[447.0744598765432,486.7766203703704]},{"x":451.5,"y":475.5,"value":[451.5,475.5]},{"x":456.5,"y":463.3,"value":[456.5,463.3]},{"x":461.8657958553792,"y":450.35714285714283,"value":[461.8657958553792,450.35714285714283]},{"x":467.70563271604937,"y":435.60084876543215,"value":[467.70563271604937,435.60084876543215]},{"x":474.2978395061728,"y":418.25,"value":[474.2978395061728,418.25]},{"x":479,"y":405.25,"value":[479,405.25]},{"x":483.5,"y":393.5,"value":[483.5,393.5]},{"x":488,"y":382.25,"value":[488,382.25]},{"x":493.5,"y":365.8,"value":[493.5,365.8]},{"x":501.429012345679,"y":209.1590909090909,"value":[501.429012345679,209.1590909090909]},{"x":498,"y":353.75,"value":[498,353.75]},{"x":502,"y":342.875,"value":[502,342.875]},{"x":505,"y":222.5,"value":[505,222.5]},{"x":505.5,"y":333.1666666666667,"value":[505.5,333.1666666666667]},{"x":509.08873456790127,"y":320.059595959596,"value":[509.08873456790127,320.059595959596]},{"x":508.5,"y":237,"value":[508.5,237]},{"x":511,"y":252.5,"value":[511,252.5]},{"x":511.44760802469096,"y":307.49746913580253,"value":[511.44760802469096,307.49746913580253]},{"x":512.2588734567901,"y":260.7,"value":[512.2588734567901,260.7]},{"x":513,"y":272.5,"value":[513,272.5]},{"x":513,"y":294.25,"value":[513,294.25]},{"x":513.5,"y":284.5,"value":[513.5,284.5]},{"x":417.6311728395062,"y":556.5200617283951,"value":[417.6311728395062,556.5200617283951]}],"autoDetectionData":{"fgColor":[21,23,24],"bgColor":[255,255,255],"mask":[[200006,10],[200984,11],[201962,11],[202940,11],[203918,11],[204896,11],[205874,12],[206852,13],[207830,13],[208808,14],[209786,14],[210765,13],[211743,13],[212721,13],[213699,14],[214679,14],[215657,14],[216635,14],[217614,13],[218593,12],[219571,12],[220550,11],[221528,11],[222506,12],[223484,12],[224462,12],[225441,11],[226419,12],[227397,12],[228375,12],[229353,12],[230331,12],[231309,12],[232287,12],[233266,12],[234244,13],[235222,13],[236201,13],[237179,13],[238158,12],[239136,12],[240115,11],[241093,11],[242071,11],[243050,10],[244028,10],[245006,10],[245984,10],[246962,10],[247940,10],[248918,10],[249896,10],[250874,10],[251853,9],[252831,9],[253809,9],[254787,9],[261633,9],[262611,9],[263589,9],[264567,9],[265545,9],[266523,9],[267501,9],[268479,9],[269457,9],[270436,8],[271414,8],[272392,8],[273370,8],[274348,8],[275326,8],[276304,8],[277282,8],[278260,8],[279238,8],[280216,8],[281194,8],[282172,8],[283150,8],[284128,8],[285106,8],[286084,8],[287062,8],[288040,8],[289018,8],[289996,8],[290974,8],[291952,8],[292930,8],[293908,8],[294886,8],[295864,8],[296842,8],[297820,8],[298798,8],[299775,9],[300753,9],[301731,9],[302709,9],[303687,9],[304665,9],[305642,10],[306620,10],[307597,11],[308574,12],[309551,13],[310521,24],[311499,24],[312477,24],[313455,24],[314439,13],[315417,13],[316395,12],[317372,13],[318350,12],[319327,13],[320305,13],[321283,12],[322260,12],[323238,12],[324216,11],[325194,11],[326172,11],[327150,11],[328128,11],[329106,10],[330084,10],[331062,10],[332040,10],[333017,11],[333995,11],[334973,11],[335950,12],[336928,12],[337906,11],[338883,12],[339861,11],[340838,12],[341816,12],[342794,11],[343771,12],[344749,12],[345727,12],[346705,11],[347683,11],[348661,11],[349639,11],[350616,12],[351593,13],[352571,13],[353548,14],[354526,12],[355503,13],[356481,12],[357458,13],[358435,13],[359413,13],[360390,14],[361368,14],[362346,13],[363323,13],[370166,12],[371144,12],[372121,12],[373098,13],[374076,13],[375054,12],[376031,13],[377009,12],[377986,13],[378964,12],[379942,12],[380919,12],[381897,12],[382875,12],[383853,12],[384830,12],[385808,12],[386786,12],[387764,11],[388742,11],[389720,11],[390698,11],[391675,12],[392653,12],[393631,12],[394608,12],[395586,12],[396563,12],[397541,12],[398519,12],[399496,13],[400473,13],[401451,12],[402429,12],[403407,11],[404384,12],[405362,12],[406339,12],[407317,12],[408295,11],[409273,11],[410250,12],[411228,12],[412206,11],[413183,12],[424914,13],[425892,13],[426870,12],[427847,13],[428825,13],[429803,12],[430780,13],[431758,12],[432735,13],[433713,13],[434690,13],[435668,13],[436645,13],[437623,13],[438600,13],[439578,12],[440556,12],[441533,13],[442511,12],[443488,12],[444466,12],[445444,12],[446422,12],[447399,12],[448377,12],[449354,12],[450332,12],[451309,12],[452287,12],[453264,13],[454242,13],[455220,12],[456197,13],[457175,12],[458152,13],[459130,13],[460107,13],[461085,13],[462062,13],[463040,12],[464017,13],[464995,13],[465973,12],[466951,11],[467928,12],[468906,12],[469884,11],[470861,12],[471839,12],[478683,12],[479661,12],[480639,12],[481616,12],[482594,12],[483571,13],[484549,12],[485526,13],[486503,14],[487481,13],[488458,13],[489435,14],[490412,14],[491390,14],[492367,14],[493345,13],[494323,12],[495300,13],[496277,14],[497255,13],[498233,12],[499210,13],[500187,13],[501165,13],[502143,12],[503120,13],[504098,12],[505075,13],[506052,13],[507030,13],[508007,14],[508984,15],[509962,14],[510939,14],[511916,14],[512894,13],[513871,14],[514849,13],[515827,12],[516805,12],[517783,11],[518761,11],[519739,11],[520717,11],[521694,12],[522672,12],[523649,13],[524627,13],[525604,13],[526582,13],[527560,12],[528538,12],[529516,11],[530494,10],[531471,11],[532449,11],[533426,12],[534404,12],[535381,13],[536358,14],[537336,13],[538314,13],[539292,13],[540269,14],[541248,11],[542229,7],[543207,7],[544185,7],[545163,6],[546141,5],[547119,5]],"colorDetectionMode":"fg","colorDistance":120,"algorithm":{"algoType":"AveragingWindowAlgo","xStep":10,"yStep":10},"name":7,"imageWidth":978,"imageHeight":692}},{"name":"guide:power=55","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":521.818049620133,"y":596.6597815764487,"value":[521.818049620133,596.6597815764487]},{"x":530,"y":580,"value":[530,580]},{"x":535.5,"y":568.4,"value":[535.5,568.4]},{"x":541,"y":557,"value":[541,557]},{"x":548.2904741863074,"y":542.6303030303029,"value":[548.2904741863074,542.6303030303029]},{"x":558.5,"y":520.7,"value":[558.5,520.7]},{"x":568.5,"y":499.1666666666667,"value":[568.5,499.1666666666667]},{"x":577.0798611111106,"y":481.6958333333333,"value":[577.0798611111106,481.6958333333333]},{"x":583.5,"y":467.7,"value":[583.5,467.7]},{"x":588.5,"y":456.5,"value":[588.5,456.5]},{"x":593,"y":445.875,"value":[593,445.875]},{"x":601.8865740740738,"y":426.1666666666667,"value":[601.8865740740738,426.1666666666667]},{"x":607.5,"y":411.4,"value":[607.5,411.4]},{"x":612.3121141975305,"y":399.5833333333333,"value":[612.3121141975305,399.5833333333333]},{"x":616.8333333333334,"y":388.5833333333333,"value":[616.8333333333334,388.5833333333333]},{"x":621.23225308642,"y":374.25595238095235,"value":[621.23225308642,374.25595238095235]},{"x":622.7056327160496,"y":208.95843855218857,"value":[622.7056327160496,208.95843855218857]},{"x":626,"y":358.625,"value":[626,358.625]},{"x":626.065354938271,"y":217.6,"value":[626.065354938271,217.6]},{"x":629.5,"y":344.5,"value":[629.5,344.5]},{"x":637.5370073599246,"y":263.6497920997921,"value":[637.5370073599246,263.6497920997921]},{"x":632,"y":234.5,"value":[632,234.5]},{"x":632.5,"y":332.5,"value":[632.5,332.5]},{"x":635.5532407407404,"y":319.85238095238094,"value":[635.5532407407404,319.85238095238094]},{"x":634.4126126126125,"y":243.40045045045053,"value":[634.4126126126125,243.40045045045053]},{"x":637,"y":305.5,"value":[637,305.5]},{"x":638,"y":293.7621621621621,"value":[638,293.7621621621621]},{"x":639,"y":281.89324324324315,"value":[639,281.89324324324315]},{"x":636.1405405405405,"y":254.33873873873875,"value":[636.1405405405405,254.33873873873875]},{"x":629.0918918918919,"y":225.55675675675676,"value":[629.0918918918919,225.55675675675676]},{"x":638.4900900900901,"y":271.372972972973,"value":[638.4900900900901,271.372972972973]}],"autoDetectionData":{"fgColor":[21,23,24],"bgColor":[255,255,255],"mask":[[201117,1],[202085,11],[203063,11],[204040,13],[205018,13],[205996,13],[206975,12],[207953,13],[208932,13],[209910,13],[210889,13],[211867,14],[212846,13],[213825,12],[214803,12],[215782,11],[216761,10],[217739,10],[218717,10],[219695,10],[220673,10],[221651,11],[222629,11],[223607,11],[224585,11],[225563,11],[226541,12],[227519,12],[228497,12],[229475,12],[230453,12],[231432,11],[232410,11],[233388,12],[234366,12],[235344,12],[236322,13],[237300,13],[238279,13],[239257,13],[240235,13],[241214,12],[242192,12],[243171,12],[244149,12],[245128,11],[246106,11],[247084,11],[248062,11],[249040,11],[250018,12],[250996,12],[251974,12],[252952,12],[253930,12],[254909,11],[255887,11],[256865,11],[257843,11],[258821,11],[259799,11],[260777,12],[261755,12],[262734,11],[263712,12],[264690,12],[265668,13],[266647,12],[267625,12],[268603,12],[269582,11],[270561,10],[271539,10],[272517,10],[273495,10],[274473,10],[275451,11],[276429,11],[277407,11],[278385,12],[279363,12],[280342,12],[281320,12],[282299,11],[283277,11],[284255,11],[285233,11],[286211,11],[287189,11],[288167,11],[289145,11],[290123,11],[291101,11],[292079,11],[293057,11],[294035,11],[295013,11],[295990,12],[296968,12],[297946,12],[298923,12],[299901,12],[300879,11],[301857,11],[302835,11],[303813,11],[304790,12],[305768,12],[306746,11],[307724,11],[308702,10],[309680,10],[310658,10],[311636,10],[312614,10],[313591,11],[314569,11],[315547,11],[316525,11],[317502,12],[318480,12],[319458,12],[320436,12],[321414,11],[322391,12],[323369,12],[324346,12],[325324,12],[326302,11],[327280,11],[328258,11],[329236,11],[330214,11],[331192,11],[332170,11],[333147,12],[334125,12],[335103,11],[336080,12],[337058,12],[338035,12],[339013,12],[339990,13],[340968,13],[341946,12],[342924,12],[343902,11],[344879,12],[345857,12],[346834,13],[347811,13],[348789,13],[349766,13],[350744,13],[351722,12],[352699,13],[353677,13],[354655,13],[355633,11],[356611,11],[357589,10],[358566,11],[359544,11],[360522,11],[361500,11],[362477,12],[363455,12],[364432,13],[365410,12],[366388,12],[367365,12],[368343,12],[369320,13],[370298,12],[371274,14],[372252,13],[373229,14],[374207,13],[375185,13],[376163,12],[377140,13],[378118,13],[379096,13],[380074,13],[381051,14],[382029,12],[383007,12],[383985,11],[384963,11],[385941,11],[386918,12],[387896,12],[388874,12],[389851,12],[390829,12],[391806,13],[392783,14],[393761,13],[394739,12],[395716,13],[396693,13],[397671,13],[398649,12],[399626,13],[400604,12],[401581,13],[402559,12],[403536,12],[404514,12],[405492,12],[406470,12],[407447,12],[408425,12],[409402,13],[410380,12],[411357,13],[412334,13],[413310,15],[414288,14],[415266,14],[416244,13],[417221,14],[418198,15],[419176,14],[420153,14],[421131,14],[422108,15],[423086,15],[424063,13],[425041,12],[426018,13],[426996,13],[427973,13],[428951,13],[429928,13],[430905,13],[431883,13],[432861,12],[433838,13],[434814,14],[435792,14],[436770,13],[437748,13],[438726,12],[439704,12],[440682,12],[441660,11],[442638,11],[443616,11],[444594,10],[445571,11],[446549,11],[447527,11],[448504,12],[449482,12],[450459,13],[451437,13],[452414,13],[453392,12],[454370,12],[455348,12],[456325,12],[457303,12],[458280,13],[459258,13],[460233,16],[461211,16],[462188,17],[463166,15],[464144,14],[465120,16],[466098,15],[467076,15],[468054,15],[469031,16],[470009,16],[470987,14],[471965,14],[472942,15],[473919,15],[474896,16],[475873,15],[476851,15],[477829,15],[478806,16],[479784,13],[480761,14],[481739,14],[482716,13],[483693,14],[484671,14],[485649,14],[486627,14],[487605,12],[488583,11],[489561,11],[490539,11],[491517,11],[492495,11],[493473,11],[494451,11],[495429,11],[496407,11],[497384,12],[498362,12],[499340,12],[500318,11],[501296,11],[502274,11],[503251,12],[504229,12],[505206,12],[506183,13],[507161,12],[508138,13],[509116,13],[510093,13],[511071,12],[512048,13],[513026,13],[514003,13],[514981,12],[515958,13],[516936,12],[517913,13],[518890,14],[519868,13],[520845,13],[521823,13],[522800,13],[523777,14],[524754,14],[525732,13],[526710,13],[527687,13],[528664,14],[529642,14],[530619,15],[531596,14],[532572,15],[533549,15],[534527,15],[535505,14],[536483,14],[537461,13],[538438,13],[539416,13],[540393,13],[541370,14],[542347,15],[543325,15],[544302,14],[545279,14],[546256,14],[547234,14],[548212,13],[549189,14],[550167,13],[551145,13],[552123,13],[553100,12],[554078,12],[555055,13],[556032,13],[557010,13],[557987,14],[558964,14],[559942,14],[560920,13],[561897,13],[562875,12],[563851,14],[564829,13],[565807,13],[566785,12],[567762,13],[568739,13],[569716,14],[570693,15],[571670,16],[572648,16],[573625,15],[574603,14],[575581,13],[576559,12],[577537,11],[578515,11],[579493,11],[580470,12],[581448,12],[582426,12],[583404,11],[584382,11]],"colorDetectionMode":"fg","colorDistance":120,"algorithm":{"algoType":"AveragingWindowAlgo","xStep":10,"yStep":10},"name":8,"imageWidth":978,"imageHeight":692}},{"name":"guide:power=65","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":616.8669230769227,"y":597.359914529915,"value":[616.8669230769227,597.359914529915]},{"x":623.6121275121267,"y":208.076645876646,"value":[623.6121275121267,208.076645876646]},{"x":627.5,"y":581.2142857142857,"value":[627.5,581.2142857142857]},{"x":632.674774774775,"y":217.4279279279281,"value":[632.674774774775,217.4279279279281]},{"x":640.0003861003865,"y":224.7298584298585,"value":[640.0003861003865,224.7298584298585]},{"x":634.5,"y":569.4285714285714,"value":[634.5,569.4285714285714]},{"x":641.5,"y":557.7857142857143,"value":[641.5,557.7857142857143]},{"x":647.5,"y":232.5,"value":[647.5,232.5]},{"x":648,"y":545.6833333333334,"value":[648,545.6833333333334]},{"x":654.5,"y":535.8333333333333,"value":[654.5,535.8333333333333]},{"x":658,"y":243.4,"value":[658,243.4]},{"x":661.5,"y":523.7142857142857,"value":[661.5,523.7142857142857]},{"x":667.7727272727273,"y":253.36363636363637,"value":[667.7727272727273,253.36363636363637]},{"x":668,"y":511.4166666666667,"value":[668,511.4166666666667]},{"x":674,"y":500.5833333333333,"value":[674,500.5833333333333]},{"x":677.5,"y":263.29266994266993,"value":[677.5,263.29266994266993]},{"x":680,"y":488.95,"value":[680,488.95]},{"x":686.2126126126125,"y":273.07882882882876,"value":[686.2126126126125,273.07882882882876]},{"x":685.5,"y":480,"value":[685.5,480]},{"x":691,"y":467.9166666666667,"value":[691,467.9166666666667]},{"x":695,"y":283,"value":[695,283]},{"x":696.5,"y":457.3,"value":[696.5,457.3]},{"x":701.5,"y":446.6,"value":[701.5,446.6]},{"x":705,"y":294.25,"value":[705,294.25]},{"x":706.5,"y":435.00666666666666,"value":[706.5,435.00666666666666]},{"x":711.5,"y":425.02666666666664,"value":[711.5,425.02666666666664]},{"x":714.5,"y":305.69849849849845,"value":[714.5,305.69849849849845]},{"x":715.7815315315313,"y":414.6799066924066,"value":[715.7815315315313,414.6799066924066]},{"x":722.2063063063063,"y":319.0873873873873,"value":[722.2063063063063,319.0873873873873]},{"x":722,"y":396,"value":[722,396]},{"x":726.6508365508366,"y":374.96507936507936,"value":[726.6508365508366,374.96507936507936]},{"x":726.0873873873875,"y":335.22252252252247,"value":[726.0873873873875,335.22252252252247]},{"x":725,"y":386.5,"value":[725,386.5]},{"x":728,"y":344.5,"value":[728,344.5]},{"x":729,"y":356.5,"value":[729,356.5]},{"x":728.3603603603603,"y":364.7675675675676,"value":[728.3603603603603,364.7675675675676]},{"x":719.2558558558559,"y":404.70990990991,"value":[719.2558558558559,404.70990990991]},{"x":700.1657657657657,"y":288.9945945945946,"value":[700.1657657657657,288.9945945945946]},{"x":710.1513513513514,"y":299.56756756756755,"value":[710.1513513513514,299.56756756756755]},{"x":719.2558558558559,"y":312.19639639639644,"value":[719.2558558558559,312.19639639639644]},{"x":724.2486486486487,"y":326.2936936936937,"value":[724.2486486486487,326.2936936936937]},{"x":653.4684684684685,"y":238.4792792792793,"value":[653.4684684684685,238.4792792792793]},{"x":663.1603603603604,"y":248.46486486486486,"value":[663.1603603603604,248.46486486486486]},{"x":681.9567567567568,"y":268.1423423423423,"value":[681.9567567567568,268.1423423423423]},{"x":672.5585585585586,"y":258.4504504504505,"value":[672.5585585585586,258.4504504504505]},{"x":690.7675675675675,"y":278.4216216216216,"value":[690.7675675675675,278.4216216216216]}],"autoDetectionData":{"fgColor":[21,23,24],"bgColor":[255,255,255],"mask":[[201113,11],[202091,11],[203069,11],[204047,19],[205025,19],[206003,19],[206981,19],[207959,19],[208937,20],[209915,21],[210893,22],[211871,23],[212850,23],[213826,25],[214804,25],[215782,25],[216760,25],[217738,25],[218716,22],[219694,24],[220672,25],[221650,25],[222628,26],[223606,27],[224590,23],[225569,23],[226548,24],[227527,24],[228506,6],[228513,18],[229491,19],[230470,19],[231449,19],[232429,18],[233408,18],[234387,17],[235366,16],[236344,20],[237325,17],[238305,15],[239284,18],[240262,18],[241241,17],[242220,16],[243198,17],[244177,17],[245156,17],[246135,17],[247113,18],[248093,18],[249073,17],[250052,17],[251031,17],[252010,17],[252989,17],[253968,17],[254947,17],[255927,19],[256905,19],[257885,17],[258865,16],[259843,16],[260822,15],[261801,16],[262780,17],[263759,16],[264738,15],[265716,15],[266694,17],[267672,17],[268652,15],[269631,15],[270610,14],[271588,14],[272566,14],[273547,12],[274525,15],[275503,16],[276481,17],[277460,17],[278438,18],[279417,18],[280396,17],[281375,17],[282355,15],[283334,15],[284313,14],[285291,15],[286272,13],[287250,13],[288229,13],[289207,14],[290186,14],[291165,15],[292143,17],[293122,16],[294101,15],[295080,15],[296058,15],[297037,15],[298016,15],[298995,15],[299973,16],[300951,17],[301929,18],[302907,20],[303889,18],[304868,17],[305847,15],[306826,13],[307805,16],[308783,16],[309763,14],[310742,13],[311722,11],[312702,9],[313681,5],[314659,5],[315637,5],[316615,5],[317593,5],[318571,5],[319549,5],[320527,5],[321505,5],[322483,5],[323461,6],[324439,8],[325417,8],[326395,8],[327375,6],[328353,6],[329332,5],[330310,5],[331288,5],[332266,5],[333244,5],[334222,5],[335200,5],[336178,8],[337156,8],[338134,9],[339112,9],[340090,9],[341068,9],[342046,9],[343024,9],[344002,9],[344982,7],[345961,6],[346940,5],[347918,5],[348893,8],[349871,8],[350849,8],[351827,8],[352805,8],[353783,8],[354761,8],[355739,8],[356717,8],[357695,7],[358673,7],[359651,7],[360629,6],[361607,6],[362584,7],[363562,7],[364540,6],[365517,6],[366495,6],[367473,6],[368451,5],[369429,5],[370407,5],[371385,5],[372362,6],[373340,6],[374318,5],[375296,5],[376274,5],[377252,5],[378230,5],[379208,5],[380185,6],[381163,6],[382141,5],[383118,6],[384096,5],[385074,5],[386052,5],[387030,5],[388008,4],[388986,4],[389963,5],[390941,5],[391919,5],[392897,5],[393875,5],[394853,5],[395830,6],[396808,5],[397785,6],[398762,7],[399738,9],[400716,7],[401694,6],[402672,5],[403650,5],[404628,5],[405606,5],[406584,5],[407560,7],[408538,7],[409515,7],[410493,7],[411470,6],[412448,6],[413425,6],[414403,6],[415381,5],[416358,6],[417336,6],[418313,6],[419291,6],[420268,6],[421246,6],[422223,6],[423200,7],[424177,7],[425155,6],[426133,6],[427110,6],[428088,6],[429065,6],[430043,6],[431021,5],[431999,5],[432976,6],[433953,7],[434931,6],[435908,6],[436886,6],[437864,5],[438842,5],[439820,5],[440797,6],[441775,5],[442753,5],[443730,6],[444707,7],[445685,6],[446662,6],[447639,7],[448617,8],[449594,8],[450571,9],[451549,8],[452526,9],[453502,10],[454480,10],[455458,9],[456436,7],[457414,6],[458391,6],[459369,6],[460346,6],[461324,6],[462301,6],[463279,6],[464257,5],[465234,6],[466212,6],[467189,7],[468167,6],[469144,7],[470121,8],[471098,8],[472075,8],[473053,8],[474031,7],[475009,7],[475986,8],[476964,8],[477941,9],[478919,9],[479897,8],[480875,6],[481853,6],[482829,7],[483807,7],[484784,7],[485761,7],[486738,7],[487716,6],[488694,5],[489672,5],[490648,7],[491626,7],[492603,7],[493580,8],[494558,7],[495536,6],[496513,6],[497491,6],[498468,6],[499445,7],[500422,7],[501400,7],[502377,7],[503355,6],[504332,6],[505309,6],[506286,7],[507264,7],[508242,6],[509219,7],[510197,6],[511175,6],[512152,6],[513130,6],[514107,7],[515085,7],[516061,7],[517039,7],[518017,6],[518994,6],[519971,7],[520948,7],[521924,8],[522902,7],[523879,10],[524857,10],[525835,9],[526812,9],[527790,8],[528767,8],[529745,8],[530722,8],[531700,7],[532677,7],[533655,6],[534632,7],[535610,7],[536587,8],[537564,9],[538542,9],[539519,9],[540497,9],[541474,9],[542452,8],[543429,9],[544407,8],[545383,10],[546361,9],[547338,10],[548315,10],[549293,9],[550270,10],[551247,10],[552225,10],[553202,10],[554179,10],[555157,10],[556134,10],[557112,10],[558090,9],[559067,9],[560045,9],[561023,8],[562000,8],[562977,9],[563954,9],[564931,9],[565908,10],[566886,10],[567863,10],[568841,10],[569818,9],[570796,9],[571773,10],[572751,9],[573729,9],[574706,5],[574712,4],[575683,6],[575691,1],[576661,6],[577638,6],[578616,6],[579593,7],[580570,6],[581548,6],[582525,6],[583503,5],[584481,5]],"colorDetectionMode":"fg","colorDistance":120,"algorithm":{"algoType":"AveragingWindowAlgo","xStep":10,"yStep":10},"name":9,"imageWidth":978,"imageHeight":692}},{"name":"guide:power=75","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":622.8669230769227,"y":208.06587412587413,"value":[622.8669230769227,208.06587412587413]},{"x":635.5,"y":219.91666666666666,"value":[635.5,219.91666666666666]},{"x":645,"y":229.95,"value":[645,229.95]},{"x":654,"y":239.125,"value":[654,239.125]},{"x":666,"y":251.65,"value":[666,251.65]},{"x":676.5,"y":262,"value":[676.5,262]},{"x":685.5,"y":271.45,"value":[685.5,271.45]},{"x":694,"y":281.9,"value":[694,281.9]},{"x":704,"y":293.05,"value":[704,293.05]},{"x":711,"y":580.5,"value":[711,580.5]},{"x":713.5,"y":304,"value":[713.5,304]},{"x":719,"y":568.625,"value":[719,568.625]},{"x":723.2951282051283,"y":318.97083333333325,"value":[723.2951282051283,318.97083333333325]},{"x":726.5,"y":557.7857142857143,"value":[726.5,557.7857142857143]},{"x":731.5,"y":331.7142857142857,"value":[731.5,331.7142857142857]},{"x":734,"y":546.1375,"value":[734,546.1375]},{"x":738.5,"y":343,"value":[738.5,343]},{"x":741.5,"y":535.3571428571429,"value":[741.5,535.3571428571429]},{"x":745.5,"y":354.14285714285717,"value":[745.5,354.14285714285717]},{"x":748.5,"y":524.9285714285714,"value":[748.5,524.9285714285714]},{"x":751.5,"y":364,"value":[751.5,364]},{"x":755.5,"y":513.8571428571429,"value":[755.5,513.8571428571429]},{"x":758,"y":374.7083333333333,"value":[758,374.7083333333333]},{"x":763.9444444444445,"y":501.55555555555554,"value":[763.9444444444445,501.55555555555554]},{"x":765,"y":385.0833333333333,"value":[765,385.0833333333333]},{"x":770,"y":488.38541666666663,"value":[770,488.38541666666663]},{"x":772,"y":396.4375,"value":[772,396.4375]},{"x":776.5,"y":477.33333333333337,"value":[776.5,477.33333333333337]},{"x":778.5,"y":409,"value":[778.5,409]},{"x":781,"y":465.75,"value":[781,465.75]},{"x":783,"y":418.75,"value":[783,418.75]},{"x":784,"y":453.5,"value":[784,453.5]},{"x":786.2905128205127,"y":440.7828205128204,"value":[786.2905128205127,440.7828205128204]},{"x":786.1615384615385,"y":427.3107692307692,"value":[786.1615384615385,427.3107692307692]},{"x":699.498735777497,"y":597.4949431099872,"value":[699.498735777497,597.4949431099872]}],"autoDetectionData":{"fgColor":[21,23,24],"bgColor":[255,255,255],"mask":[[200132,7],[201110,10],[202088,11],[203066,11],[204044,13],[205029,6],[206007,6],[206986,5],[207964,5],[208942,5],[209920,5],[210898,6],[211876,8],[212855,9],[213833,10],[214813,10],[215793,9],[216773,8],[217752,8],[218731,8],[219711,7],[220690,6],[221669,6],[222647,7],[223626,7],[224605,8],[225584,8],[226563,7],[227542,6],[228521,6],[229499,7],[230478,7],[231456,8],[232435,9],[233415,8],[234394,10],[235373,12],[236353,10],[237332,9],[238313,7],[239292,6],[240271,6],[241249,6],[242227,6],[243206,5],[244184,6],[245162,7],[246141,6],[247119,7],[248098,8],[249077,9],[250056,9],[251035,9],[252014,11],[252993,12],[253974,10],[254953,9],[255932,9],[256912,8],[257891,7],[258870,7],[259849,6],[260828,6],[261806,7],[262785,6],[263764,6],[264742,7],[265721,8],[266699,8],[267678,8],[268656,8],[269636,7],[270616,6],[271594,7],[272573,7],[273552,6],[274531,6],[275509,7],[276488,7],[277467,7],[278445,7],[279425,6],[280404,7],[281382,8],[282361,8],[283340,8],[284320,7],[285299,9],[286278,8],[287257,8],[288236,8],[289216,6],[290193,7],[291171,7],[292149,9],[293128,8],[294108,7],[295087,8],[296066,7],[297045,7],[298024,6],[299003,5],[299981,5],[300959,6],[301938,5],[302916,6],[303895,5],[304873,6],[305851,6],[306830,6],[307808,6],[308787,5],[309765,6],[310743,7],[311722,7],[312701,8],[313680,7],[314658,8],[315637,9],[316615,12],[317595,10],[318574,10],[319554,8],[320534,7],[321512,7],[322492,6],[323470,6],[324449,6],[325428,5],[326406,5],[327384,6],[328363,6],[329341,6],[330319,7],[331298,6],[332277,6],[333255,6],[334234,6],[335212,6],[336191,5],[337169,6],[338147,6],[339126,5],[340104,5],[341082,6],[342060,8],[343039,7],[344018,8],[344997,8],[345976,7],[346955,7],[347934,7],[348913,8],[349892,8],[350871,7],[351850,6],[352829,6],[353807,6],[354785,6],[355764,5],[356742,5],[357720,5],[358698,5],[359677,5],[360655,5],[361633,6],[362611,6],[363589,6],[364567,7],[365546,7],[366525,7],[367504,6],[368482,7],[369461,8],[370440,7],[371419,6],[372398,6],[373376,7],[374355,6],[375334,6],[376313,6],[377291,7],[378270,6],[379249,7],[380227,7],[381205,7],[382185,5],[383163,5],[384141,6],[385119,8],[386098,7],[387077,7],[388056,7],[389035,7],[390014,6],[390993,5],[391971,5],[392949,5],[393928,5],[394906,6],[395884,6],[396863,6],[397841,7],[398820,7],[399799,8],[400778,7],[401757,6],[402735,6],[403714,5],[404692,5],[405670,5],[406648,5],[407626,5],[408604,5],[409582,7],[410561,6],[411539,7],[412518,6],[413497,3],[414475,3],[417414,3],[418390,5],[419368,5],[420346,5],[421325,4],[422303,4],[423281,4],[424259,4],[425237,4],[426214,5],[427192,5],[428170,5],[429148,5],[430126,5],[431104,5],[432082,5],[433060,4],[434038,4],[435016,4],[435994,4],[436972,4],[437950,4],[438928,4],[439902,8],[440880,8],[441858,8],[442836,8],[443814,8],[444792,8],[445770,8],[446748,8],[447726,8],[448706,6],[449683,6],[450660,7],[451638,6],[452616,6],[453594,6],[454572,6],[455550,5],[456527,6],[457504,7],[458482,6],[459460,6],[460437,6],[461413,8],[462391,7],[463369,6],[464347,6],[465324,6],[466301,7],[467279,6],[468255,7],[469233,7],[470211,6],[471189,6],[472166,6],[473144,6],[474120,7],[475097,8],[476074,9],[477052,8],[478029,9],[479006,10],[479983,9],[480960,10],[481941,7],[482919,7],[483895,1],[483897,7],[484872,9],[485850,8],[486827,9],[487805,9],[488783,9],[489759,10],[490737,9],[491714,9],[492692,8],[493668,10],[494645,11],[495623,10],[496600,10],[497578,10],[498555,11],[499533,9],[500510,9],[501488,9],[502465,9],[503442,9],[504419,9],[505396,9],[506373,9],[507350,9],[508328,8],[509305,8],[510282,9],[511259,11],[512237,11],[513218,7],[514195,7],[515172,8],[516149,9],[517127,9],[518103,11],[519081,10],[520058,11],[521035,10],[522012,10],[522989,10],[523966,10],[524943,11],[525921,11],[526899,9],[527877,8],[528855,5],[529832,5],[530809,6],[531786,7],[532763,7],[533741,6],[534718,6],[535695,6],[536673,6],[537650,6],[538628,6],[539605,6],[540583,6],[541559,7],[542536,8],[543514,7],[544491,7],[545469,6],[546446,6],[547423,6],[548401,6],[549379,5],[550357,5],[551333,7],[552310,7],[553287,7],[554265,6],[555242,6],[556219,7],[557197,6],[558174,7],[559151,7],[560128,8],[561106,7],[562083,7],[563060,7],[564038,7],[565014,8],[565992,8],[566968,9],[567946,9],[568923,9],[569900,9],[570878,9],[571855,9],[572832,10],[573809,11],[574787,10],[575764,11],[576742,10],[577719,11],[578697,11],[579675,6],[579682,3],[580652,6],[581630,6],[582608,5],[583587,3]],"colorDetectionMode":"fg","colorDistance":120,"algorithm":{"algoType":"AveragingWindowAlgo","xStep":10,"yStep":10},"name":10,"imageWidth":978,"imageHeight":692}},{"name":"guide:power=85","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":620.8576923076915,"y":207.71079254079257,"value":[620.8576923076915,207.71079254079257]},{"x":637.5,"y":222.3181818181818,"value":[637.5,222.3181818181818]},{"x":648.5,"y":233.5,"value":[648.5,233.5]},{"x":659,"y":244.45,"value":[659,244.45]},{"x":669,"y":254.7,"value":[669,254.7]},{"x":679,"y":265.08333333333337,"value":[679,265.08333333333337]},{"x":689,"y":276.35,"value":[689,276.35]},{"x":699,"y":287.3,"value":[699,287.3]},{"x":709,"y":298.65,"value":[709,298.65]},{"x":717.5,"y":308.7142857142857,"value":[717.5,308.7142857142857]},{"x":727.7620879120876,"y":325.92857142857144,"value":[727.7620879120876,325.92857142857144]},{"x":735.5,"y":338.2857142857143,"value":[735.5,338.2857142857143]},{"x":742.5,"y":349.42857142857144,"value":[742.5,349.42857142857144]},{"x":749.5,"y":360.5,"value":[749.5,360.5]},{"x":756.5,"y":372.38095238095235,"value":[756.5,372.38095238095235]},{"x":763.5,"y":383,"value":[763.5,383]},{"x":770.5,"y":394.2857142857143,"value":[770.5,394.2857142857143]},{"x":776.5,"y":404.7,"value":[776.5,404.7]},{"x":777.5998735777498,"y":597.2558312262956,"value":[777.5998735777498,597.2558312262956]},{"x":781.5,"y":415.5,"value":[781.5,415.5]},{"x":786.5,"y":426.7382051282051,"value":[786.5,426.7382051282051]},{"x":789.5,"y":581.5,"value":[789.5,581.5]},{"x":791.5,"y":437.3,"value":[791.5,437.3]},{"x":796.5,"y":448.5,"value":[796.5,448.5]},{"x":798,"y":570.125,"value":[798,570.125]},{"x":801,"y":461.125,"value":[801,461.125]},{"x":806,"y":558.5,"value":[806,558.5]},{"x":804.8761538461539,"y":473.6284615384615,"value":[804.8761538461539,473.6284615384615]},{"x":808.8761538461539,"y":486.23484848484844,"value":[808.8761538461539,486.23484848484844]},{"x":813.6666666666666,"y":547.0833333333334,"value":[813.6666666666666,547.0833333333334]},{"x":812.7430769230766,"y":498.49538461538464,"value":[812.7430769230766,498.49538461538464]},{"x":817.5,"y":510.8,"value":[817.5,510.8]},{"x":818.9444444444445,"y":536.3333333333334,"value":[818.9444444444445,536.3333333333334]},{"x":819.5,"y":522.7222222222222,"value":[819.5,522.7222222222222]}],"autoDetectionData":{"fgColor":[21,23,24],"bgColor":[255,255,255],"mask":[[201111,3],[202089,4],[203067,6],[204045,7],[205022,8],[206002,6],[206980,6],[207959,6],[208938,5],[209916,7],[210894,11],[211873,13],[212852,14],[213832,12],[214812,11],[215793,9],[216774,7],[217753,7],[218732,7],[219711,6],[220689,7],[221668,6],[222647,5],[223626,5],[224604,7],[225582,9],[226560,9],[227540,8],[228519,8],[229498,8],[230476,9],[231456,8],[232435,7],[233414,6],[234393,5],[235371,6],[236350,6],[237328,7],[238307,6],[239286,7],[240265,6],[241243,7],[242222,8],[243201,7],[244180,7],[245158,9],[246137,10],[247117,11],[248095,12],[249074,12],[250053,12],[251035,9],[252016,8],[252995,8],[253974,7],[254953,6],[255932,6],[256910,7],[257889,6],[258867,7],[259846,7],[260825,8],[261804,8],[262783,8],[263762,7],[264741,7],[265720,8],[266699,8],[267677,9],[268656,9],[269636,8],[270615,7],[271594,7],[272573,7],[273552,7],[274531,7],[275510,6],[276489,6],[277467,8],[278446,8],[279425,9],[280404,9],[281384,9],[282363,8],[283343,6],[284322,5],[285300,5],[286278,6],[287257,6],[288235,7],[289214,7],[290193,7],[291172,7],[292151,7],[293130,7],[294109,7],[295088,8],[296067,7],[297046,6],[298024,7],[299003,6],[299982,6],[300961,6],[301939,6],[302918,7],[303897,7],[304875,8],[305854,8],[306833,9],[307813,9],[308792,9],[309771,8],[310751,6],[311731,4],[312709,4],[313687,4],[314665,4],[315643,4],[316621,4],[317599,4],[318577,4],[319555,4],[320533,5],[321511,6],[322489,7],[323467,7],[324445,7],[325425,6],[326404,6],[327382,7],[328361,7],[329340,7],[330319,6],[331298,5],[332276,5],[333254,6],[334233,5],[335211,5],[336190,5],[337168,6],[338146,6],[339124,7],[340103,7],[341082,6],[342061,6],[343040,6],[344018,7],[344997,6],[345976,6],[346954,7],[347933,6],[348912,5],[349891,4],[350869,4],[351847,5],[352825,5],[353803,6],[354782,6],[355760,6],[356739,6],[357718,6],[358696,7],[359675,7],[360654,6],[361633,6],[362611,7],[363590,7],[364569,7],[365548,6],[366526,6],[367504,7],[368483,6],[369462,6],[370440,6],[371419,5],[372397,5],[373375,6],[374354,6],[375332,6],[376311,6],[377290,5],[378268,7],[379247,6],[380225,7],[381204,7],[382183,8],[383162,7],[384140,8],[385120,7],[386098,8],[387077,8],[388057,6],[389036,6],[390014,6],[390993,6],[391972,5],[392950,6],[393929,6],[394907,6],[395886,5],[396864,6],[397843,5],[398821,6],[399800,6],[400778,7],[401757,7],[402736,6],[403715,6],[404693,7],[405671,7],[406651,5],[407629,5],[408608,4],[409586,4],[410564,4],[411542,5],[412520,5],[413498,6],[414477,5],[415455,5],[416433,6],[417412,6],[418390,6],[419369,5],[420347,5],[421325,5],[422303,5],[423281,6],[424260,6],[425238,6],[426217,6],[427196,5],[428174,6],[429153,5],[430131,5],[431109,6],[432088,6],[433066,6],[434045,5],[435023,5],[436001,6],[436980,5],[437958,6],[438936,6],[439915,5],[440893,5],[441872,5],[442850,6],[443828,6],[444806,6],[445784,6],[446762,7],[447741,6],[448720,5],[449699,5],[450677,5],[451655,5],[452633,6],[453611,6],[454590,5],[455568,6],[456547,5],[457525,5],[458504,5],[459482,7],[460460,7],[461438,7],[462417,7],[463396,7],[464374,8],[465352,8],[466330,8],[467308,9],[468287,9],[469265,9],[470243,9],[471221,10],[472199,10],[473177,11],[474155,11],[475133,12],[476112,11],[477090,11],[478069,10],[479047,10],[480025,13],[481003,13],[481981,14],[482959,14],[483937,14],[484916,14],[485894,14],[486872,14],[487851,14],[488829,7],[488838,5],[489808,7],[489816,5],[490787,6],[490794,6],[491766,12],[492744,6],[492751,5],[493722,6],[493729,5],[494700,6],[494708,4],[495680,4],[495686,4],[496658,5],[496664,5],[497636,5],[497642,5],[498614,5],[498620,5],[499592,11],[500571,10],[501549,10],[502528,9],[503506,9],[504484,9],[505463,8],[506441,8],[507419,8],[508397,8],[509376,1],[509378,5],[510356,5],[511335,5],[512313,5],[513290,7],[514268,8],[515246,8],[516224,9],[517202,9],[518180,9],[519158,9],[520136,9],[521114,7],[522092,7],[523070,7],[524047,8],[525024,8],[526001,7],[526979,7],[527957,7],[528935,6],[529913,5],[530890,6],[531866,8],[532844,8],[533822,7],[534800,6],[535777,6],[536754,6],[537732,6],[538709,7],[539686,7],[540663,7],[541640,7],[542618,6],[543596,5],[544573,6],[545550,7],[546528,6],[547505,6],[548482,7],[549459,7],[550436,7],[551414,7],[552391,7],[553368,7],[554344,8],[555321,9],[556297,9],[557275,8],[558253,7],[559231,6],[560209,6],[561186,6],[562163,7],[563140,7],[564117,7],[565095,6],[566072,6],[567050,5],[568027,6],[569005,5],[569982,6],[570959,7],[571934,10],[572911,10],[573889,10],[574867,7],[575845,6],[576823,5],[577800,6],[578778,6],[579755,7],[580731,8],[581708,8],[582686,7],[583665,5],[584643,4]],"colorDetectionMode":"fg","colorDistance":120,"algorithm":{"algoType":"AveragingWindowAlgo","xStep":10,"yStep":10},"name":11,"imageWidth":978,"imageHeight":692}},{"name":"panel:densityAltitudeCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":79.61418490919128,"y":61.28856762524066,"value":[79.61418490919128,61.28856762524066]},{"x":418.44492911668476,"y":634.6477644492903,"value":[418.44492911668476,634.6477644492903]}],"autoDetectionData":null},{"name":"panel:trueAirspeedCalculation","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":416.39428507478766,"y":205.96449355116653,"value":[416.39428507478766,205.96449355116653]},{"x":871.1165199186828,"y":598.9159076834013,"value":[871.1165199186828,598.9159076834013]}],"autoDetectionData":null},{"name":"guide:densityAltitudeCorrection@12000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":81.31046511627908,"y":93.25116279069768,"value":[81.31046511627908,93.25116279069768]},{"x":416.7872093023256,"y":263.8325581395349,"value":[416.7872093023256,263.8325581395349]}],"autoDetectionData":null},{"name":"guide:densityAltitudeCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":81.31046511627908,"y":426.45348837209303,"value":[81.31046511627908,426.45348837209303]},{"x":417.071511627907,"y":597.0348837209302,"value":[417.071511627907,597.0348837209302]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=14000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.54545454545459,"y":63,"value":[78.54545454545459,63]},{"x":420.54545454545445,"y":63,"value":[420.54545454545445,63]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=12000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.54545454545463,"y":131.99999999999997,"value":[78.54545454545463,131.99999999999997]},{"x":420.54545454545445,"y":131.99999999999997,"value":[420.54545454545445,131.99999999999997]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":420.2727272727272,"y":200.72727272727275,"value":[420.2727272727272,200.72727272727275]},{"x":78.54545454545459,"y":200.72727272727275,"value":[78.54545454545459,200.72727272727275]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.27272727272731,"y":269.99999999999994,"value":[78.27272727272731,269.99999999999994]},{"x":420.27272727272725,"y":270,"value":[420.27272727272725,270]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":420.2727272727272,"y":337.9090909090909,"value":[420.2727272727272,337.9090909090909]},{"x":78.00000000000001,"y":337.90909090909065,"value":[78.00000000000001,337.90909090909065]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.27272727272734,"y":408,"value":[78.27272727272734,408]},{"x":420.27272727272714,"y":408,"value":[420.27272727272714,408]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.00000000000006,"y":476.99999999999994,"value":[78.00000000000006,476.99999999999994]},{"x":420.5454545454545,"y":476.9999999999993,"value":[420.5454545454545,476.9999999999993]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":420.8181818181816,"y":546.0000000000001,"value":[420.8181818181816,546.0000000000001]},{"x":78.00000000000004,"y":546.0000000000001,"value":[78.00000000000004,546.0000000000001]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=-2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":78.00000000000006,"y":614.9999999999999,"value":[78.00000000000006,614.9999999999999]},{"x":420.8181818181817,"y":615,"value":[420.8181818181817,615]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":417,"y":204.27272727272722,"value":[417,204.27272727272722]},{"x":416.99999999999994,"y":611.1818181818173,"value":[416.99999999999994,611.1818181818173]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=105","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":467.1818181818182,"y":610.6363636363637,"value":[467.1818181818182,610.6363636363637]},{"x":467.18181818181813,"y":204.27272727272728,"value":[467.18181818181813,204.27272727272728]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=110","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":517.0909090909091,"y":611.4545454545446,"value":[517.0909090909091,611.4545454545446]},{"x":517.090909090909,"y":203.99999999999994,"value":[517.090909090909,203.99999999999994]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=115","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":568.0909090909091,"y":611.1818181818185,"value":[568.0909090909091,611.1818181818185]},{"x":568.090909090909,"y":203.99999999999994,"value":[568.090909090909,203.99999999999994]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=120","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":618,"y":611.4545454545456,"value":[618,611.4545454545456]},{"x":617.9999999999999,"y":204.27272727272725,"value":[617.9999999999999,204.27272727272725]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=125","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":669,"y":204.27272727272722,"value":[669,204.27272727272722]},{"x":669,"y":611.7272727272729,"value":[669,611.7272727272729]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=130","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":718.090909090909,"y":204.27272727272725,"value":[718.090909090909,204.27272727272725]},{"x":718.090909090909,"y":612.0000000000001,"value":[718.090909090909,612.0000000000001]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=135","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":769.0909090909091,"y":611.1818181818185,"value":[769.0909090909091,611.1818181818185]},{"x":769.0909090909091,"y":204.2727272727273,"value":[769.0909090909091,204.2727272727273]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=140","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":819,"y":611.1818181818184,"value":[819,611.1818181818184]},{"x":819,"y":203.99999999999994,"value":[819,203.99999999999994]}],"autoDetectionData":null},{"name":"guide:trueAirspeed=145","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":870.0000000000001,"y":610.3636363636355,"value":[870.0000000000001,610.3636363636355]},{"x":870,"y":204.5454545454545,"value":[870,204.5454545454545]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":81.45382436260624,"y":642.4878186968839,"value":[81.45382436260624,642.4878186968839]},{"x":81.45382436260624,"y":53.74844192634563,"value":[81.45382436260624,53.74844192634563]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":148.50084985835696,"y":53.74844192634561,"value":[148.50084985835696,53.74844192634561]},{"x":148.50084985835696,"y":642.4878186968839,"value":[148.50084985835696,642.4878186968839]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":215.54787535410765,"y":53.74844192634561,"value":[215.54787535410765,53.74844192634561]},{"x":215.54787535410765,"y":642.4878186968839,"value":[215.54787535410765,642.4878186968839]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":282.5949008498584,"y":53.74844192634561,"value":[282.5949008498584,53.74844192634561]},{"x":282.5949008498584,"y":642.4878186968839,"value":[282.5949008498584,642.4878186968839]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":349.36487252124647,"y":53.74844192634561,"value":[349.36487252124647,53.74844192634561]},{"x":349.36487252124647,"y":642.4878186968839,"value":[349.36487252124647,642.4878186968839]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=30","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":416.9660056657224,"y":53.74844192634563,"value":[416.9660056657224,53.74844192634563]},{"x":416.9660056657224,"y":642.4878186968839,"value":[416.9660056657224,642.4878186968839]}],"autoDetectionData":null},{"name":"guide:densityAltitude@14000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.89947105282226,"y":207.72649807984928,"value":[413.89947105282226,207.72649807984928]},{"x":872.6642779508728,"y":208.35240924570684,"value":[872.6642779508728,208.35240924570684]}],"autoDetectionData":null},{"name":"guide:densityAltitude@12000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":414.13137453807695,"y":264.1622201289761,"value":[414.13137453807695,264.1622201289761]},{"x":873.1280849213823,"y":264.09242083906963,"value":[873.1280849213823,264.09242083906963]}],"autoDetectionData":null},{"name":"guide:densityAltitude@10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.89947105282226,"y":319.2065212665749,"value":[413.89947105282226,319.2065212665749]},{"x":873.1280849213823,"y":319.13672197666835,"value":[873.1280849213823,319.13672197666835]}],"autoDetectionData":null},{"name":"guide:densityAltitude@8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.59776827766103,"y":375.34054054054053,"value":[413.59776827766103,375.34054054054053]},{"x":872.8961814361276,"y":375.34054054054053,"value":[872.8961814361276,375.34054054054053]}],"autoDetectionData":null},{"name":"guide:densityAltitude@6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.89947105282226,"y":431.312455619158,"value":[413.89947105282226,431.312455619158]},{"x":873.4297876965439,"y":431.2426563292515,"value":[873.4297876965439,431.2426563292515]}],"autoDetectionData":null},{"name":"guide:densityAltitude@4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.6675675675676,"y":486.12485327150205,"value":[413.6675675675676,486.12485327150205]},{"x":872.502173755525,"y":486.12485327150205,"value":[872.502173755525,486.12485327150205]}],"autoDetectionData":null},{"name":"guide:densityAltitude@2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.8296717629157,"y":541.1691544091005,"value":[413.8296717629157,541.1691544091005]},{"x":873.0582856314755,"y":541.1691544091005,"value":[873.0582856314755,541.1691544091005]}],"autoDetectionData":null},{"name":"guide:densityAltitude@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":413.36586479240634,"y":597.1759691326716,"value":[413.36586479240634,597.1759691326716]},{"x":873.337482791103,"y":597.1410694877181,"value":[873.337482791103,597.1410694877181]}],"autoDetectionData":null}],"measurementColl":[]} \ No newline at end of file diff --git a/__tests__/chart/chase/wpd/da40-landing-distance-flaps-up.wpd.json b/__tests__/chart/chase/wpd/da40-landing-distance-flaps-up.wpd.json new file mode 100644 index 0000000..08a0493 --- /dev/null +++ b/__tests__/chart/chase/wpd/da40-landing-distance-flaps-up.wpd.json @@ -0,0 +1 @@ +{"version":[4,2],"axesColl":[{"name":"Image","type":"ImageAxes"}],"datasetColl":[{"name":"Default Dataset","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[],"autoDetectionData":null},{"name":"panel:densityAltitudeCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":31.007580978635414,"value":[67.11509303928325,31.007580978635414]},{"x":352.50723638869744,"y":313.135768435561,"value":[352.50723638869744,313.135768435561]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":124.84631288766369,"value":[67.11509303928325,124.84631288766369]},{"x":134.2301860785665,"y":87.31082012405238,"value":[134.2301860785665,87.31082012405238]},{"x":162.78980013783598,"y":69.15506547208821,"value":[162.78980013783598,69.15506547208821]},{"x":207.87319090282563,"y":31.007580978635428,"value":[207.87319090282563,31.007580978635428]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":156.4658855961406,"value":[67.11509303928325,156.4658855961406]},{"x":139.9421088904204,"y":126.07029634734666,"value":[139.9421088904204,126.07029634734666]},{"x":175.02963473466573,"y":108.118538938663,"value":[175.02963473466573,108.118538938663]},{"x":205.01722949689866,"y":92.61474844934529,"value":[205.01722949689866,92.61474844934529]},{"x":240.71674707098552,"y":69.35906271536871,"value":[240.71674707098552,69.35906271536871]},{"x":286.61612680909707,"y":31.007580978635428,"value":[286.61612680909707,31.007580978635428]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":183.1895244658856,"value":[67.11509303928325,183.1895244658856]},{"x":143.61405926946932,"y":158.09786354238457,"value":[143.61405926946932,158.09786354238457]},{"x":220.1130254996554,"y":127.29427980702964,"value":[220.1130254996554,127.29427980702964]},{"x":296.4079944865608,"y":89.75878704341834,"value":[296.4079944865608,89.75878704341834]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":204.2012405237767,"value":[67.11509303928325,204.2012405237767]},{"x":143.61405926946932,"y":183.3935217091661,"value":[143.61405926946932,183.3935217091661]},{"x":181.55754651964162,"y":171.3576843556168,"value":[181.55754651964162,171.3576843556168]},{"x":254.18056512749828,"y":147.49000689179877,"value":[254.18056512749828,147.49000689179877]},{"x":296.4079944865609,"y":131.98621640248103,"value":[296.4079944865609,131.98621640248103]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":220.7250172294969,"value":[67.11509303928325,220.7250172294969]},{"x":143.20606478290833,"y":204.4052377670572,"value":[143.20606478290833,204.4052377670572]},{"x":181.55754651964162,"y":194.81736733287389,"value":[181.55754651964162,194.81736733287389]},{"x":220.1130254996554,"y":184.41350792556858,"value":[220.1130254996554,184.41350792556858]},{"x":258.4645072363887,"y":173.80565127498275,"value":[258.4645072363887,173.80565127498275]},{"x":296.4079944865609,"y":162.1778084079945,"value":[296.4079944865609,162.1778084079945]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":235.20882150241212,"value":[67.11509303928325,235.20882150241212]},{"x":143.61405926946932,"y":221.74500344589939,"value":[143.61405926946932,221.74500344589939]},{"x":219.9090282563749,"y":204.6092350103377,"value":[219.9090282563749,204.6092350103377]},{"x":258.4645072363887,"y":195.83735354927634,"value":[258.4645072363887,195.83735354927634]},{"x":296.407994486561,"y":186.04548587181253,"value":[296.407994486561,186.04548587181253]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":67.11509303928325,"y":313.1357684355615,"value":[67.11509303928325,313.1357684355615]},{"x":67.11509303928325,"y":31.007580978635428,"value":[67.11509303928325,31.007580978635428]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":105.46657477601654,"y":313.1357684355614,"value":[105.46657477601654,313.1357684355614]},{"x":105.46657477601654,"y":31.00758097863545,"value":[105.46657477601654,31.00758097863545]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":143.41006202618883,"y":313.1357684355611,"value":[143.41006202618883,313.1357684355611]},{"x":143.41006202618883,"y":31.007580978635435,"value":[143.41006202618883,31.007580978635435]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":181.55754651964162,"y":313.1357684355615,"value":[181.55754651964162,313.1357684355615]},{"x":181.55754651964162,"y":31.00758097863544,"value":[181.55754651964162,31.00758097863544]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":220.1130254996554,"y":313.1357684355615,"value":[220.1130254996554,313.1357684355615]},{"x":220.1130254996554,"y":31.00758097863543,"value":[220.1130254996554,31.00758097863543]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=30","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":258.46450723638867,"y":313.1357684355615,"value":[258.46450723638867,313.1357684355615]},{"x":258.4645072363886,"y":31.007580978635428,"value":[258.4645072363886,31.007580978635428]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=40","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":296.407994486561,"y":313.1357684355615,"value":[296.407994486561,313.1357684355615]},{"x":296.407994486561,"y":31.007580978635428,"value":[296.407994486561,31.007580978635428]}],"autoDetectionData":null},{"name":"panel:weightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.5072363886974,"y":31.007580978635424,"value":[352.5072363886974,31.007580978635424]},{"x":645.44727773949,"y":456.5458304617504,"value":[645.44727773949,456.5458304617504]}],"autoDetectionData":null},{"name":"guide:weightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.50723638869744,"y":36.71950379048932,"value":[352.50723638869744,36.71950379048932]},{"x":467.56168159889734,"y":132.39421088904203,"value":[467.56168159889734,132.39421088904203]},{"x":582.4121295658166,"y":189.71743625086148,"value":[582.4121295658166,189.71743625086148]}],"autoDetectionData":null},{"name":"guide:weightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.50723638869744,"y":94.45072363886975,"value":[352.50723638869744,94.45072363886975]},{"x":467.5616815988974,"y":165.03376981392142,"value":[467.5616815988974,165.03376981392142]},{"x":582.4121295658166,"y":208.68917987594762,"value":[582.4121295658166,208.68917987594762]}],"autoDetectionData":null},{"name":"guide:weightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.5072363886973,"y":135.250172294969,"value":[352.5072363886973,135.250172294969]},{"x":467.5616815988972,"y":189.71743625086148,"value":[467.5616815988972,189.71743625086148]},{"x":582.4121295658166,"y":228.27291523087524,"value":[582.4121295658166,228.27291523087524]}],"autoDetectionData":null},{"name":"guide:weightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.5072363886974,"y":165.03376981392142,"value":[352.5072363886974,165.03376981392142]},{"x":467.5616815988972,"y":208.48518263266712,"value":[467.5616815988972,208.48518263266712]},{"x":582.4121295658166,"y":244.79669193659544,"value":[582.4121295658166,244.79669193659544]}],"autoDetectionData":null},{"name":"guide:weightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.5072363886974,"y":189.71743625086148,"value":[352.5072363886974,189.71743625086148]},{"x":467.5616815988973,"y":226.2329427980703,"value":[467.5616815988973,226.2329427980703]},{"x":582.4121295658167,"y":260.0964851826327,"value":[582.4121295658167,260.0964851826327]}],"autoDetectionData":null},{"name":"guide:weightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.5072363886973,"y":208.68917987594762,"value":[352.5072363886973,208.68917987594762]},{"x":467.5616815988972,"y":243.1647139903515,"value":[467.5616815988972,243.1647139903515]},{"x":582.4121295658166,"y":273.7643004824258,"value":[582.4121295658166,273.7643004824258]}],"autoDetectionData":null},{"name":"guide:weight=1150","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":352.50723638869744,"y":476.12956581667805,"value":[352.50723638869744,476.12956581667805]},{"x":352.5072363886973,"y":31.007580978635435,"value":[352.5072363886973,31.007580978635435]}],"autoDetectionData":null},{"name":"guide:weight=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":467.5616815988972,"y":476.1295658166779,"value":[467.5616815988972,476.1295658166779]},{"x":467.5616815988972,"y":31.007580978635435,"value":[467.5616815988972,31.007580978635435]}],"autoDetectionData":null},{"name":"guide:weight=850","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":582.4121295658166,"y":476.129565816678,"value":[582.4121295658166,476.129565816678]},{"x":582.4121295658166,"y":31.007580978635435,"value":[582.4121295658166,31.007580978635435]}],"autoDetectionData":null},{"name":"panel:windComponentCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.44727773949,"y":31.00758097863546,"value":[645.44727773949,31.00758097863546]},{"x":838.6326671261198,"y":456.54583046175037,"value":[838.6326671261198,456.54583046175037]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394899,"y":37.331495520330805,"value":[645.4472777394899,37.331495520330805]},{"x":721.946243969676,"y":170.94968986905582,"value":[721.946243969676,170.94968986905582]},{"x":798.4452101998622,"y":244.59269469331494,"value":[798.4452101998622,244.59269469331494]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394899,"y":95.06271536871122,"value":[645.4472777394899,95.06271536871122]},{"x":721.9462439696761,"y":198.8973121984838,"value":[721.9462439696761,198.8973121984838]},{"x":798.445210199862,"y":264.9924190213646,"value":[798.445210199862,264.9924190213646]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394899,"y":135.250172294969,"value":[645.4472777394899,135.250172294969]},{"x":721.9462439696761,"y":220.1130254996554,"value":[721.9462439696761,220.1130254996554]},{"x":798.4452101998621,"y":283.5561681598897,"value":[798.4452101998621,283.5561681598897]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.44727773949,"y":165.23776705720192,"value":[645.44727773949,165.23776705720192]},{"x":721.946243969676,"y":239.90075809786353,"value":[721.946243969676,239.90075809786353]},{"x":760.0937284631289,"y":271.31633356305997,"value":[760.0937284631289,271.31633356305997]},{"x":798.4452101998621,"y":300.69193659545135,"value":[798.4452101998621,300.69193659545135]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.44727773949,"y":208.8931771192281,"value":[645.44727773949,208.8931771192281]},{"x":721.9462439696761,"y":273.7643004824258,"value":[721.9462439696761,273.7643004824258]},{"x":798.4452101998621,"y":328.2315644383183,"value":[798.4452101998621,328.2315644383183]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394899,"y":243.1647139903515,"value":[645.4472777394899,243.1647139903515]},{"x":721.946243969676,"y":302.32391454169533,"value":[721.946243969676,302.32391454169533]},{"x":798.4452101998622,"y":352.303239145417,"value":[798.4452101998622,352.303239145417]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394897,"y":273.764300482426,"value":[645.4472777394897,273.764300482426]},{"x":721.946243969676,"y":328.63955892487934,"value":[721.946243969676,328.63955892487934]},{"x":798.4452101998622,"y":373.51895244658846,"value":[798.4452101998622,373.51895244658846]}],"autoDetectionData":null},{"name":"guide:windComponent=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":645.4472777394899,"y":475.313576843556,"value":[645.4472777394899,475.313576843556]},{"x":645.44727773949,"y":31.00758097863544,"value":[645.44727773949,31.00758097863544]}],"autoDetectionData":null},{"name":"guide:windComponent=5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":683.3907649896622,"y":475.31357684355515,"value":[683.3907649896622,475.31357684355515]},{"x":683.3907649896623,"y":31.00758097863544,"value":[683.3907649896623,31.00758097863544]}],"autoDetectionData":null},{"name":"guide:windComponent=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":721.9462439696761,"y":475.31357684355595,"value":[721.9462439696761,475.31357684355595]},{"x":721.9462439696761,"y":31.007580978635435,"value":[721.9462439696761,31.007580978635435]}],"autoDetectionData":null},{"name":"guide:windComponent=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":760.5017229496898,"y":475.313576843556,"value":[760.5017229496898,475.313576843556]},{"x":760.5017229496898,"y":31.00758097863543,"value":[760.5017229496898,31.00758097863543]}],"autoDetectionData":null},{"name":"guide:windComponent=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":798.4452101998621,"y":475.3135768435559,"value":[798.4452101998621,475.3135768435559]},{"x":798.4452101998623,"y":31.0075809786354,"value":[798.4452101998623,31.0075809786354]}],"autoDetectionData":null},{"name":"panel:obstacleHeightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":31.007580978635428,"value":[838.4286698828394,31.007580978635428]},{"x":1035.082012405238,"y":456.5458304617507,"value":[1035.082012405238,456.5458304617507]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":37.33149552033081,"value":[838.4286698828394,37.33149552033081]},{"x":953.6871123363197,"y":321.4996554100621,"value":[953.6871123363197,321.4996554100621]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":208.68917987594762,"value":[838.4286698828394,208.68917987594762]},{"x":953.6871123363197,"y":365.3590627153687,"value":[953.6871123363197,365.3590627153687]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828384,"y":242.75671950379066,"value":[838.4286698828384,242.75671950379066]},{"x":953.6871123363196,"y":369.64300482425847,"value":[953.6871123363196,369.64300482425847]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":273.96829772570686,"value":[838.4286698828394,273.96829772570686]},{"x":953.6871123363197,"y":377.59889731219744,"value":[953.6871123363197,377.59889731219744]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":302.32391454169544,"value":[838.4286698828394,302.32391454169544]},{"x":953.6871123363196,"y":385.35079255685724,"value":[953.6871123363196,385.35079255685724]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828392,"y":328.0275671950379,"value":[838.4286698828392,328.0275671950379]},{"x":953.6871123363196,"y":393.9186767746379,"value":[953.6871123363196,393.9186767746379]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":352.3032391454169,"value":[838.4286698828394,352.3032391454169]},{"x":953.6871123363197,"y":412.2784286698827,"value":[953.6871123363197,412.2784286698827]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@7","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":373.7229496898689,"value":[838.4286698828394,373.7229496898689]},{"x":953.6871123363196,"y":425.130254996554,"value":[953.6871123363196,425.130254996554]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":838.4286698828394,"y":468.98966230186073,"value":[838.4286698828394,468.98966230186073]},{"x":838.4286698828394,"y":31.00758097863543,"value":[838.4286698828394,31.00758097863543]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":953.6871123363197,"y":468.9896623018606,"value":[953.6871123363197,468.9896623018606]},{"x":953.6871123363197,"y":31.007580978635428,"value":[953.6871123363197,31.007580978635428]}],"autoDetectionData":null},{"name":"panel:landingDistance","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.082012405238,"y":31.007580978635424,"value":[1035.082012405238,31.007580978635424]},{"x":1057.1137146795324,"y":456.5458304617504,"value":[1057.1137146795324,456.5458304617504]}],"autoDetectionData":null},{"name":"guide:landingDistance=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052382,"y":55.48725017229497,"value":[1035.0820124052382,55.48725017229497]},{"x":1057.1137146795324,"y":55.48725017229497,"value":[1057.1137146795324,55.48725017229497]}],"autoDetectionData":null},{"name":"guide:landingDistance=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052375,"y":117.50241212956581,"value":[1035.0820124052375,117.50241212956581]},{"x":1057.1137146795318,"y":117.50241212956581,"value":[1057.1137146795318,117.50241212956581]}],"autoDetectionData":null},{"name":"guide:landingDistance=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052389,"y":164.42177808407996,"value":[1035.0820124052389,164.42177808407996]},{"x":1057.1137146795331,"y":164.42177808407996,"value":[1057.1137146795331,164.42177808407996]}],"autoDetectionData":null},{"name":"guide:landingDistance=800","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052361,"y":200.52929014472775,"value":[1035.0820124052361,200.52929014472775]},{"x":1057.1137146795331,"y":200.52929014472775,"value":[1057.1137146795331,200.52929014472775]}],"autoDetectionData":null},{"name":"guide:landingDistance=700","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052382,"y":233.57684355616814,"value":[1035.0820124052382,233.57684355616814]},{"x":1057.113714679532,"y":233.57684355616814,"value":[1057.113714679532,233.57684355616814]}],"autoDetectionData":null},{"name":"guide:landingDistance=600","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052386,"y":266.012405237767,"value":[1035.0820124052386,266.012405237767]},{"x":1057.1137146795327,"y":266.01240523776704,"value":[1057.1137146795327,266.01240523776704]}],"autoDetectionData":null},{"name":"guide:landingDistance=500","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.082012405236,"y":300.4879393521708,"value":[1035.082012405236,300.4879393521708]},{"x":1057.1137146795324,"y":300.4879393521708,"value":[1057.1137146795324,300.4879393521708]}],"autoDetectionData":null},{"name":"guide:landingDistance=400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052393,"y":335.57546519641625,"value":[1035.0820124052393,335.57546519641625]},{"x":1057.1137146795315,"y":335.5754651964163,"value":[1057.1137146795315,335.5754651964163]}],"autoDetectionData":null},{"name":"guide:landingDistance=300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052382,"y":366.58304617505127,"value":[1035.0820124052382,366.58304617505127]},{"x":1057.113714679532,"y":366.58304617505127,"value":[1057.113714679532,366.58304617505127]}],"autoDetectionData":null},{"name":"guide:landingDistance=200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.082012405238,"y":397.38662991040655,"value":[1035.082012405238,397.38662991040655]},{"x":1057.1137146795324,"y":397.3866299104066,"value":[1057.1137146795324,397.3866299104066]}],"autoDetectionData":null},{"name":"guide:landingDistance=100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.0820124052382,"y":427.57822191592,"value":[1035.0820124052382,427.57822191592]},{"x":1057.1137146795324,"y":427.57822191592004,"value":[1057.1137146795324,427.57822191592004]}],"autoDetectionData":null},{"name":"guide:landingDistance=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1035.08201240524,"y":456.54583046175054,"value":[1035.08201240524,456.54583046175054]},{"x":1057.113714679532,"y":456.54583046175037,"value":[1057.113714679532,456.54583046175037]}],"autoDetectionData":null}],"measurementColl":[]} \ No newline at end of file diff --git a/__tests__/chart/chase/wpd/da40-landing-distance.wpd.json b/__tests__/chart/chase/wpd/da40-landing-distance.wpd.json new file mode 100644 index 0000000..e28e821 --- /dev/null +++ b/__tests__/chart/chase/wpd/da40-landing-distance.wpd.json @@ -0,0 +1 @@ +{"version":[4,2],"axesColl":[{"name":"Image","type":"ImageAxes"}],"datasetColl":[{"name":"Default Dataset","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[],"autoDetectionData":null},{"name":"panel:densityAltitudeCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.08060453400504,"y":87.56738035264482,"value":[13.08060453400504,87.56738035264482]},{"x":548.295340050378,"y":603.1612090680104,"value":[548.295340050378,603.1612090680104]}],"autoDetectionData":null},{"name":"panel:weightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.2953400503781,"y":87.5673803526448,"value":[548.2953400503781,87.5673803526448]},{"x":738.3274559193956,"y":603.1612090680104,"value":[738.3274559193956,603.1612090680104]}],"autoDetectionData":null},{"name":"panel:windComponentCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":87.56738035264483,"value":[738.3274559193954,87.56738035264483]},{"x":928.3595717884133,"y":603.1612090680102,"value":[928.3595717884133,603.1612090680102]}],"autoDetectionData":null},{"name":"panel:obstacleHeightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884132,"y":87.56738035264485,"value":[928.3595717884132,87.56738035264485]},{"x":1070.7928211586902,"y":603.1612090680101,"value":[1070.7928211586902,603.1612090680101]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.08060453400504,"y":627.5056675062979,"value":[13.08060453400504,627.5056675062979]},{"x":13.08060453400504,"y":63.58627204030232,"value":[13.08060453400504,63.58627204030232]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":86.47732997481117,"y":627.5056675062983,"value":[86.47732997481117,627.5056675062983]},{"x":86.47732997481071,"y":63.58627204030262,"value":[86.47732997481071,63.58627204030262]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":159.14735516372795,"y":627.5056675062978,"value":[159.14735516372795,627.5056675062978]},{"x":159.14735516372744,"y":63.58627204030232,"value":[159.14735516372744,63.58627204030232]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":232.544080604534,"y":627.5056675062979,"value":[232.544080604534,627.5056675062979]},{"x":232.544080604534,"y":63.586272040302305,"value":[232.544080604534,63.586272040302305]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":305.21410579345087,"y":627.5056675062979,"value":[305.21410579345087,627.5056675062979]},{"x":305.21410579345087,"y":63.586272040302305,"value":[305.21410579345087,63.586272040302305]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=30","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":378.24748110831234,"y":627.5056675062978,"value":[378.24748110831234,627.5056675062978]},{"x":378.24748110831234,"y":63.58627204030236,"value":[378.24748110831234,63.58627204030236]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=40","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":451.2808564231738,"y":627.5056675062979,"value":[451.2808564231738,627.5056675062979]},{"x":451.2808564231738,"y":63.58627204030233,"value":[451.2808564231738,63.58627204030233]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=50","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":524.6775818639799,"y":627.5056675062979,"value":[524.6775818639799,627.5056675062979]},{"x":524.67758186398,"y":63.58627204030229,"value":[524.67758186398,63.58627204030229]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.08060453400504,"y":416.7625944584383,"value":[13.08060453400504,416.7625944584383]},{"x":305.21410579345087,"y":383.6977329974811,"value":[305.21410579345087,383.6977329974811]},{"x":524.6775818639794,"y":358.98992443324937,"value":[524.6775818639794,358.98992443324937]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.08060453400504,"y":401.50188916876573,"value":[13.08060453400504,401.50188916876573]},{"x":159.14735516372798,"y":384.4244332493703,"value":[159.14735516372798,384.4244332493703]},{"x":305.9408060453401,"y":364.0768261964736,"value":[305.9408060453401,364.0768261964736]},{"x":413.492443324937,"y":353.9030226700252,"value":[413.492443324937,353.9030226700252]},{"x":524.6775818639799,"y":335.37216624685135,"value":[524.6775818639799,335.37216624685135]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.080604534005039,"y":387.33123425692696,"value":[13.080604534005039,387.33123425692696]},{"x":160.6007556675063,"y":363.3501259445844,"value":[160.6007556675063,363.3501259445844]},{"x":305.9408060453401,"y":345.1826196473552,"value":[305.9408060453401,345.1826196473552]},{"x":524.67758186398,"y":314.2978589420655,"value":[524.67758186398,314.2978589420655]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.08060453400504,"y":365.53022670025183,"value":[13.08060453400504,365.53022670025183]},{"x":158.4206549118388,"y":342.2758186397985,"value":[158.4206549118388,342.2758186397985]},{"x":305.21410579345087,"y":322.65491183879095,"value":[305.21410579345087,322.65491183879095]},{"x":524.67758186398,"y":289.59005037783373,"value":[524.67758186398,289.59005037783373]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.080604534005039,"y":345.54596977329976,"value":[13.080604534005039,345.54596977329976]},{"x":159.51070528967253,"y":317.9313602015114,"value":[159.51070528967253,317.9313602015114]},{"x":305.57745591939545,"y":295.4036523929471,"value":[305.57745591939545,295.4036523929471]},{"x":414.58249370277076,"y":270.33249370277076,"value":[414.58249370277076,270.33249370277076]},{"x":524.6775818639798,"y":240.17443324937025,"value":[524.6775818639798,240.17443324937025]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.080604534005037,"y":323.3816120906801,"value":[13.080604534005037,323.3816120906801]},{"x":159.14735516372795,"y":297.9471032745592,"value":[159.14735516372795,297.9471032745592]},{"x":232.544080604534,"y":284.13979848866495,"value":[232.544080604534,284.13979848866495]},{"x":378.6108312342569,"y":244.5346347607053,"value":[378.6108312342569,244.5346347607053]}],"autoDetectionData":null},{"name":"guide:weightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244334,"y":254.34508816120908,"value":[547.9319899244334,254.34508816120908]},{"x":643.8564231738035,"y":325.9250629722922,"value":[643.8564231738035,325.9250629722922]},{"x":715.0730478589423,"y":369.1637279596977,"value":[715.0730478589423,369.1637279596977]}],"autoDetectionData":null},{"name":"guide:weightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244334,"y":291.77015113350126,"value":[547.9319899244334,291.77015113350126]},{"x":643.8564231738037,"y":353.176322418136,"value":[643.8564231738037,353.176322418136]},{"x":715.0730478589421,"y":388.7846347607053,"value":[715.0730478589421,388.7846347607053]}],"autoDetectionData":null},{"name":"guide:weightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244333,"y":315.3879093198993,"value":[547.9319899244333,315.3879093198993]},{"x":643.8564231738037,"y":373.5239294710328,"value":[643.8564231738037,373.5239294710328]},{"x":715.0730478589422,"y":408.4055415617128,"value":[715.0730478589422,408.4055415617128]}],"autoDetectionData":null},{"name":"guide:weightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244334,"y":341.54911838790935,"value":[547.9319899244334,341.54911838790935]},{"x":643.8564231738035,"y":393.1448362720403,"value":[643.8564231738035,393.1448362720403]},{"x":715.0730478589423,"y":428.38979848866495,"value":[715.0730478589423,428.38979848866495]}],"autoDetectionData":null},{"name":"guide:weightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244333,"y":363.71347607052894,"value":[547.9319899244333,363.71347607052894]},{"x":643.8564231738038,"y":408.7688916876574,"value":[643.8564231738038,408.7688916876574]},{"x":715.0730478589423,"y":442.1971032745592,"value":[715.0730478589423,442.1971032745592]}],"autoDetectionData":null},{"name":"guide:weightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244334,"y":377.1574307304786,"value":[547.9319899244334,377.1574307304786]},{"x":643.8564231738038,"y":424.02959697733,"value":[643.8564231738038,424.02959697733]},{"x":715.0730478589423,"y":456.36775818639865,"value":[715.0730478589423,456.36775818639865]}],"autoDetectionData":null},{"name":"guide:weight=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9319899244334,"y":627.5056675062978,"value":[547.9319899244334,627.5056675062978]},{"x":547.9319899244334,"y":63.949622166246876,"value":[547.9319899244334,63.949622166246876]}],"autoDetectionData":null},{"name":"guide:weight=1150","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":571.913098236776,"y":627.5056675062974,"value":[571.913098236776,627.5056675062974]},{"x":571.9130982367758,"y":63.949622166246854,"value":[571.9130982367758,63.949622166246854]}],"autoDetectionData":null},{"name":"guide:weight=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":596.257556675063,"y":627.5056675062978,"value":[596.257556675063,627.5056675062978]},{"x":596.257556675063,"y":63.949622166246854,"value":[596.257556675063,63.949622166246854]}],"autoDetectionData":null},{"name":"guide:weight=1050","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":619.5119647355165,"y":627.5056675062978,"value":[619.5119647355165,627.5056675062978]},{"x":619.148614609572,"y":63.949622166246854,"value":[619.148614609572,63.949622166246854]}],"autoDetectionData":null},{"name":"guide:weight=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":643.4930730478591,"y":627.5056675062979,"value":[643.4930730478591,627.5056675062979]},{"x":643.4930730478594,"y":63.94962216624686,"value":[643.4930730478594,63.94962216624686]}],"autoDetectionData":null},{"name":"guide:weight=950","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":666.7474811083123,"y":627.5056675062974,"value":[666.7474811083123,627.5056675062974]},{"x":666.7474811083125,"y":63.949622166246854,"value":[666.7474811083125,63.949622166246854]}],"autoDetectionData":null},{"name":"guide:weight=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":691.0919395465995,"y":627.5056675062976,"value":[691.0919395465995,627.5056675062976]},{"x":691.0919395465995,"y":63.94962216624686,"value":[691.0919395465995,63.94962216624686]}],"autoDetectionData":null},{"name":"guide:weight=850","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":715.0730478589422,"y":627.5056675062979,"value":[715.0730478589422,627.5056675062979]},{"x":715.0730478589422,"y":63.949622166246876,"value":[715.0730478589422,63.949622166246876]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":276.1460957178841,"value":[738.3274559193954,276.1460957178841]},{"x":822.2613350125946,"y":353.5396725440806,"value":[822.2613350125946,353.5396725440806]},{"x":904.7418136020152,"y":426.2096977329973,"value":[904.7418136020152,426.2096977329973]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":305.5774559193955,"value":[738.3274559193954,305.5774559193955]},{"x":822.2613350125945,"y":380.0642317380353,"value":[822.2613350125945,380.0642317380353]},{"x":904.7418136020151,"y":448.01070528967256,"value":[904.7418136020151,448.01070528967256]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193956,"y":327.74181360201516,"value":[738.3274559193956,327.74181360201516]},{"x":822.2613350125945,"y":403.68198992443325,"value":[822.2613350125945,403.68198992443325]},{"x":904.7418136020151,"y":463.99811083123427,"value":[904.7418136020151,463.99811083123427]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":352.8129722921915,"value":[738.3274559193954,352.8129722921915]},{"x":822.2613350125947,"y":425.11964735516375,"value":[822.2613350125947,425.11964735516375]},{"x":904.7418136020152,"y":484.345717884131,"value":[904.7418136020152,484.345717884131]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":393.1448362720403,"value":[738.3274559193954,393.1448362720403]},{"x":822.2613350125946,"y":456.7311083123426,"value":[822.2613350125946,456.7311083123426]},{"x":904.7418136020151,"y":507.60012594458436,"value":[904.7418136020151,507.60012594458436]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":424.7562972292192,"value":[738.3274559193954,424.7562972292192]},{"x":822.2613350125947,"y":483.9823677581864,"value":[822.2613350125947,483.9823677581864]},{"x":904.7418136020152,"y":534.8513853904283,"value":[904.7418136020152,534.8513853904283]}],"autoDetectionData":null},{"name":"guide:windComponentCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193956,"y":456.367758186398,"value":[738.3274559193956,456.367758186398]},{"x":822.2613350125946,"y":512.6870277078085,"value":[822.2613350125946,512.6870277078085]},{"x":904.7418136020151,"y":557.0157430730478,"value":[904.7418136020151,557.0157430730478]}],"autoDetectionData":null},{"name":"guide:windComponent=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.3274559193954,"y":627.5056675062976,"value":[738.3274559193954,627.5056675062976]},{"x":738.3274559193954,"y":63.949622166246854,"value":[738.3274559193954,63.949622166246854]}],"autoDetectionData":null},{"name":"guide:windComponent=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":821.5346347607054,"y":627.5056675062976,"value":[821.5346347607054,627.5056675062976]},{"x":821.5346347607056,"y":63.94962216624685,"value":[821.5346347607056,63.94962216624685]}],"autoDetectionData":null},{"name":"guide:windComponent=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":904.7418136020151,"y":627.5056675062976,"value":[904.7418136020151,627.5056675062976]},{"x":904.7418136020151,"y":63.94962216624685,"value":[904.7418136020151,63.94962216624685]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884131,"y":275.41939546599497,"value":[928.3595717884131,275.41939546599497]},{"x":976.3217884130985,"y":471.265113350126,"value":[976.3217884130985,471.265113350126]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884133,"y":391.32808564231743,"value":[928.3595717884133,391.32808564231743]},{"x":976.3217884130983,"y":526.8576826196477,"value":[976.3217884130983,526.8576826196477]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884132,"y":425.4829974811083,"value":[928.3595717884132,425.4829974811083]},{"x":976.3217884130985,"y":540.6649874055416,"value":[976.3217884130985,540.6649874055416]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884133,"y":459.6379093198992,"value":[928.3595717884133,459.6379093198992]},{"x":976.3217884130983,"y":555.1989924433253,"value":[976.3217884130983,555.1989924433253]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884132,"y":512.323677581864,"value":[928.3595717884132,512.323677581864]},{"x":976.3217884130985,"y":582.8136020151134,"value":[976.3217884130985,582.8136020151134]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884131,"y":537.7581863979851,"value":[928.3595717884131,537.7581863979851]},{"x":976.3217884130985,"y":592.6240554156172,"value":[976.3217884130985,592.6240554156172]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884131,"y":558.4691435768264,"value":[928.3595717884131,558.4691435768264]},{"x":976.3217884130985,"y":602.0711586901762,"value":[976.3217884130985,602.0711586901762]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.3595717884132,"y":627.5056675062982,"value":[928.3595717884132,627.5056675062982]},{"x":928.3595717884132,"y":63.94962216624693,"value":[928.3595717884132,63.94962216624693]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":976.3217884130984,"y":627.5056675062981,"value":[976.3217884130984,627.5056675062981]},{"x":976.3217884130985,"y":63.94962216624692,"value":[976.3217884130985,63.94962216624692]}],"autoDetectionData":null},{"name":"guide:landingDistance=1400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.31234256927,"y":87.20403022670024,"value":[988.31234256927,87.20403022670024]},{"x":1003.5730478589422,"y":87.20403022670024,"value":[1003.5730478589422,87.20403022670024]}],"autoDetectionData":null},{"name":"guide:landingDistance=1300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.31234256927,"y":127.1725440806045,"value":[988.31234256927,127.1725440806045]},{"x":1003.5730478589425,"y":127.17254408060448,"value":[1003.5730478589425,127.17254408060448]}],"autoDetectionData":null},{"name":"guide:landingDistance=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":166.41435768261965,"value":[988.3123425692698,166.41435768261965]},{"x":1003.5730478589425,"y":166.41435768261965,"value":[1003.5730478589425,166.41435768261965]}],"autoDetectionData":null},{"name":"guide:landingDistance=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692702,"y":206.3828715365239,"value":[988.3123425692702,206.3828715365239]},{"x":1003.5730478589425,"y":206.3828715365239,"value":[1003.5730478589425,206.3828715365239]}],"autoDetectionData":null},{"name":"guide:landingDistance=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":246.3513853904282,"value":[988.3123425692698,246.3513853904282]},{"x":1003.5730478589425,"y":246.35138539042822,"value":[1003.5730478589425,246.35138539042822]}],"autoDetectionData":null},{"name":"guide:landingDistance=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":285.5931989924433,"value":[988.3123425692698,285.5931989924433]},{"x":1003.573047858942,"y":285.5931989924433,"value":[1003.573047858942,285.5931989924433]}],"autoDetectionData":null},{"name":"guide:landingDistance=800","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":325.56171284634763,"value":[988.3123425692698,325.56171284634763]},{"x":1003.5730478589423,"y":325.56171284634763,"value":[1003.5730478589423,325.56171284634763]}],"autoDetectionData":null},{"name":"guide:landingDistance=700","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":364.80352644836273,"value":[988.3123425692698,364.80352644836273]},{"x":1003.5730478589425,"y":364.80352644836273,"value":[1003.5730478589425,364.80352644836273]}],"autoDetectionData":null},{"name":"guide:landingDistance=600","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692702,"y":404.4086901763224,"value":[988.3123425692702,404.4086901763224]},{"x":1003.5730478589422,"y":404.4086901763224,"value":[1003.5730478589422,404.4086901763224]}],"autoDetectionData":null},{"name":"guide:landingDistance=500","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.31234256927,"y":444.0138539042821,"value":[988.31234256927,444.0138539042821]},{"x":1003.5730478589427,"y":444.01385390428214,"value":[1003.5730478589427,444.01385390428214]}],"autoDetectionData":null},{"name":"guide:landingDistance=400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692703,"y":483.9823677581864,"value":[988.3123425692703,483.9823677581864]},{"x":1003.5730478589425,"y":483.9823677581864,"value":[1003.5730478589425,483.9823677581864]}],"autoDetectionData":null},{"name":"guide:landingDistance=300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692698,"y":523.9508816120909,"value":[988.3123425692698,523.9508816120909]},{"x":1003.5730478589427,"y":523.9508816120907,"value":[1003.5730478589427,523.9508816120907]}],"autoDetectionData":null},{"name":"guide:landingDistance=200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.31234256927,"y":563.1926952141059,"value":[988.31234256927,563.1926952141059]},{"x":1003.5730478589422,"y":563.192695214106,"value":[1003.5730478589422,563.192695214106]}],"autoDetectionData":null},{"name":"guide:landingDistance=100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":988.3123425692696,"y":602.4345088161209,"value":[988.3123425692696,602.4345088161209]},{"x":1003.573047858943,"y":602.434508816121,"value":[1003.573047858943,602.434508816121]}],"autoDetectionData":null}],"measurementColl":[]} \ No newline at end of file diff --git a/__tests__/chart/chase/wpd/da40-takeoff-climb-rate.wpd.json b/__tests__/chart/chase/wpd/da40-takeoff-climb-rate.wpd.json new file mode 100644 index 0000000..903b5a9 --- /dev/null +++ b/__tests__/chart/chase/wpd/da40-takeoff-climb-rate.wpd.json @@ -0,0 +1 @@ +{"version":[4,2],"axesColl":[{"name":"Image","type":"ImageAxes"}],"datasetColl":[{"name":"Default Dataset","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[],"autoDetectionData":null},{"name":"panel:densityAltitudeCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":110.38766368022033,"value":[13.13473466574776,110.38766368022033]},{"x":595.8139214334942,"y":693.3463128876638,"value":[595.8139214334942,693.3463128876638]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":252.07512060647838,"value":[13.13473466574776,252.07512060647838]},{"x":119.60992419021365,"y":222.172639558925,"value":[119.60992419021365,222.172639558925]},{"x":251.51619572708478,"y":175.50241212956584,"value":[251.51619572708478,175.50241212956584]},{"x":334.237077877326,"y":148.39455547898007,"value":[334.237077877326,148.39455547898007]},{"x":389.5706409372847,"y":132.1857339765679,"value":[389.5706409372847,132.1857339765679]},{"x":450.4934527911785,"y":110.38766368022053,"value":[450.4934527911785,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":311.32115782219165,"value":[13.13473466574776,311.32115782219165]},{"x":133.0241212956582,"y":273.87319090282557,"value":[133.0241212956582,273.87319090282557]},{"x":189.19607167470716,"y":255.70813232253622,"value":[189.19607167470716,255.70813232253622]},{"x":291.47932460372164,"y":218.53962784286702,"value":[291.47932460372164,218.53962784286702]},{"x":371.1261199172985,"y":190.03445899379744,"value":[371.1261199172985,190.03445899379744]},{"x":450.4934527911786,"y":160.97036526533427,"value":[450.4934527911786,160.97036526533427]},{"x":569.8239145416954,"y":110.38766368022054,"value":[569.8239145416954,110.38766368022054]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":368.8904203997244,"value":[13.13473466574776,368.8904203997244]},{"x":133.3035837353549,"y":325.8532046864232,"value":[133.3035837353549,325.8532046864232]},{"x":252.35458304617507,"y":283.37491385251553,"value":[252.35458304617507,283.37491385251553]},{"x":331.72191592005515,"y":260.4589937973812,"value":[331.72191592005515,260.4589937973812]},{"x":569.8239145416954,"y":175.2229496898691,"value":[569.8239145416954,175.2229496898691]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":422.8266712611992,"value":[13.13473466574776,422.8266712611992]},{"x":133.30358373535498,"y":382.0251550654721,"value":[133.30358373535498,382.0251550654721]},{"x":291.47932460372164,"y":325.8532046864232,"value":[291.47932460372164,325.8532046864232]},{"x":450.4934527911785,"y":273.8731909028256,"value":[450.4934527911785,273.8731909028256]},{"x":569.8239145416954,"y":232.7922122674019,"value":[569.8239145416954,232.7922122674019]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":476.4834596829773,"value":[13.13473466574776,476.4834596829773]},{"x":211.83252929014475,"y":409.4124741557547,"value":[211.83252929014475,409.4124741557547]},{"x":411.368711233632,"y":341.7825637491386,"value":[411.368711233632,341.7825637491386]},{"x":569.8239145416954,"y":283.09545141281876,"value":[569.8239145416954,283.09545141281876]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.134734665747763,"y":527.3456237077877,"value":[13.134734665747763,527.3456237077877]},{"x":172.70778773259818,"y":476.48345968297735,"value":[172.70778773259818,476.48345968297735]},{"x":569.8239145416954,"y":340.94417643004834,"value":[569.8239145416954,340.94417643004834]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":13.13473466574776,"y":728.2791178497589,"value":[13.13473466574776,728.2791178497589]},{"x":13.13473466574776,"y":110.3876636802205,"value":[13.13473466574776,110.3876636802205]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":53.377325982081324,"y":728.279117849759,"value":[53.377325982081324,728.279117849759]},{"x":53.377325982081324,"y":110.38766368022053,"value":[53.377325982081324,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":92.7815299793246,"y":728.2791178497589,"value":[92.7815299793246,728.2791178497589]},{"x":92.7815299793246,"y":110.38766368022051,"value":[92.7815299793246,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":132.74465885596143,"y":728.2791178497589,"value":[132.74465885596143,728.2791178497589]},{"x":132.74465885596143,"y":110.38766368022051,"value":[132.74465885596143,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":172.14886285320472,"y":728.2791178497589,"value":[172.14886285320472,728.2791178497589]},{"x":172.1488628532047,"y":110.3876636802205,"value":[172.1488628532047,110.3876636802205]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":211.55306685044798,"y":728.2791178497589,"value":[211.55306685044798,728.2791178497589]},{"x":211.55306685044798,"y":110.38766368022051,"value":[211.55306685044798,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":251.79565816678155,"y":728.2791178497589,"value":[251.79565816678155,728.2791178497589]},{"x":251.79565816678152,"y":110.38766368022051,"value":[251.79565816678152,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":291.19986216402486,"y":728.2791178497589,"value":[291.19986216402486,728.2791178497589]},{"x":291.1998621640248,"y":110.38766368022053,"value":[291.1998621640248,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":331.16299104066167,"y":728.279117849759,"value":[331.16299104066167,728.279117849759]},{"x":331.1629910406616,"y":110.38766368022051,"value":[331.1629910406616,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=25","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":370.5671950379049,"y":728.2791178497587,"value":[370.5671950379049,728.2791178497587]},{"x":370.5671950379049,"y":110.38766368022053,"value":[370.5671950379049,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=30","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":410.8097863542385,"y":728.2791178497589,"value":[410.8097863542385,728.2791178497589]},{"x":410.8097863542385,"y":110.38766368022053,"value":[410.8097863542385,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=35","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":450.2139903514818,"y":728.2791178497589,"value":[450.2139903514818,728.2791178497589]},{"x":450.2139903514818,"y":110.38766368022051,"value":[450.2139903514818,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=40","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":490.17711922811856,"y":728.279117849759,"value":[490.17711922811856,728.279117849759]},{"x":490.17711922811856,"y":110.38766368022053,"value":[490.17711922811856,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=45","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":529.5813232253619,"y":728.2791178497589,"value":[529.5813232253619,728.2791178497589]},{"x":529.5813232253619,"y":110.38766368022051,"value":[529.5813232253619,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=50","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":569.8239145416954,"y":728.2791178497589,"value":[569.8239145416954,728.2791178497589]},{"x":569.8239145416954,"y":110.38766368022053,"value":[569.8239145416954,110.38766368022053]}],"autoDetectionData":null},{"name":"panel:weightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":110.38766368022054,"value":[595.8139214334942,110.38766368022054]},{"x":1112.8436124528198,"y":693.3463128876638,"value":[1112.8436124528198,693.3463128876638]}],"autoDetectionData":null},{"name":"guide:weightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":171.0310130944177,"value":[595.8139214334942,171.0310130944177]},{"x":834.7543073742247,"y":189.19607167470713,"value":[834.7543073742247,189.19607167470713]},{"x":993.2095106822882,"y":204.28704341833225,"value":[993.2095106822882,204.28704341833225]}],"autoDetectionData":null},{"name":"guide:weightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":214.62715368711235,"value":[595.8139214334942,214.62715368711235]},{"x":834.7543073742247,"y":240.05823569951758,"value":[834.7543073742247,240.05823569951758]},{"x":993.2095106822882,"y":262.1357684355617,"value":[993.2095106822882,262.1357684355617]}],"autoDetectionData":null},{"name":"guide:weightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":261.57684355616817,"value":[595.8139214334942,261.57684355616817]},{"x":834.7543073742247,"y":293.7150241212957,"value":[834.7543073742247,293.7150241212957]},{"x":993.2095106822882,"y":322.4996554100621,"value":[993.2095106822882,322.4996554100621]}],"autoDetectionData":null},{"name":"guide:weightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":304.61405926946935,"value":[595.8139214334942,304.61405926946935]},{"x":834.7543073742247,"y":348.2101998621641,"value":[834.7543073742247,348.2101998621641]},{"x":993.209510682288,"y":383.4224672639559,"value":[993.209510682288,383.4224672639559]}],"autoDetectionData":null},{"name":"guide:weightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":347.65127498277053,"value":[595.8139214334942,347.65127498277053]},{"x":834.7543073742247,"y":397.6750516884907,"value":[834.7543073742247,397.6750516884907]},{"x":993.2095106822882,"y":448.2577532736044,"value":[993.2095106822882,448.2577532736044]}],"autoDetectionData":null},{"name":"guide:weightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":387.05547898001385,"value":[595.8139214334942,387.05547898001385]},{"x":834.7543073742247,"y":451.05237767057207,"value":[834.7543073742247,451.05237767057207]},{"x":993.2095106822882,"y":501.6350792556858,"value":[993.2095106822882,501.6350792556858]}],"autoDetectionData":null},{"name":"guide:weightCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334941,"y":434.8435561681599,"value":[595.8139214334941,434.8435561681599]},{"x":834.7543073742247,"y":505.2680909717437,"value":[834.7543073742247,505.2680909717437]},{"x":993.2095106822882,"y":563.1168159889731,"value":[993.2095106822882,563.1168159889731]}],"autoDetectionData":null},{"name":"guide:weight=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":595.8139214334942,"y":728.2791178497589,"value":[595.8139214334942,728.2791178497589]},{"x":595.8139214334941,"y":110.38766368022051,"value":[595.8139214334941,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:weight=1150","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":675.1812543073743,"y":728.2791178497589,"value":[675.1812543073743,728.2791178497589]},{"x":675.1812543073743,"y":110.3876636802205,"value":[675.1812543073743,110.3876636802205]}],"autoDetectionData":null},{"name":"guide:weight=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":754.8280496209511,"y":728.2791178497589,"value":[754.8280496209511,728.2791178497589]},{"x":754.8280496209511,"y":110.38766368022053,"value":[754.8280496209511,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:weight=1050","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":834.1953824948312,"y":728.2791178497589,"value":[834.1953824948312,728.2791178497589]},{"x":834.1953824948313,"y":110.38766368022051,"value":[834.1953824948313,110.38766368022051]}],"autoDetectionData":null},{"name":"guide:weight=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":913.8421778084081,"y":728.2791178497589,"value":[913.8421778084081,728.2791178497589]},{"x":913.842177808408,"y":110.38766368022053,"value":[913.842177808408,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:weight=950","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":993.2095106822882,"y":728.2791178497589,"value":[993.2095106822882,728.2791178497589]},{"x":993.2095106822882,"y":110.38766368022053,"value":[993.2095106822882,110.38766368022053]}],"autoDetectionData":null},{"name":"panel:climbRate","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":110.38766368022054,"value":[1006.6237077877327,110.38766368022054]},{"x":1129.307718814615,"y":693.3463128876638,"value":[1129.307718814615,693.3463128876638]}],"autoDetectionData":null},{"name":"guide:climbRate=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":110.38766368022054,"value":[1006.6237077877327,110.38766368022054]},{"x":1022.8325292901449,"y":110.38766368022053,"value":[1022.8325292901449,110.38766368022053]}],"autoDetectionData":null},{"name":"guide:climbRate=100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877328,"y":146.7177808407995,"value":[1006.6237077877328,146.7177808407995]},{"x":1022.832529290145,"y":146.7177808407995,"value":[1022.832529290145,146.7177808407995]}],"autoDetectionData":null},{"name":"guide:climbRate=200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":183.32736044107514,"value":[1006.6237077877327,183.32736044107514]},{"x":1022.8325292901449,"y":183.32736044107514,"value":[1022.8325292901449,183.32736044107514]}],"autoDetectionData":null},{"name":"guide:climbRate=300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877328,"y":220.21640248104762,"value":[1006.6237077877328,220.21640248104762]},{"x":1022.832529290145,"y":220.2164024810476,"value":[1022.832529290145,220.2164024810476]}],"autoDetectionData":null},{"name":"guide:climbRate=400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":256.26705720192973,"value":[1006.6237077877327,256.26705720192973]},{"x":1022.832529290145,"y":256.26705720192973,"value":[1022.832529290145,256.26705720192973]}],"autoDetectionData":null},{"name":"guide:climbRate=500","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":292.3177119228119,"value":[1006.6237077877327,292.3177119228119]},{"x":1022.832529290145,"y":292.3177119228119,"value":[1022.832529290145,292.3177119228119]}],"autoDetectionData":null},{"name":"guide:climbRate=600","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877328,"y":328.92729152308755,"value":[1006.6237077877328,328.92729152308755]},{"x":1022.8325292901449,"y":328.9272915230876,"value":[1022.8325292901449,328.9272915230876]}],"autoDetectionData":null},{"name":"guide:climbRate=700","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":365.5368711233633,"value":[1006.6237077877327,365.5368711233633]},{"x":1022.8325292901449,"y":365.53687112336326,"value":[1022.8325292901449,365.53687112336326]}],"autoDetectionData":null},{"name":"guide:climbRate=800","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":401.58752584424536,"value":[1006.6237077877327,401.58752584424536]},{"x":1022.8325292901449,"y":401.5875258442454,"value":[1022.8325292901449,401.5875258442454]}],"autoDetectionData":null},{"name":"guide:climbRate=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":438.47656788421784,"value":[1006.6237077877327,438.47656788421784]},{"x":1022.8325292901449,"y":438.4765678842178,"value":[1022.8325292901449,438.4765678842178]}],"autoDetectionData":null},{"name":"guide:climbRate=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":474.8066850447968,"value":[1006.6237077877327,474.8066850447968]},{"x":1022.8325292901449,"y":474.8066850447967,"value":[1022.8325292901449,474.8066850447967]}],"autoDetectionData":null},{"name":"guide:climbRate=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":511.41626464507243,"value":[1006.6237077877327,511.41626464507243]},{"x":1022.8325292901449,"y":511.41626464507243,"value":[1022.8325292901449,511.41626464507243]}],"autoDetectionData":null},{"name":"guide:climbRate=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":547.7463818056514,"value":[1006.6237077877327,547.7463818056514]},{"x":1022.8325292901449,"y":547.7463818056514,"value":[1022.8325292901449,547.7463818056514]}],"autoDetectionData":null},{"name":"guide:climbRate=1300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":584.355961405927,"value":[1006.6237077877327,584.355961405927]},{"x":1022.8325292901449,"y":584.355961405927,"value":[1022.8325292901449,584.355961405927]}],"autoDetectionData":null},{"name":"guide:climbRate=1400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":620.4066161268091,"value":[1006.6237077877327,620.4066161268091]},{"x":1022.8325292901449,"y":620.4066161268091,"value":[1022.8325292901449,620.4066161268091]}],"autoDetectionData":null},{"name":"guide:climbRate=1500","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":657.0161957270849,"value":[1006.6237077877327,657.0161957270849]},{"x":1022.8325292901449,"y":657.0161957270849,"value":[1022.8325292901449,657.0161957270849]}],"autoDetectionData":null},{"name":"guide:climbRate=1600","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":1006.6237077877327,"y":693.3463128876638,"value":[1006.6237077877327,693.3463128876638]},{"x":1022.8325292901449,"y":693.3463128876638,"value":[1022.8325292901449,693.3463128876638]}],"autoDetectionData":null}],"measurementColl":[]} \ No newline at end of file diff --git a/__tests__/chart/chase/wpd/da40-takeoff-distance.wpd.json b/__tests__/chart/chase/wpd/da40-takeoff-distance.wpd.json new file mode 100644 index 0000000..9433f3f --- /dev/null +++ b/__tests__/chart/chase/wpd/da40-takeoff-distance.wpd.json @@ -0,0 +1 @@ +{"version":[4,2],"axesColl":[{"name":"Image","type":"ImageAxes"}],"datasetColl":[{"name":"guide:headwindComponentCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":822.2421173977032,"y":605.4376202198591,"value":[822.2421173977032,605.4376202198591]},{"x":738.5746517569123,"y":580.5802213208154,"value":[738.5746517569123,580.5802213208154]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.4507846058779,"y":580.9383378016087,"value":[738.4507846058779,580.9383378016087]},{"x":782.171997036653,"y":564.885516226982,"value":[782.171997036653,564.885516226982]}],"autoDetectionData":null},{"name":"guide:headwindComponentCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.0780669144982,"y":558.3035935563817,"value":[738.0780669144982,558.3035935563817]},{"x":822.5935563816606,"y":583.371747211896,"value":[822.5935563816606,583.371747211896]},{"x":906.3928128872368,"y":605.574969021066,"value":[906.3928128872368,605.574969021066]}],"autoDetectionData":null},{"name":"guide:headwindComponentCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.436183395291,"y":520.7013630731102,"value":[738.436183395291,520.7013630731102]},{"x":822.9516728624536,"y":557.2292441140027,"value":[822.9516728624536,557.2292441140027]},{"x":907.8252788104094,"y":585.8785625774476,"value":[907.8252788104094,585.8785625774476]}],"autoDetectionData":null},{"name":"guide:headwindComponentCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":737.3618339529118,"y":472.35563816604713,"value":[737.3618339529118,472.35563816604713]},{"x":822.9516728624535,"y":525.3568773234202,"value":[822.9516728624535,525.3568773234202]},{"x":907.8252788104093,"y":569.4052044609667,"value":[907.8252788104093,569.4052044609667]}],"autoDetectionData":null},{"name":"guide:headwindComponentCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.0780669144979,"y":405.02973977695166,"value":[738.0780669144979,405.02973977695166]},{"x":822.5935563816604,"y":465.909541511772,"value":[822.5935563816604,465.909541511772]},{"x":907.4671623296163,"y":515.3296158612146,"value":[907.4671623296163,515.3296158612146]}],"autoDetectionData":null},{"name":"guide:headwindComponentCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.0780669144981,"y":269.6617100371747,"value":[738.0780669144981,269.6617100371747]},{"x":822.5935563816607,"y":365.99504337050803,"value":[822.5935563816607,365.99504337050803]},{"x":907.4671623296163,"y":429.73977695167287,"value":[907.4671623296163,429.73977695167287]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.4361833952911,"y":559.7360594795543,"value":[738.4361833952911,559.7360594795543]},{"x":781.0520446096655,"y":536.1003717472122,"value":[781.0520446096655,536.1003717472122]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.7942998760843,"y":522.1338289962825,"value":[738.7942998760843,522.1338289962825]},{"x":781.4101610904587,"y":499.5724907063197,"value":[781.4101610904587,499.5724907063197]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":738.7942998760843,"y":473.42998760842625,"value":[738.7942998760843,473.42998760842625]},{"x":781.4101610904582,"y":436.9021065675341,"value":[781.4101610904582,436.9021065675341]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":739.1524163568776,"y":406.4622057001239,"value":[739.1524163568776,406.4622057001239]},{"x":781.7682775712515,"y":369.9343246592317,"value":[781.7682775712515,369.9343246592317]}],"autoDetectionData":null},{"name":"guide:tailwindComponentCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":739.1524163568772,"y":270.7360594795539,"value":[739.1524163568772,270.7360594795539]},{"x":781.4101610904584,"y":219.5254027261462,"value":[781.4101610904584,219.5254027261462]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":11.817843866171005,"y":561.5266418835196,"value":[11.817843866171005,561.5266418835196]},{"x":85.23172242874845,"y":553.2899628252791,"value":[85.23172242874845,553.2899628252791]},{"x":232.77571251548946,"y":529.2961586121437,"value":[232.77571251548946,529.2961586121437]},{"x":525.7149938042135,"y":468.0582403965304,"value":[525.7149938042135,468.0582403965304]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=2000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":11.817843866171003,"y":537.1747211895914,"value":[11.817843866171003,537.1747211895914]},{"x":85.23172242874845,"y":527.1474597273855,"value":[85.23172242874845,527.1474597273855]},{"x":232.77571251548946,"y":501.36307311028503,"value":[232.77571251548946,501.36307311028503]},{"x":378.1710037174721,"y":466.26765799256503,"value":[378.1710037174721,466.26765799256503]},{"x":525.7149938042135,"y":423.6517967781908,"value":[525.7149938042135,423.6517967781908]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=4000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":85.58983890954151,"y":497.7819083023544,"value":[85.58983890954151,497.7819083023544]},{"x":232.77571251548946,"y":463.76084262701363,"value":[232.77571251548946,463.76084262701363]},{"x":525.7149938042133,"y":365.9950433705081,"value":[525.7149938042133,365.9950433705081]},{"x":11.10161090458488,"y":509.5997521685254,"value":[11.10161090458488,509.5997521685254]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=6000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":12.175960346964064,"y":481.3085501858736,"value":[12.175960346964064,481.3085501858736]},{"x":85.94795539033457,"y":461.6121437422553,"value":[85.94795539033457,461.6121437422553]},{"x":158.6456009913259,"y":439.40892193308554,"value":[158.6456009913259,439.40892193308554]},{"x":378.52912019826516,"y":349.8798017348203,"value":[378.52912019826516,349.8798017348203]},{"x":451.5848822800496,"y":310.8451053283767,"value":[451.5848822800496,310.8451053283767]},{"x":526.0731102850064,"y":262.4993804213135,"value":[526.0731102850064,262.4993804213135]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=8000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":11.817843866171003,"y":441.5576208178439,"value":[11.817843866171003,441.5576208178439]},{"x":158.6456009913259,"y":374.23172242874847,"value":[158.6456009913259,374.23172242874847]},{"x":249.24907063197026,"y":326.6022304832714,"value":[249.24907063197026,326.6022304832714]},{"x":305.47335811648077,"y":292.2230483271376,"value":[305.47335811648077,292.2230483271376]},{"x":378.52912019826516,"y":238.8636926889715,"value":[378.52912019826516,238.8636926889715]},{"x":451.58488228004956,"y":159.00371747211898,"value":[451.58488228004956,159.00371747211898]},{"x":500.6468401486989,"y":87.02230483271376,"value":[500.6468401486989,87.02230483271376]}],"autoDetectionData":null},{"name":"guide:pressureAltitude=10000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":12.175960346964066,"y":372.08302354399007,"value":[12.175960346964066,372.08302354399007]},{"x":158.28748451053292,"y":292.93928128872363,"value":[158.28748451053292,292.93928128872363]},{"x":232.4175960346964,"y":238.14745972738538,"value":[232.4175960346964,238.14745972738538]},{"x":299.38537794299873,"y":166.8822800495663,"value":[299.38537794299873,166.8822800495663]},{"x":328.7509293680297,"y":128.20570012391576,"value":[328.7509293680297,128.20570012391576]},{"x":358.1164807930607,"y":86.6641883519207,"value":[358.1164807930607,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:weightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.6344485749694,"y":509.5997521685254,"value":[548.6344485749694,509.5997521685254]},{"x":716.9491945477076,"y":587.669144981413,"value":[716.9491945477076,587.669144981413]},{"x":644.9677819083028,"y":557.2292441140027,"value":[644.9677819083028,557.2292441140027]}],"autoDetectionData":null},{"name":"guide:weightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.9925650557623,"y":454.4498141263941,"value":[548.9925650557623,454.4498141263941]},{"x":644.6096654275094,"y":526.7893432465925,"value":[644.6096654275094,526.7893432465925]},{"x":716.5910780669145,"y":548.992565055762,"value":[716.5910780669145,548.992565055762]}],"autoDetectionData":null},{"name":"guide:weightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":547.9182156133828,"y":369.9343246592317,"value":[547.9182156133828,369.9343246592317]},{"x":644.6096654275094,"y":477.727385377943,"value":[644.6096654275094,477.727385377943]},{"x":717.3073110285009,"y":522.1338289962828,"value":[717.3073110285009,522.1338289962828]}],"autoDetectionData":null},{"name":"guide:weightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.2763320941762,"y":218.8091697645601,"value":[548.2763320941762,218.8091697645601]},{"x":644.6096654275093,"y":405.0297397769517,"value":[644.6096654275093,405.0297397769517]},{"x":716.9491945477079,"y":469.84882280049567,"value":[716.9491945477079,469.84882280049567]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":928.2379182156133,"y":588.7434944237917,"value":[928.2379182156133,588.7434944237917]},{"x":978.732342007435,"y":535.3841387856256,"value":[978.732342007435,535.3841387856256]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@1","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579925,"y":554.7224287484512,"value":[929.3122676579925,554.7224287484512]},{"x":978.7323420074349,"y":501.00495662949197,"value":[978.7323420074349,501.00495662949197]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@2","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579927,"y":519.9851301115248,"value":[929.3122676579927,519.9851301115248]},{"x":978.3742255266421,"y":462.3283767038414,"value":[978.3742255266421,462.3283767038414]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@3","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579927,"y":490.2614622057001,"value":[929.3122676579927,490.2614622057001]},{"x":979.0904584882284,"y":413.26641883519204,"value":[979.0904584882284,413.26641883519204]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@4","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.312267657993,"y":458.3890954151177,"value":[929.312267657993,458.3890954151177]},{"x":978.7323420074354,"y":368.85997521685255,"value":[978.7323420074354,368.85997521685255]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579926,"y":436.543990086741,"value":[929.3122676579926,436.543990086741]},{"x":978.732342007435,"y":327.67657992565057,"value":[978.732342007435,327.67657992565057]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@6","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579925,"y":396.7930607187113,"value":[929.3122676579925,396.7930607187113]},{"x":979.0904584882283,"y":285.41883519206937,"value":[979.0904584882283,285.41883519206937]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@7","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.6703841387857,"y":355.96778190830236,"value":[929.6703841387857,355.96778190830236]},{"x":978.7323420074351,"y":214.51177199504335,"value":[978.7323420074351,214.51177199504335]}],"autoDetectionData":null},{"name":"guide:obstacleHeightCorrection@8","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.3122676579926,"y":268.58736059479554,"value":[929.3122676579926,268.58736059479554]},{"x":978.374225526642,"y":109.22552664188352,"value":[978.374225526642,109.22552664188352]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":12.175960346964066,"y":622.048327137546,"value":[12.175960346964066,622.048327137546]},{"x":12.175960346964064,"y":86.6641883519207,"value":[12.175960346964064,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":48.70384138785625,"y":622.0483271375462,"value":[48.70384138785625,622.0483271375462]},{"x":48.70384138785625,"y":86.66418835192069,"value":[48.70384138785625,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":85.23172242874844,"y":622.0483271375459,"value":[85.23172242874844,622.0483271375459]},{"x":85.23172242874847,"y":86.66418835192069,"value":[85.23172242874847,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=-5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":121.75960346964064,"y":622.048327137546,"value":[121.75960346964064,622.048327137546]},{"x":121.75960346964064,"y":86.6641883519207,"value":[121.75960346964064,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":158.6456009913259,"y":622.048327137546,"value":[158.6456009913259,622.048327137546]},{"x":158.6456009913259,"y":86.66418835192069,"value":[158.6456009913259,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":195.1734820322181,"y":622.0483271375459,"value":[195.1734820322181,622.0483271375459]},{"x":195.1734820322181,"y":86.6641883519207,"value":[195.1734820322181,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":232.4175960346964,"y":622.048327137546,"value":[232.4175960346964,622.048327137546]},{"x":232.4175960346964,"y":86.66418835192069,"value":[232.4175960346964,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":268.2292441140025,"y":622.0483271375463,"value":[268.2292441140025,622.0483271375463]},{"x":268.2292441140025,"y":86.6641883519207,"value":[268.2292441140025,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":305.1152416356877,"y":622.0483271375463,"value":[305.1152416356877,622.0483271375463]},{"x":305.1152416356877,"y":86.6641883519207,"value":[305.1152416356877,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=25","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":341.64312267657994,"y":622.048327137546,"value":[341.64312267657994,622.048327137546]},{"x":341.64312267657994,"y":86.6641883519207,"value":[341.64312267657994,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=30","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":378.52912019826516,"y":622.0483271375463,"value":[378.52912019826516,622.0483271375463]},{"x":378.52912019826516,"y":86.66418835192069,"value":[378.52912019826516,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=35","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":414.69888475836433,"y":622.0483271375459,"value":[414.69888475836433,622.0483271375459]},{"x":414.69888475836433,"y":86.66418835192069,"value":[414.69888475836433,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=40","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":451.58488228004956,"y":622.0483271375459,"value":[451.58488228004956,622.0483271375459]},{"x":451.58488228004956,"y":86.66418835192069,"value":[451.58488228004956,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=45","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":488.47087980173484,"y":622.0483271375464,"value":[488.47087980173484,622.0483271375464]},{"x":488.47087980173484,"y":86.6641883519207,"value":[488.47087980173484,86.6641883519207]}],"autoDetectionData":null},{"name":"guide:outsideAirTemperature=50","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":524.998760842627,"y":622.0483271375465,"value":[524.998760842627,622.0483271375465]},{"x":524.9987608426272,"y":86.66418835192069,"value":[524.9987608426272,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.6344485749692,"y":623.1226765799253,"value":[548.6344485749692,623.1226765799253]},{"x":548.6344485749692,"y":86.66418835192069,"value":[548.6344485749692,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=1150","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":572.6282527881044,"y":623.1226765799254,"value":[572.6282527881044,623.1226765799254]},{"x":572.6282527881044,"y":86.66418835192069,"value":[572.6282527881044,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":596.6220570012393,"y":623.122676579925,"value":[596.6220570012393,623.122676579925]},{"x":596.6220570012393,"y":86.66418835192069,"value":[596.6220570012393,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=1050","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":619.8996282527883,"y":623.1226765799256,"value":[619.8996282527883,623.1226765799256]},{"x":619.8996282527881,"y":86.66418835192069,"value":[619.8996282527881,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":644.2515489467163,"y":623.1226765799254,"value":[644.2515489467163,623.1226765799254]},{"x":644.2515489467163,"y":86.66418835192069,"value":[644.2515489467163,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=950","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":667.5291201982653,"y":623.1226765799253,"value":[667.5291201982653,623.1226765799253]},{"x":667.5291201982654,"y":86.66418835192069,"value":[667.5291201982654,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:weight=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":691.5229244114005,"y":623.1226765799256,"value":[691.5229244114005,623.1226765799256]},{"x":691.5229244114005,"y":86.66418835192067,"value":[691.5229244114005,86.66418835192067]}],"autoDetectionData":null},{"name":"guide:weight=850","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":715.5167286245353,"y":623.1226765799262,"value":[715.5167286245353,623.1226765799262]},{"x":715.5167286245353,"y":86.66418835192069,"value":[715.5167286245353,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=-5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":781.0520446096654,"y":623.1226765799254,"value":[781.0520446096654,623.1226765799254]},{"x":781.0520446096656,"y":86.66418835192069,"value":[781.0520446096656,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":781.0520446096654,"y":623.1226765799254,"value":[781.0520446096654,623.1226765799254]},{"x":781.0520446096656,"y":86.66418835192069,"value":[781.0520446096656,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":739.1524163568773,"y":623.1226765799254,"value":[739.1524163568773,623.1226765799254]},{"x":739.1524163568773,"y":86.66418835192069,"value":[739.1524163568773,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=5","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":781.0520446096654,"y":623.1226765799254,"value":[781.0520446096654,623.1226765799254]},{"x":781.0520446096656,"y":86.66418835192069,"value":[781.0520446096656,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=10","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":822.5935563816606,"y":623.1226765799252,"value":[822.5935563816606,623.1226765799252]},{"x":822.5935563816605,"y":86.66418835192069,"value":[822.5935563816605,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":864.1350681536557,"y":623.122676579925,"value":[864.1350681536557,623.122676579925]},{"x":864.1350681536555,"y":86.66418835192069,"value":[864.1350681536555,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:windComponent=20","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":906.3928128872369,"y":623.1226765799266,"value":[906.3928128872369,623.1226765799266]},{"x":906.3928128872368,"y":86.66418835192067,"value":[906.3928128872368,86.66418835192067]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=0","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":929.6703841387857,"y":623.1226765799261,"value":[929.6703841387857,623.1226765799261]},{"x":929.6703841387857,"y":86.66418835192069,"value":[929.6703841387857,86.66418835192069]}],"autoDetectionData":null},{"name":"guide:obstacleHeight=15","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":977.6579925650559,"y":623.1226765799264,"value":[977.6579925650559,623.1226765799264]},{"x":977.6579925650559,"y":86.66418835192067,"value":[977.6579925650559,86.66418835192067]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464686,"y":603.7843866171007,"value":[987.3271375464686,603.7843866171007]},{"x":1003.0842627013633,"y":603.7843866171005,"value":[1003.0842627013633,603.7843866171005]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464682,"y":564.7496902106568,"value":[987.3271375464682,564.7496902106568]},{"x":1003.0842627013635,"y":564.7496902106569,"value":[1003.0842627013635,564.7496902106569]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":525.3568773234203,"value":[987.3271375464684,525.3568773234203]},{"x":1003.0842627013631,"y":525.3568773234203,"value":[1003.0842627013631,525.3568773234203]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":485.24783147459726,"value":[987.3271375464684,485.24783147459726]},{"x":1003.0842627013634,"y":485.24783147459726,"value":[1003.0842627013634,485.24783147459726]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=500","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464685,"y":445.8550185873606,"value":[987.3271375464685,445.8550185873606]},{"x":1003.0842627013636,"y":445.8550185873606,"value":[1003.0842627013636,445.8550185873606]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=600","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":405.3878562577448,"value":[987.3271375464684,405.3878562577448]},{"x":1003.0842627013636,"y":405.3878562577447,"value":[1003.0842627013636,405.3878562577447]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=700","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464685,"y":365.9950433705081,"value":[987.3271375464685,365.9950433705081]},{"x":1002.0099132589839,"y":365.9950433705081,"value":[1002.0099132589839,365.9950433705081]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=800","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464685,"y":326.24411400247834,"value":[987.3271375464685,326.24411400247834]},{"x":1003.0842627013637,"y":326.24411400247834,"value":[1003.0842627013637,326.24411400247834]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=900","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":286.4931846344486,"value":[987.3271375464684,286.4931846344486]},{"x":1003.0842627013634,"y":286.4931846344486,"value":[1003.0842627013634,286.4931846344486]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=1000","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464685,"y":246.74225526641885,"value":[987.3271375464685,246.74225526641885]},{"x":1003.0842627013634,"y":246.74225526641885,"value":[1003.0842627013634,246.74225526641885]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=1100","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":206.9913258983891,"value":[987.3271375464684,206.9913258983891]},{"x":1003.0842627013632,"y":206.99132589838908,"value":[1003.0842627013632,206.99132589838908]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=1200","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464683,"y":166.8822800495663,"value":[987.3271375464683,166.8822800495663]},{"x":1003.0842627013633,"y":166.88228004956628,"value":[1003.0842627013633,166.88228004956628]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=1300","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464683,"y":127.84758364312268,"value":[987.3271375464683,127.84758364312268]},{"x":1003.0842627013639,"y":127.84758364312268,"value":[1003.0842627013639,127.84758364312268]}],"autoDetectionData":null},{"name":"guide:takeoffDistance=1400","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":987.3271375464684,"y":87.3804213135068,"value":[987.3271375464684,87.3804213135068]},{"x":1003.0842627013633,"y":87.3804213135068,"value":[1003.0842627013633,87.3804213135068]}],"autoDetectionData":null},{"name":"panel:densityAltitudeCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":12.534076827757126,"y":88.454770755886,"value":[12.534076827757126,88.454770755886]},{"x":548.9925650557659,"y":604.1425030978935,"value":[548.9925650557659,604.1425030978935]}],"autoDetectionData":null},{"name":"panel:weightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":548.9867016876889,"y":88.45477075588599,"value":[548.9867016876889,88.45477075588599]},{"x":739.5046694695969,"y":604.1425030978935,"value":[739.5046694695969,604.1425030978935]}],"autoDetectionData":null},{"name":"panel:windComponentCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":739.5105328376704,"y":88.45477075588599,"value":[739.5105328376704,88.45477075588599]},{"x":930.0285006195787,"y":604.1425030978936,"value":[930.0285006195787,604.1425030978936]}],"autoDetectionData":null},{"name":"panel:obstacleHeightCorrection","axesName":"Image","colorRGB":[200,0,0,255],"metadataKeys":[],"data":[{"x":930.0285006195787,"y":88.45477075588599,"value":[930.0285006195787,88.45477075588599]},{"x":1072.8213584535818,"y":604.1425030978936,"value":[1072.8213584535818,604.1425030978936]}],"autoDetectionData":null}],"measurementColl":[]} \ No newline at end of file diff --git a/__tests__/chart/chase/wpd/wpd-types.test.ts b/__tests__/chart/chase/wpd/wpd-types.test.ts new file mode 100644 index 0000000..915b411 --- /dev/null +++ b/__tests__/chart/chase/wpd/wpd-types.test.ts @@ -0,0 +1,10 @@ +import projectJson from "./da40-takeoff-distance.wpd.json"; +import { isWpdProject } from "../../../../src/chart/chase/wpd/wpd-types"; + +describe("wpd-types", () => { + describe("isWpdProject()", () => { + it("correctly identifies a valid WPD project", () => { + expect(isWpdProject(projectJson)).toBe(true); + }); + }); +}); diff --git a/__tests__/geometry/GeometryUtils.test.ts b/__tests__/geometry/GeometryUtils.test.ts index 7ca547a..95a32a3 100644 --- a/__tests__/geometry/GeometryUtils.test.ts +++ b/__tests__/geometry/GeometryUtils.test.ts @@ -1,17 +1,63 @@ -import {contact} from "../../src/geometry"; +import { contact, interpolatePath, sortPath } from "../../src/geometry"; + +import type { Path } from "../../src/geometry"; describe("GeometryUtils", () => { - describe("contact()", () => { - it("should return abutting paths", () => { - expect(contact([[1, 1], [1, 2]], [[1, 2], [1, 3]])).toEqual([1, 2]); - expect(contact([[1, 2], [1, 3]], [[1, 1], [1, 2]])).toEqual([1, 2]); - expect(contact([[1, 1], [2, 1]], [[2, 1], [3, 1]])).toEqual([2, 1]); - }); - it("should return intersecting paths", () => { - expect(contact([[1, 1], [2, 2]], [[1, 2], [2, 1]])).toEqual([1.5, 1.5]); - }); - it("should return undefined for paths that do not abut or intersect", () => { - expect(contact([[1, 1], [1, 2]], [[2, 1], [2, 2]])).toBeUndefined(); - }); + describe("contact()", () => { + it("should return abutting paths", () => { + expect(contact([[1, 1], [1, 2]], [[1, 2], [1, 3]])).toEqual([1, 2]); + expect(contact([[1, 2], [1, 3]], [[1, 1], [1, 2]])).toEqual([1, 2]); + expect(contact([[1, 1], [2, 1]], [[2, 1], [3, 1]])).toEqual([2, 1]); + }); + it("should return intersecting paths", () => { + expect(contact([[1, 1], [2, 2]], [[1, 2], [2, 1]])).toEqual([1.5, 1.5]); + }); + it("should return undefined for paths that do not abut or intersect", () => { + expect(contact([[1, 1], [1, 2]], [[2, 1], [2, 2]])).toBeUndefined(); + }); + }); + describe("interpolatePath()", () => { + it("should return the first path if factor is 0", () => { + const original: Path = [[1, 1], [1, 2], [1, 3]]; + expect(interpolatePath(original, [[2, 1], [2, 2], [2, 3]], "right", 0)).toBe(original); + }); + it("should return the second path if factor is 1", () => { + const original: Path = [[2, 1], [2, 2], [2, 3]]; + expect(interpolatePath([[1, 1], [1, 2], [1, 3]], original, "right", 1)).toBe(original); + }); + it("should interpolate between paths in direction: down", () => { + expect(interpolatePath([[1, 1], [1, 2]], [[2, 1], [2, 2]], "down", 0.25)) + .toEqual([[1.25, 1], [1.25, 2]]); + }); + it("should interpolate between paths in direction: left", () => { + expect(interpolatePath([[1, 1], [2, 1]], [[1, 2], [2, 2]], "left", 0.5)) + .toEqual([[2, 1.5], [1, 1.5]]); + }); + it("should interpolate between paths in direction: right", () => { + expect(interpolatePath([[1, 1], [2, 1]], [[1, 2], [2, 2]], "right", 0.75)) + .toEqual([[1, 1.75], [2, 1.75]]); + }); + it("should interpolate between paths in direction: up", () => { + expect(interpolatePath([[1, 1], [1, 2]], [[2, 1], [2, 2]], "up", 0.5)) + .toEqual([[1.5, 2], [1.5, 1]]); + }); + it("should interpolate between staggered paths", () => { + expect(interpolatePath([[1, 1], [2, 1], [3, 1]], [[1.5, 2], [2.5, 2], [3.5, 2]], "right", 0.5)) + .toEqual([[1.5, 1.5], [2, 1.5], [2.5, 1.5], [3, 1.5]]); + }); + }); + describe("sortPath()", () => { + it("sorts a path in direction: down", () => { + expect(sortPath([[2, 1], [2, 2], [2, 0]], "down")).toEqual([[2, 0], [2, 1], [2, 2]]); + }); + it("sorts a path in direction: left", () => { + expect(sortPath([[1, 2], [2, 2], [0, 2]], "left")).toEqual([[2, 2], [1, 2], [0, 2]]); + }); + it("sorts a path in direction: right", () => { + expect(sortPath([[1, 2], [2, 2], [0, 2]], "right")).toEqual([[0, 2], [1, 2], [2, 2]]); + }); + it("sorts a path in direction: up", () => { + expect(sortPath([[2, 1], [2, 2], [2, 0]], "up")).toEqual([[2, 2], [2, 1], [2, 0]]); }); + }); }); diff --git a/__tests__/misc/InterpolationUtils.test.ts b/__tests__/misc/InterpolationUtils.test.ts new file mode 100644 index 0000000..a19bca5 --- /dev/null +++ b/__tests__/misc/InterpolationUtils.test.ts @@ -0,0 +1,44 @@ +import { pickAdjacent } from "../../src/misc"; + +describe("InterpolationUtils", () => { + describe("pickAdjacent()", () => { + it("returns the first entry if out of range low", () => { + const entries = [ + { value: 100 }, + { value: 200 }, + ]; + const [index, adjacent] = pickAdjacent(entries, "value", 0); + expect(index).toEqual(0); + expect(adjacent[0]).toBe(entries[0]); + }); + it("returns the last entry if out of range high", () => { + const entries = [ + { value: 100 }, + { value: 200 }, + ]; + const [index, adjacent] = pickAdjacent(entries, "value", 300); + expect(index).toEqual(1); + expect(adjacent[0]).toBe(entries[1]); + }); + it("returns a single entry if exact match", () => { + const entries = [ + { value: 100 }, + { value: 200 }, + ]; + const [index, adjacent] = pickAdjacent(entries, "value", 200); + expect(index).toEqual(1); + expect(adjacent[0]).toBe(entries[1]); + }); + it("returns two adjacent entries if between", () => { + const entries = [ + { value: 100 }, + { value: 200 }, + ]; + const [index, adjacent] = pickAdjacent(entries, "value", 150); + expect(index).toEqual(0); + expect(adjacent.length).toBe(2); + expect(adjacent[0]).toBe(entries[0]); + expect(adjacent[1]).toBe(entries[1]); + }); + }); +}); diff --git a/__tests__/performance/PerformanceUtils.test.ts b/__tests__/performance/PerformanceUtils.test.ts new file mode 100644 index 0000000..0e17bf4 --- /dev/null +++ b/__tests__/performance/PerformanceUtils.test.ts @@ -0,0 +1,70 @@ +import _ from "lodash"; +import { convertUnits, convertVariables } from "../../src/performance/PerformanceUtils"; + +describe("PerformanceUtils", () => { + describe("convertUnits()", () => { + it("converts celsius to fahrenheit", () => { + expect(convertUnits(0, "degrees celsius", "degrees fahrenheit")).toBeCloseTo(32); + expect(convertUnits(15, "degrees celsius", "degrees fahrenheit")).toBeCloseTo(59); + expect(convertUnits(-40, "degrees celsius", "degrees fahrenheit")).toBeCloseTo(-40); + }); + it("converts fahrenheit to celsius", () => { + expect(convertUnits(32, "degrees fahrenheit", "degrees celsius")).toBeCloseTo(0); + expect(convertUnits(59, "degrees fahrenheit", "degrees celsius")).toBeCloseTo(15); + expect(convertUnits(-40, "degrees fahrenheit", "degrees celsius")).toBeCloseTo(-40); + }); + it("converts feet to meters", () => { + expect(convertUnits(1_000, "feet", "meters")).toBeCloseTo(304.8, 1); + }); + it("converts meters to feet", () => { + expect(convertUnits(15, "meters", "feet")).toBeCloseTo(49.2, 1); + }); + it("converts pounds to kilograms", () => { + expect(convertUnits(2646, "pounds", "kilograms")).toBeCloseTo(1200.2, 1); + }); + it("converts kilograms to pounds", () => { + expect(convertUnits(1200, "kilograms", "pounds")).toBeCloseTo(2645.5, 1); + }); + it("throws a descriptive error if no conversion is provided", () => { + expect(() => convertUnits(0, "degrees celsius", "kilograms")).toThrow("No conversion: degrees celsius to kilograms"); + }); + }); + describe("convertVariables()", () => { + it("converts variables to match the units of the inputs", () => { + const converted = convertVariables({ + obstacleHeight: { + unit: "feet", + value: 50, + }, + outsideAirTemperature: { + unit: "degrees fahrenheit", + value: 59, + }, + weight: { + unit: "pounds", + value: 2646, + }, + }, { + obstacleHeight: { + unit: "meters", + range: [0, 0], + }, + outsideAirTemperature: { + unit: "degrees celsius", + range: [0, 0], + }, + weight: { + unit: "kilograms", + range: [0, 0], + }, + }); + expect(_.keys(converted).sort()).toEqual(["obstacleHeight", "outsideAirTemperature", "weight"]); + expect(converted["obstacleHeight"].unit).toEqual("meters"); + expect(converted["obstacleHeight"].value).toBeCloseTo(15.2, 1); + expect(converted["outsideAirTemperature"].unit).toEqual("degrees celsius"); + expect(converted["outsideAirTemperature"].value).toBeCloseTo(15); + expect(converted["weight"].unit).toEqual("kilograms"); + expect(converted["weight"].value).toBeCloseTo(1200.2, 1); + }); + }); +}); diff --git a/__tests__/tracking/TrackingState.test.ts b/__tests__/tracking/TrackingState.test.ts index ab3c352..7346b27 100644 --- a/__tests__/tracking/TrackingState.test.ts +++ b/__tests__/tracking/TrackingState.test.ts @@ -1,305 +1,305 @@ -import {TrackingState} from "../../src/tracking/TrackingState"; -import {DateTime} from "luxon"; +import { TrackingState } from "../../src/tracking/TrackingState"; +import { DateTime } from "luxon"; describe("TrackingState", () => { - test("initial()", () => { - const instance = TrackingState.initial({ - ids: ["a60631"], - nonTrackingInterval: {minute: 5}, - trackingInterval: {minute: 1} + test("initial()", () => { + const instance = TrackingState.initial({ + ids: ["a60631"], + nonTrackingInterval: { minute: 5 }, + trackingInterval: { minute: 1 }, + }); + expect(instance.error).toBeUndefined(); + expect(instance.ids).toStrictEqual(["a60631"]); + expect(instance.interval).toStrictEqual({ minute: 5 }); + expect(instance.positions).toStrictEqual({}); + expect(instance.nonTrackingInterval).toStrictEqual({ minute: 5 }); + expect(instance.trackingInterval).toStrictEqual({ minute: 1 }); + }); + describe("reduce()", () => { + const initial = TrackingState.initial({ + ids: ["a60631"], + nonTrackingInterval: { minute: 5 }, + trackingInterval: { minute: 1 }, + }); + test("Action: error occurred", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const intermediate = TrackingState.reduce(initial, { + kind: "positions updated", + payload: { + positions: { + a60631: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }, + timestamp: now, + }, + }); + const updated = TrackingState.reduce(intermediate, { + kind: "error occurred", + payload: { + error: "this is an error", + timestamp: now, + }, + }); + expect(updated.error).toStrictEqual({ + error: "this is an error", + timestamp: now, + }); + expect(updated.ids).toStrictEqual(initial.ids); + expect(updated.interval).toStrictEqual({ minute: 5 }); + expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); + expect(updated.positions).toStrictEqual({}); + expect(updated.tracking).toBe(false); + expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); + }); + describe("Action: ids updated", () => { + test("Clears any previous error", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const intermediate = TrackingState.reduce(initial, { + kind: "error occurred", + payload: { + error: "this is an error", + timestamp: now, + }, + }); + expect(intermediate.error).not.toBeUndefined(); + const updated = TrackingState.reduce(intermediate, { + kind: "ids updated", + payload: [], + }); + expect(updated.error).toBeUndefined(); + }); + test("Makes no change if the list of IDs is the same.", () => { + const updated = TrackingState.reduce(initial, { + kind: "ids updated", + payload: ["a60631"], + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toBe(initial.ids); + expect(updated.positions).toBe(initial.positions); + expect(updated.nextUpdate).toBe(initial.nextUpdate); + }); + test("Adds newly tracked IDs.", () => { + const before = Date.now(); + const updated = TrackingState.reduce(initial, { + kind: "ids updated", + payload: ["a60632", "a60631"], }); - expect(instance.error).toBeUndefined(); - expect(instance.ids).toStrictEqual(["a60631"]); - expect(instance.interval).toStrictEqual({minute: 5}); - expect(instance.positions).toStrictEqual({}); - expect(instance.nonTrackingInterval).toStrictEqual({minute: 5}); - expect(instance.trackingInterval).toStrictEqual({minute: 1}); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(["a60631", "a60632"]); + expect(updated.positions).toBe(initial.positions); + expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); + }); + test("Removes no longer tracked IDs.", () => { + const before = Date.now(); + const updated = TrackingState.reduce(initial, { + kind: "ids updated", + payload: ["a60632"], + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(["a60632"]); + expect(updated.positions).toStrictEqual({}); + expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); + }); + test("Sorts and ensures uniqueness of the ID list.", () => { + const before = Date.now(); + const updated = TrackingState.reduce(initial, { + kind: "ids updated", + payload: ["a60634", "a60633", "a60632", "a60633", "a60634"], + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(["a60632", "a60633", "a60634"]); + expect(updated.positions).toStrictEqual({}); + expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); + }); }); - describe("reduce()", () => { - const initial = TrackingState.initial({ - ids: ["a60631"], - nonTrackingInterval: {minute: 5}, - trackingInterval: {minute: 1} + describe("Action: positions updated", () => { + test("Clears any previous error", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const intermediate = TrackingState.reduce(initial, { + kind: "error occurred", + payload: { + error: "this is an error", + timestamp: now, + }, + }); + expect(intermediate.error).not.toBeUndefined(); + const updated = TrackingState.reduce(intermediate, { + kind: "positions updated", + payload: { + positions: {}, + timestamp: now, + }, + }); + expect(updated.error).toBeUndefined(); + }); + test("Removes position for previously seen aircraft which is no longer seen", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const intermediate = TrackingState.reduce(initial, { + kind: "positions updated", + payload: { + positions: { + a60631: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }, + timestamp: now, + }, + }); + expect(Object.keys(intermediate.positions)).toStrictEqual(["a60631"]); + const updated = TrackingState.reduce(intermediate, { + kind: "positions updated", + payload: { + positions: {}, + timestamp: now, + }, }); - test("Action: error occurred", () => { - const now = DateTime.now().setZone("UTC"); - const intermediate = TrackingState.reduce(initial, { - kind: "positions updated", - payload: { - positions: { - a60631: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }, - timestamp: now - } - }); - const updated = TrackingState.reduce(intermediate, { - kind: "error occurred", - payload: { - error: "this is an error", - timestamp: now - } - }); - expect(updated.error).toStrictEqual({ - error: "this is an error", - timestamp: now - }); - expect(updated.ids).toStrictEqual(initial.ids); - expect(updated.interval).toStrictEqual({minute: 5}); - expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); - expect(updated.positions).toStrictEqual({}); - expect(updated.tracking).toBe(false); - expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(["a60631"]); + expect(updated.interval).toStrictEqual({ minute: 5 }); + expect(updated.nonTrackingInterval).toStrictEqual({ minute: 5 }); + expect(updated.positions).toStrictEqual({}); + expect(updated.tracking).toBe(false); + expect(updated.trackingInterval).toStrictEqual({ minute: 1 }); + }); + describe("For previously seen aircraft", () => { + test("Updates position for tracked aircraft", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const intermediate = TrackingState.reduce(initial, { + kind: "positions updated", + payload: { + positions: { + a60631: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }, + timestamp: now, + }, + }); + expect(Object.keys(intermediate.positions)).toStrictEqual(["a60631"]); + const oneMinuteFromNow = now.plus({ minute: 1 }); + const updated = TrackingState.reduce(intermediate, { + kind: "positions updated", + payload: { + positions: { + a60631: { + altitude: 4_000, + coordinates: [43.029878, -89.117773], + track: 90, + velocity: { + horizontal: 103, + vertical: 129, + }, + }, + }, + timestamp: oneMinuteFromNow, + }, + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(["a60631"]); + expect(updated.interval).toStrictEqual({ minute: 1 }); + expect(updated.nonTrackingInterval).toStrictEqual({ minute: 5 }); + expect(updated.positions).toStrictEqual({ + a60631: { + altitude: 4_000, + coordinates: [43.029878, -89.117773], + timestamp: oneMinuteFromNow, + track: 90, + velocity: { + horizontal: 103, + vertical: 129, + }, + }, + }); + expect(updated.tracking).toBe(true); + expect(updated.trackingInterval).toStrictEqual({ minute: 1 }); }); - describe("Action: ids updated", () => { - test("Clears any previous error", () => { - const now = DateTime.now().setZone("UTC"); - const intermediate = TrackingState.reduce(initial, { - kind: "error occurred", - payload: { - error: "this is an error", - timestamp: now - } - }); - expect(intermediate.error).not.toBeUndefined(); - const updated = TrackingState.reduce(intermediate, { - kind: "ids updated", - payload: [] - }); - expect(updated.error).toBeUndefined(); - }); - test("Makes no change if the list of IDs is the same.", () => { - const updated = TrackingState.reduce(initial, { - kind: "ids updated", - payload: ["a60631"] - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toBe(initial.ids); - expect(updated.positions).toBe(initial.positions); - expect(updated.nextUpdate).toBe(initial.nextUpdate); - }); - test("Adds newly tracked IDs.", () => { - const before = Date.now(); - const updated = TrackingState.reduce(initial, { - kind: "ids updated", - payload: ["a60632", "a60631"] - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(["a60631", "a60632"]); - expect(updated.positions).toBe(initial.positions); - expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); - }); - test("Removes no longer tracked IDs.", () => { - const before = Date.now(); - const updated = TrackingState.reduce(initial, { - kind: "ids updated", - payload: ["a60632"] - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(["a60632"]); - expect(updated.positions).toStrictEqual({}); - expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); - }); - test("Sorts and ensures uniqueness of the ID list.", () => { - const before = Date.now(); - const updated = TrackingState.reduce(initial, { - kind: "ids updated", - payload: ["a60634", "a60633", "a60632", "a60633", "a60634"] - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(["a60632", "a60633", "a60634"]); - expect(updated.positions).toStrictEqual({}); - expect(updated.nextUpdate.toMillis()).toBeGreaterThanOrEqual(before); - }); + }); + describe("For previously unseen aircraft", () => { + test("Adds position for tracked aircraft", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const updated = TrackingState.reduce(initial, { + kind: "positions updated", + payload: { + positions: { + a60631: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }, + timestamp: now, + }, + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(initial.ids); + expect(updated.interval).toStrictEqual({ minute: 1 }); + expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); + expect(updated.positions).toStrictEqual({ + a60631: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + timestamp: now, + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }); + expect(updated.tracking).toBe(true); + expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); }); - describe("Action: positions updated", () => { - test("Clears any previous error", () => { - const now = DateTime.now().setZone("UTC"); - const intermediate = TrackingState.reduce(initial, { - kind: "error occurred", - payload: { - error: "this is an error", - timestamp: now - } - }); - expect(intermediate.error).not.toBeUndefined(); - const updated = TrackingState.reduce(intermediate, { - kind: "positions updated", - payload: { - positions: {}, - timestamp: now - } - }); - expect(updated.error).toBeUndefined(); - }); - test("Removes position for previously seen aircraft which is no longer seen", () => { - const now = DateTime.now().setZone("UTC"); - const intermediate = TrackingState.reduce(initial, { - kind: "positions updated", - payload: { - positions: { - a60631: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }, - timestamp: now - } - }); - expect(Object.keys(intermediate.positions)).toStrictEqual(["a60631"]); - const updated = TrackingState.reduce(intermediate, { - kind: "positions updated", - payload: { - positions: {}, - timestamp: now - } - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(["a60631"]); - expect(updated.interval).toStrictEqual({minute: 5}); - expect(updated.nonTrackingInterval).toStrictEqual({minute: 5}); - expect(updated.positions).toStrictEqual({}); - expect(updated.tracking).toBe(false); - expect(updated.trackingInterval).toStrictEqual({minute: 1}); - }); - describe("For previously seen aircraft", () => { - test("Updates position for tracked aircraft", () => { - const now = DateTime.now().setZone("UTC"); - const intermediate = TrackingState.reduce(initial, { - kind: "positions updated", - payload: { - positions: { - a60631: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }, - timestamp: now - } - }); - expect(Object.keys(intermediate.positions)).toStrictEqual(["a60631"]); - const oneMinuteFromNow = now.plus({minute: 1}); - const updated = TrackingState.reduce(intermediate, { - kind: "positions updated", - payload: { - positions: { - a60631: { - altitude: 4_000, - coordinates: [43.029878, -89.117773], - track: 90, - velocity: { - horizontal: 103, - vertical: 129 - } - } - }, - timestamp: oneMinuteFromNow - } - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(["a60631"]); - expect(updated.interval).toStrictEqual({minute: 1}); - expect(updated.nonTrackingInterval).toStrictEqual({minute: 5}); - expect(updated.positions).toStrictEqual({ - a60631: { - altitude: 4_000, - coordinates: [43.029878, -89.117773], - timestamp: oneMinuteFromNow, - track: 90, - velocity: { - horizontal: 103, - vertical: 129 - } - } - }); - expect(updated.tracking).toBe(true); - expect(updated.trackingInterval).toStrictEqual({minute: 1}); - }); - }); - describe("For previously unseen aircraft", () => { - test("Adds position for tracked aircraft", () => { - const now = DateTime.now().setZone("UTC"); - const updated = TrackingState.reduce(initial, { - kind: "positions updated", - payload: { - positions: { - a60631: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }, - timestamp: now - } - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(initial.ids); - expect(updated.interval).toStrictEqual({minute: 1}); - expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); - expect(updated.positions).toStrictEqual({ - a60631: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - timestamp: now, - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }); - expect(updated.tracking).toBe(true); - expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); - }); - test("Ignores position for untracked aircraft", () => { - const now = DateTime.now().setZone("UTC"); - const updated = TrackingState.reduce(initial, { - kind: "positions updated", - payload: { - positions: { - abc123: { - altitude: 3_000, - coordinates: [43.029877, -89.117772], - track: 270, - velocity: { - horizontal: 102.9, - vertical: 128 - } - } - }, - timestamp: now - } - }); - expect(updated.error).toBeUndefined(); - expect(updated.ids).toStrictEqual(initial.ids); - expect(updated.interval).toStrictEqual({minute: 5}); - expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); - expect(updated.positions).toStrictEqual(initial.positions); - expect(updated.tracking).toBe(false); - expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); - }); - }); + test("Ignores position for untracked aircraft", () => { + const now = DateTime.now().setZone("UTC") as DateTime; + const updated = TrackingState.reduce(initial, { + kind: "positions updated", + payload: { + positions: { + abc123: { + altitude: 3_000, + coordinates: [43.029877, -89.117772], + track: 270, + velocity: { + horizontal: 102.9, + vertical: 128, + }, + }, + }, + timestamp: now, + }, + }); + expect(updated.error).toBeUndefined(); + expect(updated.ids).toStrictEqual(initial.ids); + expect(updated.interval).toStrictEqual({ minute: 5 }); + expect(updated.nonTrackingInterval).toStrictEqual(initial.nonTrackingInterval); + expect(updated.positions).toStrictEqual(initial.positions); + expect(updated.tracking).toBe(false); + expect(updated.trackingInterval).toStrictEqual(initial.trackingInterval); }); + }); }); + }); }); diff --git a/package-lock.json b/package-lock.json index c82484b..1d6c747 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@mattj65817/aviation-js": "^1.0.1", "@mattj65817/util-js": "^1.1.4", - "axios": "^1.6.2", + "axios": "^1.6.7", "axios-retry": "^4.0.0", "immer": "^10.0.3", "lodash": "^4.17.21", @@ -23,11 +23,10 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/luxon": "^3.4.2", - "@types/qs": "^6.9.11", - "@types/react": "^18.2.60", + "@types/qs": "^6.9.12", + "@types/react": "^18.2.61", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", - "axios": "^1.6.7", "axios-mock-adapter": "^1.22.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -2039,15 +2038,15 @@ "dev": true }, "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", + "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==", "dev": true }, "node_modules/@types/react": { - "version": "18.2.60", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.60.tgz", - "integrity": "sha512-dfiPj9+k20jJrLGOu9Nf6eqxm2EyJRrq2NvwOFsfbb7sFExZ9WELPs67UImHj3Ayxg8ruTtKtNnbjaF8olPq0A==", + "version": "18.2.61", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.61.tgz", + "integrity": "sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==", "dev": true, "dependencies": { "@types/prop-types": "*", diff --git a/package.json b/package.json index e73006d..405b1c7 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "dependencies": { "@mattj65817/aviation-js": "^1.0.1", "@mattj65817/util-js": "^1.1.4", - "axios": "^1.6.2", + "axios": "^1.6.7", "axios-retry": "^4.0.0", "immer": "^10.0.3", "lodash": "^4.17.21", @@ -50,11 +50,10 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.14.202", "@types/luxon": "^3.4.2", - "@types/qs": "^6.9.11", - "@types/react": "^18.2.60", + "@types/qs": "^6.9.12", + "@types/react": "^18.2.61", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", - "axios": "^1.6.7", "axios-mock-adapter": "^1.22.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -80,6 +79,7 @@ "jest": { "preset": "ts-jest", "reporters": [ + "default", [ "jest-junit", { diff --git a/src/chart/ChartLoader.ts b/src/chart/ChartLoader.ts new file mode 100644 index 0000000..fa4ca89 --- /dev/null +++ b/src/chart/ChartLoader.ts @@ -0,0 +1,52 @@ +import { freeze } from "immer"; +import { ChaseChart, isChaseChartDef } from "./chase"; +import { WpdProject, WpdProjectDef } from "./chase/wpd"; +import { Chart } from "./chart-types"; + +/** + * {@link ChartLoader} loads performance charts by URL, providing the appropriate {@link Chart} instance based on the + * content of the chart definition. + */ +export class ChartLoader { + private constructor(private readonly base: URL, private readonly fetchJson: typeof defaultFetchJson) { + } + + /** + * Load a performance chart. + * + * @param src the URL of the chart definition. + */ + public async load(src: string | URL): Promise { + const { fetchJson } = this; + const url = src instanceof URL ? src : new URL(src, this.base); + const def = await fetchJson(url); + if (isChaseChartDef(def)) { + const proj = WpdProject.create(await fetchJson(new URL(def.project.src, url))); + return ChaseChart.create(def, proj, url); + } + throw Error("Unsupported chart type."); + } + + /** + * Create a {@link ChartLoader} instance. + * + * @param base the base URL. + * @param fetchJson the JSON fetch callback, primarily for testing. + */ + static create(base: URL, fetchJson: typeof defaultFetchJson = defaultFetchJson) { + return freeze(new ChartLoader(base, fetchJson), true); + } +} + +/** + * Default JSON fetch implementation. + * + * @param src the URL to fetch. + */ +async function defaultFetchJson(src: URL) { + const response = await fetch(src.toString()); + if (!response.ok) { + throw new Error(`Failed to fetch ${src}: ${response.statusText}`); + } + return await response.json() as B; +} diff --git a/src/chart/Contour.ts b/src/chart/Contour.ts deleted file mode 100644 index f2394ee..0000000 --- a/src/chart/Contour.ts +++ /dev/null @@ -1,13 +0,0 @@ -import _ from "lodash"; -import {Flow} from "./Flow"; -import {Path} from "../geometry"; -import {freeze} from "immer"; - -export class Contour { - constructor(private readonly flow: Flow, private readonly path: Path) { - } - - static create(flow: Flow, path: Path) { - return freeze(new Contour(flow, flow.sort(_.cloneDeep(path))), true); - } -} diff --git a/src/chart/Flow.ts b/src/chart/Flow.ts deleted file mode 100644 index e17a0b3..0000000 --- a/src/chart/Flow.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {Path, Point} from "../geometry"; -import {freeze} from "immer"; - -/** - * Axial flows. - */ -export const Flows = freeze, Flow>>({ - DOWN: { - descending: false, - vertical: true, - compare(pt0: Point, pt1: Point) { - return pt0[1] - pt1[1]; - }, - position(pt: Point) { - return pt[1]; - }, - sort(path: Path) { - return path.sort(this.compare); - }, - value(pt: Point) { - return pt[0]; - } - }, - LEFT: { - descending: true, - vertical: false, - compare(pt0: Point, pt1: Point) { - return pt1[0] - pt0[0]; - }, - position(pt: Point) { - return pt[0]; - }, - sort(path: Path): Path { - return path.sort(this.compare); - }, - value(pt: Point) { - return pt[1]; - } - }, - RIGHT: { - descending: false, - vertical: false, - compare(pt0: Point, pt1: Point) { - return pt0[0] - pt1[0]; - }, - position(pt: Point) { - return pt[0]; - }, - sort(path: Path) { - return path.sort(this.compare); - }, - value(pt: Point) { - return pt[1]; - } - }, - UP: { - descending: true, - vertical: true, - compare(pt0: Point, pt1: Point) { - return pt1[1] - pt0[1]; - }, - position(pt: Point) { - return pt[1]; - }, - sort(path: Path) { - return path.sort(this.compare); - }, - value(pt: Point) { - return pt[0]; - } - } -}, true); - -/** - * {@link Flow} implements comparison and extraction functions for an axial flow. - */ -export interface Flow { - - /** - * Flag indicating that the flow is in *descending* coordinate order, rather than the default *ascending*. - */ - descending: boolean; - - /** - * Flag indicating that the flow is vertical, rather than the default horizontal. - */ - vertical: boolean; - - /** - * Compare two points on the *position* axis. - * - * @param pt0 the first point. - * @param pt1 the second point. - */ - compare(pt0: Point, pt1: Point): number; - - /** - * Get the value of a point on the *position* axis. - * - * @param pt the point. - */ - position(pt: Point): number; - - /** - * Sort a path on the *position* axis. - * - * @param path the path. - */ - sort(path: Path): Path; - - /** - * Get the value of a point on the *value* axis. - * - * @param pt the point. - */ - value(pt: Point): number; -} diff --git a/src/chart/Guide.ts b/src/chart/Guide.ts deleted file mode 100644 index 7ff9b24..0000000 --- a/src/chart/Guide.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {freeze, immerable, produce} from "immer"; -import _ from "lodash"; -import {Contour} from "./Contour"; -import {Flow} from "./Flow"; - -import type {Path, Point} from "../geometry"; - -export class Guide { - [immerable] = true; - - readonly contours: [[order: number, value?: number], Contour][] = []; - - private constructor(private readonly flow: Flow, private readonly bounds: ("max" | "min")[]) { - } - - /** - * Determine whether the contours in this guide have values associated with them. - */ - get valued() { - const {contours} = this; - if (0 === contours.length) { - return false; - } - return null != contours[0][0][1]; - } - - /** - * Get the contour associated with a given value, interpolating if necessary. - * - * @param value the value. - */ - public at(value: number): Contour { - if (!this.valued) { - throw Error("Guide does not have valued contours."); - } - throw Error("TODO"); - } - - /** - * Get the contour *contacting* (intersecting or abutting) a given contour, interpolating if necessary. - * - * @param contour the contour. - */ - public contacting(contour: Contour): Contour { - throw Error("TODO"); - } - - /** - * Get the contour passing through a given point, interpolating if necessary. - * - * @param point the point. - */ - public through(point: Point): Contour { - throw Error("TODO"); - } - - /** - * Add a contour, optionally associating it with a value. - * - * @param path the path. - * @param value the value, if any. - */ - public addContour(path: Path, value?: number) { - const valued = this.valued; - if (valued !== (null != value)) { - throw Error("Guide cannot mix valued and unvalued contours."); - } - const {contours, flow} = this; - if (null != value && -1 !== contours.findIndex(([[nextValue]]) => value === nextValue)) { - throw Error(`Guide already contains a contour for value ${value}.`); - } - const contour: Guide["contours"][number] = [[contours.length, value], Contour.create(flow, path)]; - const index = _.sortedIndexBy(contours, contour, - ([[order, value]]) => valued ? value! : order); - return produce(this, draft => { - draft.contours.splice(index, 0, contour); - }); - } - - /** - * Create a new, empty {@link Guide} instance. - * - * @param flow the guide flow. - * @param bounds the end(s) at which the guide is bounded. - */ - static create(flow: Flow, bounds: Guide["bounds"] = ["max", "min"]) { - return freeze(new Guide(flow, [...bounds]), true); - } -} diff --git a/src/chart/chart-types.ts b/src/chart/chart-types.ts new file mode 100644 index 0000000..3957388 --- /dev/null +++ b/src/chart/chart-types.ts @@ -0,0 +1,31 @@ +/** + * {@link Chart} defines the public interface to a performance chart. + */ +export interface Chart { + + /** + * Chart metadata. + */ + meta: { + /** + * Metadata regarding the chart image. + */ + image: { + + /** + * URL from which the image can be loaded. + */ + src: URL; + + /** + * Pixel dimensions of the image. + */ + size: [width: number, height: number]; + }; + + /** + * URL from which the chart was loaded, typically a chart definition JSON file of some sort. + */ + src: URL; + }; +} diff --git a/src/chart/chase/CalcContext.ts b/src/chart/chase/CalcContext.ts new file mode 100644 index 0000000..cd51724 --- /dev/null +++ b/src/chart/chase/CalcContext.ts @@ -0,0 +1,98 @@ +import { freeze } from "immer"; +import _ from "lodash"; +import { isChase } from "./chase-types"; +import { Contour } from "./wpd"; + +import type { Path } from "../../geometry"; +import type { PerformanceVariables, VectorCalculation } from "../../performance"; +import type { Step } from "./chase-types"; + +/** + * {@link CalcContext} holds context for a performance calculation by {@link ChaseChart.calculate}. + */ +export class CalcContext implements VectorCalculation { + pos: Path = []; + steps: StepResult[] = []; + + private constructor(public readonly vars: PerformanceVariables) { + } + + /** + * Direction of the most recent chase step. + */ + get dir() { + const last = _.findLast(_.map(this.steps, "step"), isChase); + if (null == last) { + throw Error("No direction set."); + } + return last.chase; + } + + /** + * All guides used during calculation. + */ + get guides() { + return _.map(_.flatMap(this.steps, step => step.guides), "path"); + } + + /** + * Current cursor position within the calculation. + */ + get position() { + const { pos } = this; + if (_.isEmpty(pos)) { + throw Error("Position not set."); + } + return _.last(pos)!; + } + + /** + * All vectors navigated during calculation. + */ + get vectors() { + return _.transform(this.steps, (acc, { vector }, index, steps) => { + if (index > 0) { + const { step: previous } = steps[index - 1]; + if (isChase(previous) && false === previous.advance) { + acc.push([]); + } + } + acc[acc.length - 1].push(...vector.path); + }, [[]] as Path[]); + } + + get(variable: string) { + const { vars } = this; + if (!(variable in vars)) { + throw Error(`Variable not set: ${variable}`); + } + return vars[variable].value; + } + + apply(result: StepResult) { + const copy = _.cloneDeep(result); + this.steps.push(copy); + const { outputs, step } = copy; + if (isChase(step) && false !== step.advance) { + this.pos.push(_.last(copy.vector.path)!); + } + if (null != outputs) { + _.assign(this.vars, outputs); + } + return this; + } + + static create(vars: PerformanceVariables): CalcContext { + return freeze(new CalcContext(vars), true); + } +} + +/** + * Step which was applied during a performance calculation and its results. + */ +interface StepResult { + step: Step; + guides: Contour[]; + outputs?: PerformanceVariables; + vector: Contour; +} diff --git a/src/chart/chase/ChaseChart.ts b/src/chart/chase/ChaseChart.ts new file mode 100644 index 0000000..2abd2a3 --- /dev/null +++ b/src/chart/chase/ChaseChart.ts @@ -0,0 +1,167 @@ +import { freeze } from "immer"; +import _ from "lodash"; +import { convertVariables } from "../../performance"; +import { isChase, isSolve } from "./chase-types"; +import { WpdProject } from "./wpd"; +import { CalcContext } from "./CalcContext"; + +import type { AxialDirection } from "../../geometry"; +import type { PerformanceCalculator, PerformanceVariables, Calculation } from "../../performance"; +import type { Chart } from "../chart-types"; +import type { Chase, ChaseChartDef, GuideSpec, Solve } from "./chase-types"; + +/** + * {@link ChaseChart} implements the {@link PerformanceCalculator} interface for an aviation chase-around chart. + */ +export class ChaseChart implements Chart, PerformanceCalculator { + readonly inputs: PerformanceCalculator["inputs"] = {}; + readonly outputs: PerformanceCalculator["outputs"] = {}; + + meta: Chart["meta"]; + + private constructor( + private readonly def: ChaseChartDef, + private readonly proj: WpdProject, + src: URL, + ) { + const { inputs, outputs } = this; + def.steps.forEach(step => { + if (isChase(step) && step.until) { + inputs[step.until] = { + unit: step.unit, + range: proj.range(step.until) as [number, number], + }; + } else if (isSolve(step)) { + outputs[step.using] = { + unit: step.unit, + }; + } + }); + const { image } = def; + this.meta = { + image: { + src: new URL(image.src, src), + size: [...image.size], + }, + src, + }; + } + + calculate(vars: PerformanceVariables): Calculation { + + /* Check for inputs not present in the variables. */ + const { inputs } = this; + const missing = _.omit(inputs, _.keys(vars)); + if (!_.isEmpty(missing)) { + throw new Error(`Missing inputs: ${_.keys(missing).sort().join(", ")}`); + } + + /* Attempt to solve. */ + const { def: { steps } } = this; + return _.reduce(steps, (context, step) => { + if (isChase(step)) { + return this.doChase(context, step); + } else if (isSolve(step)) { + return this.doSolve(context, step); + } + throw Error("Unsupported step type."); + }, CalcContext.create(convertVariables(vars, inputs))); + } + + /** + * Apply a {@link Chase} step to a solution context. + * + * @param context the context. + * @param step the step. + * @private + */ + private doChase(context: CalcContext, step: Chase): CalcContext { + const { chase } = step; + const along = this.resolveContour(context, step.along, chase); + if (null == step.until) { + const [, vector] = along.split(context.position); + context = context.apply({ + guides: [along], + step, vector, + }); + } else { + const cross = "left" === chase || "right" === chase ? "down" : "right"; + const until = this.resolveContour(context, step.until, cross); + const [vector] = along.split(until); + context = context.apply({ + guides: [along, until], + step, vector, + }); + } + return context; + } + + /** + * Apply a {@link Solve} step to a solution context. + * + * @param context the context. + * @param step the step. + * @private + */ + private doSolve(context: CalcContext, step: Solve): CalcContext { + const { solve, using } = step; + const { outputs: { [using]: { unit } }, proj } = this; + const [value, along] = proj.solve(using, solve, context.position); + const vector = solve === context.dir ? along : along.split(context.position)[1]; + return context.apply({ + guides: [vector], + outputs: { [using]: { value, unit } }, + step, vector, + }); + } + + /** + * Resolve a guide, returning it as a sorted path. + * + * @param context the solution context. + * @param guide the guide or scale name. + * @param dir the guide direction. + * @private + */ + private resolveContour(context: CalcContext, guide: GuideSpec, dir: AxialDirection) { + const { proj } = this; + if (_.isString(guide)) { + if (proj.isScale(guide)) { + return proj.scale(guide, dir, context.get(guide)); + } else { + return proj.guide(guide, dir, context.position); + } + } else { + const vars = _.mapValues(context.vars, "value"); + const matches = _.entries(guide).flatMap( + ([expr, guide]) => { + const evaluate = new Function("vars", `with (vars) { return !!(${expr}); }`); + try { + if (evaluate(vars)) { + return [guide]; + } + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + return []; + }); + if (1 !== matches.length) { + throw Error(`Expected exactly one match in expression(s): [${_.keys(guide).join("], [")}]`); + } + return proj.guide(matches[0], dir, context.position); + } + } + + /** + * Create a {@link ChaseChart} instance. + * + * @param def the chart definition. + * @param proj the associated WebPlotDigital project. + * @param src the URL from which the chart was loaded. + */ + static create(def: ChaseChartDef, proj: WpdProject, src: URL): ChaseChart { + return freeze(new ChaseChart(_.cloneDeep(def), proj, src), true); + } +} \ No newline at end of file diff --git a/src/chart/chase/chase-types.ts b/src/chart/chase/chase-types.ts new file mode 100644 index 0000000..35d3c21 --- /dev/null +++ b/src/chart/chase/chase-types.ts @@ -0,0 +1,102 @@ +import _ from "lodash"; +import { isAxialDirection } from "../../geometry"; + +import type { AxialDirection } from "../../geometry"; +import type { AltitudeUnit, DistanceUnit, PowerUnit, TemperatureUnit } from "../../performance"; + +/** + * Top level of a chase-around chart definition file. + */ +export interface ChaseChartDef { + kind: "chase"; + version: "1.0"; + image: { + src: string; + size: [width: number, height: number]; + }; + project: { + src: string; + }; + steps: Step[]; +} + +/** + * Union of all step types. + */ +export type Step = + | Chase + | Solve; + +/** + * Chase a guide to an intersecting scale. + */ +export interface Chase { + chase: AxialDirection; + along: GuideSpec; + until: GuideName; + unit: PerformanceUnit; + advance?: false; +} + +/** + * Solve for a variable using a scale. + */ +export interface Solve { + solve: AxialDirection; + using: GuideName; + unit: PerformanceUnit; +} + +/** + * Guide specification; either a single guide name or a hash of test expressions to guide names. + */ +export type GuideSpec = + | GuideName + | { [expr in string]: GuideName; }; + +/** + * Guide name. + */ +export type GuideName = string; + +/** + * Any performance-related unit. + */ +type PerformanceUnit = + | AltitudeUnit + | DistanceUnit + | PowerUnit + | TemperatureUnit; + +/** + * Type guard for {@link Chase}. + * + * @param val the value. + */ +export function isChase(val: unknown): val is Chase { + return _.isObject(val) + && "chase" in val + && isAxialDirection(val.chase); +} + +/** + * Type guard for {@link ChaseChartDef}. + * + * @param val the value. + */ +export function isChaseChartDef(val: unknown): val is ChaseChartDef { + return _.isObject(val) + && "kind" in val + && "chase" === val.kind; +} + +/** + * Type guard for {@link Solve}. + * + * @param val the value. + */ +export function isSolve(val: unknown): val is Solve { + return _.isObject(val) + && "solve" in val + && isAxialDirection(val.solve); +} diff --git a/src/chart/chase/index.ts b/src/chart/chase/index.ts new file mode 100644 index 0000000..4a5b8e1 --- /dev/null +++ b/src/chart/chase/index.ts @@ -0,0 +1,8 @@ +import { ChaseChart } from "./ChaseChart"; +import { isChaseChartDef } from "./chase-types"; + +/* Module exports. */ +export { + ChaseChart, + isChaseChartDef, +}; diff --git a/src/chart/chase/wpd/Contour.ts b/src/chart/chase/wpd/Contour.ts new file mode 100644 index 0000000..1686045 --- /dev/null +++ b/src/chart/chase/wpd/Contour.ts @@ -0,0 +1,61 @@ +import { freeze } from "immer"; +import { scale } from "@mattj65817/util-js"; +import { contact, interpolatePath } from "../../../geometry"; +import { normalizePath } from "../../../geometry"; +import { pickAdjacent } from "../../../misc"; + +import type { AxialDirection, Path, Point } from "../../../geometry"; + +export class Contour { + private constructor(public readonly path: Path, private readonly dir: AxialDirection) { + } + + public interpolate(other: Contour, factor: number): Contour { + const { dir, path } = this; + return Contour.create(interpolatePath(path, other.path, dir, factor), dir); + } + + public split(at: Contour | Point): [Contour, Contour] { + const { dir, path } = this; + let pt: Point; + if (!(at instanceof Contour)) { + pt = at; + } else { + const point = contact(path, at.path); + if (null == point) { + throw Error("No contact between contours."); + } + pt = point; + } + const pK = "left" === dir || "right" === dir ? 0 : 1; + const pos = pt[pK]; + const [index, adjacent] = pickAdjacent(path, pK, pos); + if (pos === adjacent[0][pK]) { + return [Contour.create(path.slice(0, index + 1), dir), Contour.create(path.slice(index), dir)]; + } + if (1 === adjacent.length) { + throw Error("Point not in path."); + } + const [pa0, pa1] = adjacent; + const p0 = pa0[pK]; + const factor = (pos - p0) / (pa1[pK] - p0); + const vK = 1 - pK; + const v0 = pa0[vK]; + const value = scale(v0 + factor * (pa1[vK] - v0), 4); + if (0 === pK) { + return [ + Contour.create([...path.slice(0, index + 1), [pos, value]], dir), + Contour.create([[pos, value], ...path.slice(index + 1)], dir), + ]; + } else { + return [ + Contour.create([...path.slice(0, index + 1), [value, pos]], dir), + Contour.create([[value, pos], ...path.slice(index + 1)], dir), + ]; + } + } + + static create(path: Path, dir: AxialDirection) { + return freeze(new Contour(normalizePath(path, dir), dir), true); + } +} \ No newline at end of file diff --git a/src/chart/chase/wpd/WpdProject.ts b/src/chart/chase/wpd/WpdProject.ts new file mode 100644 index 0000000..2cb0437 --- /dev/null +++ b/src/chart/chase/wpd/WpdProject.ts @@ -0,0 +1,170 @@ +import { freeze } from "immer"; +import _ from "lodash"; +import { interpolatePath, pointAt, sortPath } from "../../../geometry"; +import { pickAdjacent } from "../../../misc"; +import { Contour } from "./Contour"; + +import type { AxialDirection, Path, Point } from "../../../geometry"; +import type { GuideName } from "../chase-types"; +import type { WpdProjectDef } from "./wpd-types"; + +export class WpdProject { + + /** + * Guides, which are non-valued contours ending in "@{number}" where "{number}" is simple sort order. + * + * The `contours` array is sorted in ascending order by `order`. + */ + readonly guides: { + [name in string]: { + contours: { + order: number; + path: Path; + }[]; + } + } = {}; + + /** + * Scales, which are valued contours ending in "={number}" where "{number}" is the value associated with the contour. + * + * The `values` array is sorted in ascending order by `value`. + */ + readonly scales: { + [name in string]: { + values: { + path: Path; + value: number; + }[]; + } + } = {}; + + private constructor(private readonly proj: WpdProjectDef) { + const { guides, scales } = this; + proj.datasetColl.forEach(({ name, data }) => { + const path: Path = _.map(data, "value"); + const guideMatcher = GUIDE.exec(name); + if (null != guideMatcher) { + const [, guide, type, value] = guideMatcher; + if ("=" === type) { + if (!(guide in scales)) { + scales[guide] = { values: [] }; + } + scales[guide].values.push({ path, value: parseFloat(value) }); + } else if ("@" === type) { + if (!(guide in guides)) { + guides[guide] = { contours: [] }; + } + guides[guide].contours.push({ order: parseInt(value), path }); + } + } + }); + _.values(guides).forEach(({ contours }) => contours.sort(({ order: o0 }, { order: o1 }) => o0 - o1)); + _.values(scales).forEach(({ values }) => values.sort(({ value: v0 }, { value: v1 }) => v0 - v1)); + } + + public isScale(guide: GuideName) { + return guide in this.scales; + } + + public guide(guide: GuideName, dir: AxialDirection, through: Point) { + const { guides } = this; + if (!(guide in guides)) { + throw Error(`Guide not found: ${guide}.`); + } + const nPK = "left" === dir || "right" === dir ? 0 : 1; + const nVK = 1 - nPK; + const negative = "left" === dir || "up" === dir; + const nPaths = guides[guide].contours.map(({ path }) => { + const sorted = sortPath(path, dir); + const pos = negative + ? Math.min(through[nPK], sorted[0][nPK]) + : Math.max(through[nPK], sorted[0][nPK]); + const pt = pointAt(sorted, pos, nPK); + return [pt[nVK], sorted] as const; + }); + const val = through[nVK]; + const [, nAdjacent] = pickAdjacent(nPaths, 0, val); + const [[nFirstVal, nFirstPath]] = nAdjacent; + if (val === nFirstVal) { + return Contour.create(nFirstPath, dir); + } else if (2 === nAdjacent.length) { + const [, [nSecondVal, nSecondPath]] = nAdjacent; + const factor = (val - nFirstVal) / (nSecondVal - nFirstVal); + const nPath = interpolatePath(nFirstPath, nSecondPath, dir, factor); + return Contour.create(nPath, dir); + } + throw Error("TODO: interpolate below min or above max."); + } + + public scale(scale: GuideName, dir: AxialDirection, value: number) { + const { scales } = this; + if (!(scale in scales)) { + throw Error(`Scale not found: ${scale}.`); + } + const { [scale]: { values } } = scales; + const [, adjacent] = pickAdjacent(values, "value", value); + if (value === adjacent[0].value) { + return Contour.create(sortPath(adjacent[0].path, dir), dir); + } else if (2 === adjacent.length) { + const [{ path: p0, value: v0 }, { path: p1, value: v1 }] = adjacent; + const factor = (value - v0) / (v1 - v0); + return Contour.create(interpolatePath(p0, p1, dir, factor), dir); + } + throw Error("TODO: interpolate below min or above max."); + } + + public solve(scale: GuideName, dir: AxialDirection, pos: Point): [number, Contour] { + const { scales } = this; + if (!(scale in scales)) { + throw Error(`Scale not found: ${scale}.`); + } + const vK = "left" === dir || "right" === dir ? 1 : 0; + const value = pos[vK]; + const { [scale]: { values } } = scales; + const points = _.transform(values, (values, { value, path }) => { + values.push([path[0][vK], value, path]); + }, [] as [number, number, Path][]); + const [, adjacent] = pickAdjacent(points, 0, value); + const [first] = adjacent; + const [p0, v0] = first; + if (value === p0) { + return [v0, Contour.create(first[2], dir)]; + } + if (1 === adjacent.length) { + throw Error("Value not in scale."); + } + const [, [p1, v1, path1]] = adjacent; + const factor = (value - p0) / (p1 - p0); + return [v0 + (v1 - v0) * factor, Contour.create(first[2], dir).interpolate(Contour.create(path1, dir), factor)]; + } + + /** + * Get the range of values associated with a scale. + * + * @param scale the scale. + */ + public range(scale: string) { + const { scales } = this; + if (!(scale in scales)) { + throw Error(`Scale not found: ${scale}.`); + } + const values = _.map(scales[scale].values, "value"); + return [_.min(values)!, _.max(values)!] as const; + } + + /** + * Create a {@link WpdProject} instance. + * + * @param proj the project definition file. + */ + static create(proj: WpdProjectDef): WpdProject { + return freeze(new WpdProject(_.cloneDeep(proj)), true); + } +} + +/** + * Name pattern for a guide dataset. + * + * @private + */ +const GUIDE = freeze(/^guide:([^=]+)([=@])(0|(-?[1-9]\d*))$/, true); diff --git a/src/chart/chase/wpd/index.ts b/src/chart/chase/wpd/index.ts new file mode 100644 index 0000000..cf6868b --- /dev/null +++ b/src/chart/chase/wpd/index.ts @@ -0,0 +1,11 @@ +import { Contour } from "./Contour"; +import { WpdProject } from "./WpdProject"; + +import type { WpdProjectDef } from "./wpd-types"; + +/* Module exports. */ +export { + Contour, + WpdProject, + WpdProjectDef, +}; diff --git a/src/chart/chase/wpd/wpd-types.ts b/src/chart/chase/wpd/wpd-types.ts new file mode 100644 index 0000000..eea34c9 --- /dev/null +++ b/src/chart/chase/wpd/wpd-types.ts @@ -0,0 +1,46 @@ +import _ from "lodash"; + +/** + * JSON format of a WebPlotDigitizer project file (the subset of it that we care about.) + */ +export interface WpdProjectDef { + version: [ + major: 4, + minor: 2 + ]; + datasetColl: { + name: DatasetName; + data: { + value: [ + x: number, + y: number + ]; + }[]; + }[]; +} + +/** + * Name of a dataset in a WebPlotDigitizer project. + */ +type DatasetName = + | `guide:${string}@${number}` + | `guide:${string}=${number}` + | `panel:${string}`; + +/** + * Type guard for {@link WpdProjectDef}. + * + * @param val the value. + */ +export function isWpdProject(val: unknown): val is WpdProjectDef { + return _.isObject(val) + && "datasetColl" in val + && "version" in val + && _.isEqual(val.version, [4, 2]) + && _.isArray(val.datasetColl) + && -1 === val.datasetColl.findIndex(next => !( + _.isObject(next) + && "data" in next + && _.isArray(next.data) + )); +} diff --git a/src/chart/index.ts b/src/chart/index.ts index 1ae809f..abfacdd 100644 --- a/src/chart/index.ts +++ b/src/chart/index.ts @@ -1,6 +1,6 @@ -import {Guide} from "./Guide"; +import { ChartLoader } from "./ChartLoader"; /* Module exports. */ export { - Guide + ChartLoader, }; diff --git a/src/flight-types.ts b/src/flight-types.ts index 3477fd5..16c5f77 100644 --- a/src/flight-types.ts +++ b/src/flight-types.ts @@ -1,5 +1,10 @@ -import {DateTime} from "luxon"; -import {AxiosInstance, CreateAxiosDefaults} from "axios"; +import { DateTime } from "luxon"; +import { AxiosInstance, CreateAxiosDefaults } from "axios"; + +/** + * Geographic coordinates. + */ +export type GeoCoordinates = [latitude: number, longitude: number]; /** * Factory function for creating Axios instances with merged configuration. diff --git a/src/geometry/GeometryUtils.ts b/src/geometry/GeometryUtils.ts index 660e8cf..b75287d 100644 --- a/src/geometry/GeometryUtils.ts +++ b/src/geometry/GeometryUtils.ts @@ -1,5 +1,8 @@ import _ from "lodash"; -import {Path, Point} from "./geometry-types"; +import { pickAdjacent } from "../misc"; + +import type { AxialDirection, Path, Point } from "./geometry-types"; +import { scale } from "@mattj65817/util-js"; /** * Find the *first* point, if any, at which two paths make contact, meaning that they intersect at or share that point. @@ -8,24 +11,135 @@ import {Path, Point} from "./geometry-types"; * @param p1 the second path. */ export function contact(p0: Path, p1: Path) { - for (let i0 = 0; i0 < p0.length - 1; i0 += 1) { - const [p00x, p00y] = p0[i0]; - const [p01x, p01y] = p0[i0 + 1]; - for (let i1 = 0; i1 < p1.length - 1; i1 += 1) { - const [p10x, p10y] = p1[i1]; - const [p11x, p11y] = p1[i1 + 1]; - const s0x = p01x - p00x; - const s0y = p01y - p00y; - const s1x = p11x - p10x; - const x1y = p11y - p10y; - const s = (-s0y * (p00x - p10x) + s0x * (p00y - p10y)) / (-s1x * s0y + s0x * x1y); - const t = (s1x * (p00y - p10y) - x1y * (p00x - p10x)) / (-s1x * s0y + s0x * x1y); - if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { - return [p00x + t * s0x, p00y + t * s0y] as Point; - } - } + for (let i0 = 0; i0 < p0.length - 1; i0 += 1) { + const [p00x, p00y] = p0[i0]; + const [p01x, p01y] = p0[i0 + 1]; + for (let i1 = 0; i1 < p1.length - 1; i1 += 1) { + const [p10x, p10y] = p1[i1]; + const [p11x, p11y] = p1[i1 + 1]; + const s0x = p01x - p00x; + const s0y = p01y - p00y; + const s1x = p11x - p10x; + const x1y = p11y - p10y; + const s = (-s0y * (p00x - p10x) + s0x * (p00y - p10y)) / (-s1x * s0y + s0x * x1y); + const t = (s1x * (p00y - p10y) - x1y * (p00x - p10x)) / (-s1x * s0y + s0x * x1y); + if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { + return [p00x + t * s0x, p00y + t * s0y] as Point; + } + } + } + + /* The above will not match abutting points (two paths sharing an exact endpoint.) Handle that case. */ + return _.head(_.intersectionWith(p0, p1, _.isEqual)); +} + +/** + * Interpolate a path at some position between two existing paths. + * + * Always returns a path that is sorted according to `dir`; there is no need to pre-sort paths. + * + * @param p0 the first path. + * @param p1 the second path. + * @param dir the direction of the paths. + * @param factor the interpolation factor; e.g. `0.5` to interpolate at the midpoint between the two paths. + */ +export function interpolatePath(p0: Path, p1: Path, dir: AxialDirection, factor: number = 0.5) { + if (0 === factor) { + return p0; + } else if (1 === factor) { + return p1; + } + const pK = "down" === dir || "up" === dir ? 1 : 0; + const vK = 1 - pK; + const [lVertices, uVertices] = [sortPath([...p0], dir), sortPath([...p1], dir)].sort( + ([pt0], [pt1]) => pt0[pK] - pt1[pK]); + const iPath: Path = []; + let inRange = false; + while (0 !== lVertices.length && 0 !== uVertices.length) { + const l = lVertices[0]; + const u = uVertices[0]; + const lPos = l[pK]; + const uPos = u[pK]; + if (!inRange) { + if (lPos < uPos) { + lVertices.shift(); + continue; + } + inRange = true; + } + + /* Determine next position to add to the interpolated contour. */ + let pos: number; + if (lPos > uPos) { + pos = uPos; + uVertices.shift(); + } else { + pos = lPos; + lVertices.shift(); + if (lPos === uPos) { + uVertices.shift(); + } + } + + /* Interpolate between values at the current position. */ + const pt0 = pointAt(p0, pos, pK); + const pt1 = pointAt(p1, pos, pK); + const v0 = pt0[vK]; + if (0 === pK) { + iPath.push([pos, v0 + factor * (pt1[vK] - v0)]); + } else { + iPath.push([v0 + factor * (pt1[vK] - v0), pos]); } + } + return iPath; +} + +/** + * Normalize a path by sorting it according to its direction and scaling it to four decimal places. + * + * @param path the path. + * @param dir the direction. + */ +export function normalizePath(path: Path, dir: AxialDirection): Path { + return sortPath(path, dir).map(([x, y]) => [scale(x, 4), scale(y, 4)]); +} - /* The above will not match abutting points (two paths sharing an exact endpoint.) Handle that case. */ - return _.head(_.intersectionWith(p0, p1, _.isEqual)); +/** + * Sort a path according to a direction. + * + * @param path the path. + * @param dir the direction. + * @private + */ +export function sortPath(path: Path, dir: AxialDirection) { + const order = "left" === dir || "up" === dir ? -1 : 1; + const key = "left" === dir || "right" === dir ? 0 : 1; + return _.clone(path).sort((p0, p1) => order * (p0[key] - p1[key])); +} + +/** + * Get the point at a specific position in a path, interpolating if necessary. + * + * @param path the path. + * @param pos the position. + * @param pK the position key (`0` for the X coordinate, `1` for the Y coordinate.) + */ +export function pointAt(path: Path, pos: number, pK: number): Point { + const [, adjacent] = pickAdjacent(path, pK, pos); + if (1 === adjacent.length) { + if (pos !== adjacent[0][pK]) { + throw Error(`Position not in path: ${pos}`); + } + return adjacent[0]; + } + const [pt0, pt1] = adjacent; + const p0 = pt0[pK]; + const factor = (pos - p0) / (pt1[pK] - p0); + const vK = 1 - pK; + const v0 = pt0[vK]; + if (0 === pK) { + return [pos, v0 + factor * (pt1[vK] - v0)]; + } else { + return [v0 + factor * (pt1[vK] - v0), pos]; + } } diff --git a/src/geometry/geometry-types.ts b/src/geometry/geometry-types.ts index 516dfc1..1a31b46 100644 --- a/src/geometry/geometry-types.ts +++ b/src/geometry/geometry-types.ts @@ -1,20 +1,41 @@ +import _ from "lodash"; + +/** + * Cardinal direction along an axis on a plane. + */ +export type AxialDirection = + | "down" + | "left" + | "right" + | "up"; + /** * Bounding box on a plane, in polar coordinates, defined by top-left and bottom-right points. */ export type Box = [ - topLeft: Point, - bottomRight: Point + topLeft: Point, + bottomRight: Point ]; /** * Single point on a plane, in polar coordinates. */ export type Point = [ - x: number, - y: number + x: number, + y: number ]; /** * Sequence of one or more connected points on a plane. */ export type Path = Point[]; + +/** + * Type guard for {@link AxialDirection}. + * + * @param val the value. + */ +export function isAxialDirection(val: unknown): val is AxialDirection { + return _.isString(val) + && ["down", "left", "right", "up"].includes(val); +} diff --git a/src/geometry/index.ts b/src/geometry/index.ts index 027cec6..5db8fb4 100644 --- a/src/geometry/index.ts +++ b/src/geometry/index.ts @@ -1,10 +1,18 @@ -import {contact} from "./GeometryUtils"; -import type {Box, Path, Point} from "./geometry-types"; +import { contact, interpolatePath, normalizePath, pointAt, sortPath } from "./GeometryUtils"; +import { isAxialDirection } from "./geometry-types"; + +import type { AxialDirection, Box, Path, Point } from "./geometry-types"; /* Module exports. */ export { - Box, - Path, - Point, - contact + AxialDirection, + Box, + Path, + Point, + contact, + interpolatePath, + isAxialDirection, + normalizePath, + pointAt, + sortPath, }; diff --git a/src/misc/InterpolationUtils.ts b/src/misc/InterpolationUtils.ts new file mode 100644 index 0000000..d3d4013 --- /dev/null +++ b/src/misc/InterpolationUtils.ts @@ -0,0 +1,29 @@ +import _ from "lodash"; + +/** + * Given a list of entries containing some *value* property, pick the one or two entries that are adjacent to a given + * value, returning the index of the *first* adjacent entry and the adjacent entries themselves. + * + * * If `value` exactly matches one of the entries, that entry will be returned. + * * If `value` falls before the first entry, the first entry will be returned. + * * If `value` falls after the last entry, the last entry will be returned. + * * If `value` falls between two entries, those two entries will be returned (preceding then following). + * + * @param entries the entries. + * @param key the key under which the value is stored in each entry. + * @param value the value. + */ +export function pickAdjacent(entries: E[], key: keyof E, value: number): [number, E[]] { + const keyed = _.keyBy(entries, key); + const keys = _.keys(keyed).map(parseFloat).sort(_.subtract); + const uI = keys.findIndex(key => key >= value); + if (-1 === uI) { + return [keys.length - 1, [keyed[keys[keys.length - 1]]]]; + } + const uE = keyed[keys[uI]]; + const { [key]: uV } = uE; + if (uV === value || 0 === uI) { + return [uI, [uE]]; + } + return [uI - 1, [keyed[keys[uI - 1]], uE]]; +} diff --git a/src/misc/index.ts b/src/misc/index.ts new file mode 100644 index 0000000..6a8fcd7 --- /dev/null +++ b/src/misc/index.ts @@ -0,0 +1,6 @@ +import { pickAdjacent } from "./InterpolationUtils"; + +/* Module exports. */ +export { + pickAdjacent, +}; diff --git a/src/performance/PerformanceUtils.ts b/src/performance/PerformanceUtils.ts new file mode 100644 index 0000000..4096747 --- /dev/null +++ b/src/performance/PerformanceUtils.ts @@ -0,0 +1,66 @@ +import { freeze } from "immer"; +import _ from "lodash"; + +import type { AnyUnit, PerformanceCalculator, PerformanceVariables } from "./performance-types"; + +/** + * Convert a value from one unit to another. + * + * @param value the value to convert. + * @param from the unit from which to convert. + * @param to the unit to which to convert. + */ +export function convertUnits(value: number, from: AnyUnit, to: AnyUnit): number { + if (from === to) { + return value; + } + let conv = UNIT_CONVERSIONS[`${from}:${to}`]; + if (null != conv) { + const [proportion, adjustment] = conv; + let converted = value * proportion; + if (null != adjustment) { + converted += adjustment; + } + return converted; + } else { + conv = UNIT_CONVERSIONS[`${to}:${from}`]; + if (null != conv) { + const [proportion, adjustment] = conv; + let converted = value; + if (null != adjustment) { + converted -= adjustment; + } + return converted / proportion; + } + } + throw Error(`No conversion: ${from} to ${to}`); +} + +/** + * Perform unit conversions on a set of variables as needed to match the units of the inputs. + * + * @param vars the variables. + * @param inputs the inputs. + */ +export function convertVariables(vars: PerformanceVariables, inputs: PerformanceCalculator["inputs"]) { + return _.transform(_.entries(vars), (acc, [name, variable]) => { + const input = inputs[name]; + if (null != input) { + const { value, unit } = variable; + acc[name] = { + unit: input.unit, + value: convertUnits(value, unit, input.unit), + }; + } + }, _.cloneDeep(vars)); +} + +/** + * Simple proportion-based unit conversion table. + */ +const UNIT_CONVERSIONS = freeze>({ + "degrees celsius:degrees fahrenheit": [9 / 5, 32], + "feet:meters": [0.3048], + "feet per minute:meters per minute": [0.3048], + "pounds:kilograms": [0.453592], +}, true); diff --git a/src/performance/index.ts b/src/performance/index.ts new file mode 100644 index 0000000..5eceff7 --- /dev/null +++ b/src/performance/index.ts @@ -0,0 +1,29 @@ +import { convertUnits, convertVariables } from "./PerformanceUtils"; +import { isVectorCalculation } from "./performance-types"; +import type { + AltitudeUnit, + AnyUnit, + DistanceUnit, + PerformanceCalculator, + PerformanceVariables, + PowerUnit, + Calculation, + TemperatureUnit, + VectorCalculation, +} from "./performance-types"; + +/* Module exports. */ +export { + AltitudeUnit, + AnyUnit, + DistanceUnit, + PerformanceCalculator, + PerformanceVariables, + PowerUnit, + Calculation, + TemperatureUnit, + VectorCalculation, + convertUnits, + convertVariables, + isVectorCalculation, +}; diff --git a/src/performance/performance-types.ts b/src/performance/performance-types.ts new file mode 100644 index 0000000..e325d30 --- /dev/null +++ b/src/performance/performance-types.ts @@ -0,0 +1,158 @@ +import { Path } from "../geometry"; +import _ from "lodash"; + +/** + * {@link PerformanceCalculator} defines the public interface to an object which wraps a performance chart or grid and + * performs performance calculations based on the data it contains. + */ +export interface PerformanceCalculator { + + /** + * Inputs required by the calculator. + */ + readonly inputs: { + [name in VariableName]: { + unit: AnyUnit; + range: [min: number, max: number]; + } + }; + + /** + * Outputs produced by the calculator. + */ + readonly outputs: { + [name in VariableName]: { + unit: AnyUnit; + } + }; + + /** + * Calculate outputs for a given set of inputs. + * + * @param vars the variables. + */ + calculate(vars: PerformanceVariables): Calculation; +} + +/** + * {@link Calculation} holds the result of a performance calculation. + */ +export interface Calculation { + + /** + * Outputs produced by the calculation *and* inputs variables used in the calculation. + */ + vars: PerformanceVariables; +} + +/** + * {@link VectorCalculation} holds the result of a performance calculation which was performed using vectors across a + * chart, such as an aviation chase-around chart. + */ +export interface VectorCalculation extends Calculation { + + /** + * Guides, such as gridlines and scales, which were used in the calculation. + */ + guides: Path[]; + + /** + * Vectors in the calculation. + */ + vectors: Path[]; +} + +/** + * Any performance-related unit. + */ +export type AnyUnit = + | AltitudeUnit + | DistanceUnit + | PowerUnit + | TemperatureUnit + | VelocityUnit + | WeightUnit; + +/** + * Units in which an altitude may be expressed. + */ +export type AltitudeUnit = + | "feet" + | "meters"; + +/** + * Units in which a distance may be expressed. + */ +export type DistanceUnit = + | "kilometers" + | "nautical miles" + | "statute miles"; + +/** + * Inputs to a performance solver. + */ +export type PerformanceVariables = { + [name in VariableName]: { + unit: AnyUnit; + value: number; + } +}; + +/** + * Units in which engine power may be expressed. + */ +export type PowerUnit = + | "percent"; + +/** + * Units in which a temperature may be expressed. + */ +export type TemperatureUnit = + | "degrees celsius" + | "degrees fahrenheit"; + +/** + * Units in which a velocity may be expressed. + */ +export type VelocityUnit = + | "feet per minute" + | "knots" + | "meters per minute" + | "miles per hour"; + +/** + * Units in which a weight may be expressed. + */ +export type WeightUnit = + | "kilograms" + | "pounds"; + +export function isPerformanceCalculator(val: unknown): val is PerformanceCalculator { + return _.isObject(val) + && "calculate" in val + && "inputs" in val + && "outputs" in val + && _.isFunction(val.calculate) + && _.isObject(val.inputs) + && _.isObject(val.outputs); +} + +/** + * Type guard for {@link VectorCalculation}. + * + * @param val the value. + */ +export function isVectorCalculation(val: unknown): val is VectorCalculation { + return _.isObject(val) + && "guides" in val + && "vars" in val + && "vectors" in val + && _.isArray(val.guides) + && _.isArray(val.vectors) + && _.isObject(val.vars); +} + +/** + * Name of a performance variable. + */ +type VariableName = string; diff --git a/src/tracking/tracking-types.ts b/src/tracking/tracking-types.ts index f7f908c..ff74272 100644 --- a/src/tracking/tracking-types.ts +++ b/src/tracking/tracking-types.ts @@ -1,18 +1,18 @@ -import type {GeoCoordinates, ModeSCode} from "@mattj65817/aviation-js"; +import type { GeoCoordinates, ModeSCode } from "@mattj65817/aviation-js"; /** * Hash of positions keyed on hexadecimal Mode S code. */ export type Positions = { - [K in ModeSCode]: { - altitude: "ground" | number; - coordinates: GeoCoordinates; - track?: number; - velocity?: { - horizontal: number; - vertical?: number; - } - }; + [K in ModeSCode]: { + altitude: "ground" | number; + coordinates: GeoCoordinates; + track?: number; + velocity?: { + horizontal: number; + vertical?: number; + } + }; } /** @@ -20,13 +20,22 @@ export type Positions = { */ export interface PositionService { - /** - * Get current positions for zero or more aircraft based on Mode C hex code. - * - * The returned {@link Positions} object may not contain an entry for *every* aircraft present in the Mode S code - * list; typically only aircraft that are currently in flight (or in a ground-tracked area) are returned. - * - * @param ids the Mode C hex codes. - */ - getPositions(ids: ModeSCode[]): Promise; + /** + * Get current positions for zero or more aircraft based on Mode C hex code. + * + * The returned {@link Positions} object may not contain an entry for *every* aircraft present in the Mode S code + * list; typically only aircraft that are currently in flight (or in a ground-tracked area) are returned. + * + * @param ids the Mode C hex codes. + */ + getPositions(ids: ModeSCode[]): Promise; +} + +/** + * Type guard for {@link ModeSCode}. + * + * @param val the value. + */ +export function isModeSCode(val: unknown): val is ModeSCode { + return typeof val === "string" && /^[0-9a-f]{6}$/.test(val); } diff --git a/tsconfig.json b/tsconfig.json index 3637225..2277a25 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "module": "esnext", "outDir": "./dist/esm", + "resolveJsonModule": true, "target": "esnext" } }