diff --git a/remix/.storybook/preview-head.html b/remix/.storybook/preview-head.html
index 4e77d3c..42d2aaa 100644
--- a/remix/.storybook/preview-head.html
+++ b/remix/.storybook/preview-head.html
@@ -1,2 +1,7 @@
+
diff --git a/remix/app/fb/components/Map/Colors.tsx b/remix/app/fb/components/Map/Colors.tsx
index aef7286..02ddc28 100644
--- a/remix/app/fb/components/Map/Colors.tsx
+++ b/remix/app/fb/components/Map/Colors.tsx
@@ -25,6 +25,8 @@ export const Colors = {
mainThemeColor: '#002e94',
white: '#ffffff',
+
+ terrain: '#883d00bc',
};
ConfigProvider.config({
diff --git a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.new.tsx b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.new.tsx
new file mode 100644
index 0000000..98b99fe
--- /dev/null
+++ b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.new.tsx
@@ -0,0 +1,99 @@
+import _ from 'lodash';
+import { useResizeDetector } from 'react-resize-detector';
+import styled from 'styled-components';
+import type { Route } from '../../../domain';
+import type { AirspaceSegmentOverlap } from '../../../domain/AirspaceIntersection/routeAirspaceOverlaps';
+import type { ElevationAtPoint } from '../../elevationOnRoute';
+import { Colors } from '../Map/Colors';
+
+export type SetWaypointAltitude = ({
+ waypointPosition,
+ altitude,
+}: {
+ waypointPosition: number;
+ altitude: number | null;
+}) => void;
+
+export type VerticalProfileChartProps = {
+ route: Route;
+ elevation: ElevationAtPoint;
+ setWaypointAltitude?: SetWaypointAltitude;
+ airspaceOverlaps: AirspaceSegmentOverlap[];
+ fitToSpace?: boolean;
+};
+
+export const VerticalProfileChart = ({
+ route,
+ elevation,
+ setWaypointAltitude,
+ airspaceOverlaps,
+ fitToSpace,
+}: VerticalProfileChartProps) => {
+ const { width: availableWidth, height: availableHeight, ref } = useResizeDetector();
+
+ // if (availableHeight === undefined) {
+ // return <>>;
+ // }
+
+ const totalDistance = route.totalDistance;
+
+ const widthInPixels = fitToSpace
+ ? undefined
+ : Math.max(availableWidth || 0, totalDistance * 15);
+
+ const minAltitude = _.min(route.waypoints.map((w) => w.altitude));
+ const maxAltitude = _.max(route.waypoints.map((w) => w.altitude));
+ const minElevation = _.min(elevation.elevations);
+ const maxElevation = _.max(elevation.elevations) || 0;
+ const maxY = (_.max([maxElevation, maxAltitude]) || 2000) + 2000;
+ const minY = _.min([minElevation, minAltitude]) || 0;
+
+ const oneFootInPixels = availableHeight / (maxY - minY);
+
+ const oneNmInPixels = widthInPixels / totalDistance;
+
+ console.log('availableHeight', availableHeight);
+ console.log('widthInPixels', widthInPixels);
+ console.log('oneFootInPixels', oneFootInPixels);
+ console.log('oneNmInPixels', oneNmInPixels);
+ console.log('minY - maxY', `${minY} - ${maxY}`);
+
+ const toY = (y: number) => availableHeight + minY * oneFootInPixels - y * oneFootInPixels;
+
+ const d = `M${elevation.distancesFromStartInNm
+ .map((dist, i) => `${dist * oneNmInPixels} ${toY(elevation.elevations[i])}`)
+ .join('L')}L${totalDistance * oneNmInPixels} ${toY(minY)}`;
+
+ return (
+
+
+
+
+
+ );
+};
+
+const ChartOuterContainer = styled.div`
+ height: 100%;
+ overflow-y: scroll;
+`;
+
+const ChartContainer = styled.div`
+ ${({ $width }) => `${$width ? `width: ${$width}px` : 'width: 100%'};`}
+ height: 100%;
+`;
+type ContainerProps = { $width?: number };
diff --git a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx
index 49a3b59..3ba4043 100644
--- a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx
+++ b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx
@@ -34,7 +34,7 @@ export type SetWaypointAltitude = ({
altitude: number | null;
}) => void;
-type Props = {
+export type VerticalProfileChartProps = {
route: Route;
elevation: ElevationAtPoint;
setWaypointAltitude?: SetWaypointAltitude;
@@ -52,7 +52,7 @@ export const VerticalProfileChart = ({
setWaypointAltitude,
airspaceOverlaps,
fitToSpace,
-}: Props) => {
+}: VerticalProfileChartProps) => {
const { width: availableWidth, ref } = useResizeDetector();
const onDragEnd =
@@ -491,7 +491,7 @@ const hashCode = (s: string) => {
function spaceBackgroundColor(type: AirspaceType | DangerZoneType) {
return type === AirspaceType.CTR
- ? '#99ABD1'
+ ? '#99ABD17d'
: type === DangerZoneType.P
? '#ff00009f'
: type === DangerZoneType.D
@@ -499,7 +499,7 @@ function spaceBackgroundColor(type: AirspaceType | DangerZoneType) {
: type === DangerZoneType.R
? '#ff6a003d'
: type === AirspaceType.SIV
- ? Colors.sivThickBorder
+ ? '#7398807d'
: '#21003f7d';
}
diff --git a/remix/app/services/elevation/ElevationService.server.tsx b/remix/app/services/elevation/ElevationService.server.tsx
index e3d64ee..d579c08 100644
--- a/remix/app/services/elevation/ElevationService.server.tsx
+++ b/remix/app/services/elevation/ElevationService.server.tsx
@@ -1,31 +1,9 @@
import retry from 'async-retry';
import _ from 'lodash';
-import memoize from 'memoizee';
-import type { Response } from 'node-fetch';
import { requestOpenElevationChunkMemoized } from './requestOpenElevationChunkMemoized';
-import { sha1 } from './sha1';
export type LatLng = [lat: number, lng: number];
-const requestGoogleChunk = memoize(
- async (chunk: LatLng[]): Promise => {
- return await fetch(
- `https://maps.googleapis.com/maps/api/elevation/json?key=${null}&locations=${chunk
- .map(([lat, lng]) => `${lat},${lng}`)
- .join('|')}`,
- )
- .then((res) => res.json())
- //@ts-ignore
- .then((json) => json.results.map((result) => result.elevation.toFixed(2)));
- },
- {
- promise: true,
- maxAge: 1000 * 60 * 60 * 24, // 1 day
- max: 100,
- normalizer: (args: [LatLng[]]) => sha1(args[0].join(',')),
- },
-);
-
const requestOpenElevationChunkMemoizedWithRetry = async (chunk: LatLng[]) => {
return await retry(async (bail) => await requestOpenElevationChunkMemoized(chunk), {
retries: 3,
@@ -34,15 +12,9 @@ const requestOpenElevationChunkMemoizedWithRetry = async (chunk: LatLng[]) => {
});
};
-async function googleElevationService(latLngs: [lat: number, lng: number][]) {
- const chunks = _.chunk(latLngs, 300);
- console.log(chunks.map((c) => c.length));
- const dataSets = await Promise.all(chunks.map(requestGoogleChunk));
-
- return _.flatten(dataSets);
-}
-
-export async function openElevationApiElevationService(latLngs: [lat: number, lng: number][]) {
+export async function openElevationApiElevationService(
+ latLngs: [lat: number, lng: number][],
+): Promise {
const chunks = _.chunk(latLngs, 300);
const dataSets = await Promise.all(
@@ -52,22 +24,4 @@ export async function openElevationApiElevationService(latLngs: [lat: number, ln
return _.flatten(dataSets);
}
-class HTTPResponseError extends Error {
- response: Response;
-
- constructor(response: Response) {
- super(`HTTP Error Response: ${response.status} ${response.statusText}`);
- this.response = response;
- }
-}
-
-export const checkStatus = (response: Response) => {
- if (response.ok) {
- // response.status >= 200 && response.status < 300
- return response;
- } else {
- throw new HTTPResponseError(response);
- }
-};
-
export const log = () => '';
diff --git a/remix/app/services/elevation/ElevationService.tsx b/remix/app/services/elevation/ElevationService.tsx
deleted file mode 100644
index 47be56e..0000000
--- a/remix/app/services/elevation/ElevationService.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import retry from 'async-retry';
-import _ from 'lodash';
-import memoize from 'memoizee';
-import type { Response } from 'node-fetch';
-import { requestOpenElevationChunkMemoized } from './requestOpenElevationChunkMemoized';
-import { sha1 } from './sha1';
-
-export type LatLng = [lat: number, lng: number];
-
-const requestGoogleChunk = memoize(
- async (chunk: LatLng[]): Promise => {
- return await fetch(
- `https://maps.googleapis.com/maps/api/elevation/json?key=${null}&locations=${chunk
- .map(([lat, lng]) => `${lat},${lng}`)
- .join('|')}`,
- )
- .then((res) => res.json())
- //@ts-ignore
- .then((json) => json.results.map((result) => result.elevation.toFixed(2)));
- },
- {
- promise: true,
- maxAge: 1000 * 60 * 60 * 24, // 1 day
- max: 100,
- normalizer: (args: [LatLng[]]) => sha1(args[0].join(',')),
- },
-);
-
-const requestOpenElevationChunkMemoizedWithRetry = async (chunk: LatLng[]) => {
- console.log('requesting', chunk.length);
- return await retry(async (bail) => await requestOpenElevationChunkMemoized(chunk), {
- retries: 3,
- minTimeout: 500,
- onRetry: () => console.log('retrying'),
- });
-};
-
-async function googleElevationService(latLngs: [lat: number, lng: number][]) {
- const chunks = _.chunk(latLngs, 300);
- console.log(chunks.map((c) => c.length));
- const dataSets = await Promise.all(chunks.map(requestGoogleChunk));
-
- return _.flatten(dataSets);
-}
-
-export async function openElevationApiElevationService(latLngs: [lat: number, lng: number][]) {
- const chunks = _.chunk(latLngs, 300);
-
- const dataSets = await Promise.all(
- chunks.map((chunk) => requestOpenElevationChunkMemoizedWithRetry(chunk)),
- );
-
- return _.flatten(dataSets);
-}
-
-class HTTPResponseError extends Error {
- response: Response;
-
- constructor(response: Response) {
- super(`HTTP Error Response: ${response.status} ${response.statusText}`);
- this.response = response;
- }
-}
-
-export const checkStatus = (response: Response) => {
- if (response.ok) {
- // response.status >= 200 && response.status < 300
- return response;
- } else {
- throw new HTTPResponseError(response);
- }
-};
diff --git a/remix/app/services/elevation/index.tsx b/remix/app/services/elevation/index.tsx
index b4ca1e9..8770c44 100644
--- a/remix/app/services/elevation/index.tsx
+++ b/remix/app/services/elevation/index.tsx
@@ -1,2 +1,2 @@
-export * from './ElevationService';
+export * from './ElevationService.server';
export * from './requestOpenElevationChunkMemoized';
diff --git a/remix/app/services/elevation/requestOpenElevationChunkMemoized.tsx b/remix/app/services/elevation/requestOpenElevationChunkMemoized.tsx
index e175c14..fd131ef 100644
--- a/remix/app/services/elevation/requestOpenElevationChunkMemoized.tsx
+++ b/remix/app/services/elevation/requestOpenElevationChunkMemoized.tsx
@@ -1,29 +1,32 @@
import memoize from 'memoizee';
-import type { LatLng } from './ElevationService';
-import { checkStatus } from './ElevationService';
+import type { Response as NodeFetchResponse } from 'node-fetch';
+import type { LatLng } from './ElevationService.server';
import { sha1 } from './sha1';
+type Result = {
+ latitude: number;
+ longitude: number;
+ elevation: number;
+};
+
const requestOpenElevationChunk = async (chunk: LatLng[]): Promise => {
- //@ts-ignore
- const fetch = (await import('node-fetch')).default;
- console.log(`issuing a request to open elevation`);
- const response = await fetch(`https://api.open-elevation.com/api/v1/lookup`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- locations: chunk.map(([lat, lng]) => ({ latitude: lat, longitude: lng })),
- }),
- });
try {
+ const response = await fetch(`https://api.open-elevation.com/api/v1/lookup`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ locations: chunk.map(([lat, lng]) => ({ latitude: lat, longitude: lng })),
+ }),
+ });
+
const r = checkStatus(response);
- const json = await r.json();
- //@ts-ignore
+ const json = (await r.json()) as { results: Result[] };
return json.results.map((result) => result.elevation.toFixed(2));
} catch (e) {
- // console.log(e);
+ console.error(e);
throw e;
}
};
@@ -37,3 +40,21 @@ export const requestOpenElevationChunkMemoized = memoize(requestOpenElevationChu
return hash;
},
});
+
+const checkStatus = (response: Response | NodeFetchResponse) => {
+ if (response.ok) {
+ // response.status >= 200 && response.status < 300
+ return response;
+ } else {
+ throw new HTTPResponseError(response);
+ }
+};
+
+class HTTPResponseError extends Error {
+ response: Response | NodeFetchResponse;
+
+ constructor(response: Response | NodeFetchResponse) {
+ super(`HTTP Error Response: ${response.status} ${response.statusText}`);
+ this.response = response;
+ }
+}
diff --git a/remix/stories/VerticalProfileChart.new.stories.tsx b/remix/stories/VerticalProfileChart.new.stories.tsx
new file mode 100644
index 0000000..07070a3
--- /dev/null
+++ b/remix/stories/VerticalProfileChart.new.stories.tsx
@@ -0,0 +1,176 @@
+import type { ComponentMeta } from '@storybook/react';
+import { foldW } from 'fp-ts/lib/Either';
+import { pipe } from 'fp-ts/lib/function';
+import { draw } from 'io-ts/lib/Decoder';
+import { useEffect, useState } from 'react';
+import { AiracData, AirspaceType, DangerZoneType, Height } from 'ts-aerodata-france';
+import currentCycle from 'ts-aerodata-france/build/jsonData/2022-10-06.json';
+import { Route } from '~/domain/Route';
+import type { VerticalProfileChartProps } from '~/fb/components/VerticalProfileChart/VerticalProfileChart';
+import { VerticalProfileChart } from '~/fb/components/VerticalProfileChart/VerticalProfileChart.new';
+import type { ElevationAtPoint } from '~/fb/elevationOnRoute';
+import { elevationOnRoute } from '~/fb/elevationOnRoute';
+import { openElevationApiElevationService } from '~/services/elevation';
+import '../app/styles/global.css';
+import longRouteJSON from './route.json';
+import shortRouteJSON from './shortRoute.json';
+
+// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
+export default {
+ title: 'Example/VerticalProfileChartNew',
+ component: VerticalProfileChart,
+ // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
+ argTypes: {},
+} as ComponentMeta;
+
+type LoadedData = { loaded: { airacData: AiracData } };
+
+export const ShortRoute = (
+ args: VerticalProfileChartProps,
+ { loaded: { airacData } }: LoadedData,
+) => {
+ if (!airacData) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export const LongRoute = (
+ args: VerticalProfileChartProps,
+ { loaded: { airacData } }: LoadedData,
+) => {
+ if (!airacData) {
+ return null;
+ }
+
+ console.log('rendering');
+ const route = decodeRoute(airacData)(longRouteJSON);
+ console.log('route', route);
+ return (
+
+ a.type === DangerZoneType.D &&
+ a.lowerLimit.toString().includes('ASFC'),
+ )[1]!,
+ segments: [[100, 110]],
+ },
+ // {
+ // airspace: airacData.dangerZones.filter(
+ // (a) =>
+ // a.type === DangerZoneType.D &&
+ // a.lowerLimit.toString().includes('ASFC'),
+ // )[0]!,
+ // segments: [[50, 90]],
+ // },
+ {
+ airspace: {
+ ...airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.D &&
+ a.lowerLimit.toString().includes('ASFC'),
+ )[0]!,
+ name: 'D 999',
+ lowerLimit: new Height(1000),
+ },
+ segments: [[50, 90]],
+ },
+ {
+ airspace: airacData.airspaces.filter(
+ (a) => a.lowerLimit.feetValue > 5000,
+ )[0]!, // Should not get displayed
+ segments: [[50, 90]],
+ },
+ {
+ airspace: airacData.airspaces.filter(
+ (a) => a.type === AirspaceType.CTR && a.lowerLimit.toString() === 'SFC',
+ )[8]!,
+ segments: [[-100, 30]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[3]!,
+ segments: [[130, 150]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[2]!,
+ segments: [[130, 150]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[4]!,
+ segments: [[130, 150]],
+ },
+ ],
+ }}
+ />
+ );
+};
+
+const RouteC = (args: VerticalProfileChartProps & { airacData: AiracData }) => {
+ const [elevation, setElevation] = useState();
+ console.log('rendering component with airacData', args.airacData);
+ console.log('elevation', args.elevation);
+
+ useEffect(() => {
+ elevationOnRoute({
+ elevationService: {
+ getElevationsForLatLngs: (latLngs) =>
+ openElevationApiElevationService(latLngs.map(({ lat, lng }) => [lat, lng])),
+ },
+ })(args.route).then((e) => setElevation(e));
+ }, []);
+
+ return elevation ? : <>>;
+};
+
+const loaders = [
+ async () => ({
+ airacData: await AiracData.loadCycle(currentCycle),
+ }),
+];
+
+ShortRoute.loaders = loaders;
+LongRoute.loaders = loaders;
+
+const decodeRoute = (airacData: AiracData) => (routeJSON: unknown) => {
+ return pipe(
+ Route.codec(airacData).decode(routeJSON),
+ foldW(
+ (e) => {
+ console.log(draw(e));
+ return Route.empty();
+ },
+ (r) => r,
+ ),
+ );
+};
diff --git a/remix/stories/VerticalProfileChart.stories.tsx b/remix/stories/VerticalProfileChart.stories.tsx
index 71beae2..5b7752e 100644
--- a/remix/stories/VerticalProfileChart.stories.tsx
+++ b/remix/stories/VerticalProfileChart.stories.tsx
@@ -1,125 +1,170 @@
-import type { ComponentMeta, ComponentStory } from '@storybook/react';
+import type { ComponentMeta } from '@storybook/react';
import { foldW } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { draw } from 'io-ts/lib/Decoder';
import { useEffect, useState } from 'react';
-import { AiracData, AirspaceType, DangerZoneType } from 'ts-aerodata-france';
+import { AiracData, AirspaceType, DangerZoneType, Height } from 'ts-aerodata-france';
+import currentCycle from 'ts-aerodata-france/build/jsonData/2022-10-06.json';
import { Route } from '~/domain/Route';
+import type { VerticalProfileChartProps } from '~/fb/components/VerticalProfileChart/VerticalProfileChart';
import { VerticalProfileChart } from '~/fb/components/VerticalProfileChart/VerticalProfileChart';
import type { ElevationAtPoint } from '~/fb/elevationOnRoute';
-import { elevationOnRoute, emptyElevation } from '~/fb/elevationOnRoute';
-import { localApiElevationService } from '~/fb/ElevationService/localApiElevationService';
+import { elevationOnRoute } from '~/fb/elevationOnRoute';
+import { openElevationApiElevationService } from '~/services/elevation';
import '../app/styles/global.css';
-import routeJSON from './route.json';
+import longRouteJSON from './route.json';
import shortRouteJSON from './shortRoute.json';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
- title: 'Example/VerticalProfileChart',
+ title: 'Example/VerticalProfileChart1',
component: VerticalProfileChart,
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {},
} as ComponentMeta;
-const airacData = AiracData.loadCycle(AiracCycles.SEP_08_2022);
-// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
-const Template: ComponentStory = (args) => {
+type LoadedData = { loaded: { airacData: AiracData } };
+
+export const ShortRoute = (
+ args: VerticalProfileChartProps,
+ { loaded: { airacData } }: LoadedData,
+) => {
+ if (!airacData) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export const LongRoute = (
+ args: VerticalProfileChartProps,
+ { loaded: { airacData } }: LoadedData,
+) => {
+ if (!airacData) {
+ return null;
+ }
+
+ console.log('rendering');
+ const route = decodeRoute(airacData)(longRouteJSON);
+ console.log('route', route);
+ return (
+
+ a.type === DangerZoneType.D &&
+ a.lowerLimit.toString().includes('ASFC'),
+ )[1]!,
+ segments: [[100, 110]],
+ },
+ // {
+ // airspace: airacData.dangerZones.filter(
+ // (a) =>
+ // a.type === DangerZoneType.D &&
+ // a.lowerLimit.toString().includes('ASFC'),
+ // )[0]!,
+ // segments: [[50, 90]],
+ // },
+ {
+ airspace: {
+ ...airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.D &&
+ a.lowerLimit.toString().includes('ASFC'),
+ )[0]!,
+ name: 'D 999',
+ lowerLimit: new Height(1000),
+ },
+ segments: [[50, 90]],
+ },
+ {
+ airspace: airacData.airspaces.filter(
+ (a) => a.lowerLimit.feetValue > 5000,
+ )[0]!, // Should not get displayed
+ segments: [[50, 90]],
+ },
+ {
+ airspace: airacData.airspaces.filter(
+ (a) => a.type === AirspaceType.CTR && a.lowerLimit.toString() === 'SFC',
+ )[8]!,
+ segments: [[-100, 30]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[3]!,
+ segments: [[130, 150]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[2]!,
+ segments: [[130, 150]],
+ },
+ {
+ airspace: airacData.dangerZones.filter(
+ (a) =>
+ a.type === DangerZoneType.P &&
+ a.higherLimit.toString().includes('ASFC') &&
+ a.higherLimit.feetValue <= 4000,
+ )[4]!,
+ segments: [[130, 150]],
+ },
+ ],
+ }}
+ />
+ );
+};
+
+const RouteC = (args: VerticalProfileChartProps & { airacData: AiracData }) => {
const [elevation, setElevation] = useState();
+ console.log('rendering component with airacData', args.airacData);
+ console.log('elevation', args.elevation);
useEffect(() => {
elevationOnRoute({
- elevationService: localApiElevationService,
+ elevationService: {
+ getElevationsForLatLngs: (latLngs) =>
+ openElevationApiElevationService(latLngs.map(({ lat, lng }) => [lat, lng])),
+ },
})(args.route).then((e) => setElevation(e));
}, []);
- const newArgs = { ...args, elevation: elevation || emptyElevation };
-
- return ;
+ return elevation ? : <>>;
};
-// export const Primary = Template.bind({});
-export const WithElevation = Template.bind({});
-export const ShortRoute = Template.bind({});
-// More on args: https://storybook.js.org/docs/react/writing-stories/args
-// Primary.args = {
-// route: Route.factory({
-// waypoints: [
-// latLngWaypointFactory({ latLng: { lat: 42, lng: 2.5 }, name: 'WPT 1', altitude: 12 }),
-// latLngWaypointFactory({ latLng: { lat: 42, lng: 3 }, name: 'WPT 2', altitude: 3000 }),
-// latLngWaypointFactory({ latLng: { lat: 42, lng: 3.5 }, name: 'WPT 3', altitude: 123 }),
-// ],
-// }),
-// elevation: emptyElevation,
-// airspaceOverlaps: [],
-// };
+const loaders = [
+ async () => ({
+ airacData: await AiracData.loadCycle(currentCycle),
+ }),
+];
-WithElevation.args = {
- route: pipe(
- Route.codec(airacData).decode(routeJSON),
- foldW(
- (e) => {
- console.log(draw(e));
- return Route.empty();
- },
- (r) => r,
- ),
- ),
- airspaceOverlaps: [
- {
- airspace: airacData.dangerZones.filter(
- (a) => a.type === DangerZoneType.D && a.lowerLimit.toString().includes('ASFC'),
- )[1]!,
- segments: [[100, 110]],
- },
- {
- airspace: airacData.dangerZones.filter(
- (a) => a.type === DangerZoneType.D && a.lowerLimit.toString().includes('ASFC'),
- )[0]!,
- segments: [[50, 90]],
- },
- {
- airspace: airacData.airspaces.filter((a) => a.lowerLimit.feetValue > 5000)[0]!, // Should not get displayed
- segments: [[50, 90]],
- },
- {
- airspace: airacData.airspaces.filter(
- (a) => a.type === AirspaceType.CTR && a.lowerLimit.toString() === 'SFC',
- )[8]!,
- segments: [[-100, 30]],
- },
- {
- airspace: airacData.dangerZones.filter(
- (a) =>
- a.type === DangerZoneType.P &&
- a.higherLimit.toString().includes('ASFC') &&
- a.higherLimit.feetValue <= 4000,
- )[3]!,
- segments: [[130, 150]],
- },
- {
- airspace: airacData.dangerZones.filter(
- (a) =>
- a.type === DangerZoneType.P &&
- a.higherLimit.toString().includes('ASFC') &&
- a.higherLimit.feetValue <= 4000,
- )[2]!,
- segments: [[130, 150]],
- },
- {
- airspace: airacData.dangerZones.filter(
- (a) =>
- a.type === DangerZoneType.P &&
- a.higherLimit.toString().includes('ASFC') &&
- a.higherLimit.feetValue <= 4000,
- )[4]!,
- segments: [[130, 150]],
- },
- ],
-};
+ShortRoute.loaders = loaders;
+LongRoute.loaders = loaders;
-ShortRoute.args = {
- route: pipe(
- Route.codec(airacData).decode(shortRouteJSON),
+const decodeRoute = (airacData: AiracData) => (routeJSON: unknown) => {
+ return pipe(
+ Route.codec(airacData).decode(routeJSON),
foldW(
(e) => {
console.log(draw(e));
@@ -127,6 +172,5 @@ ShortRoute.args = {
},
(r) => r,
),
- ),
- airspaceOverlaps: [],
+ );
};