From c354c5a8237533a68eb32154a892234d4be13aa8 Mon Sep 17 00:00:00 2001 From: Thomas Carli Date: Sat, 12 Nov 2022 19:37:03 +0100 Subject: [PATCH 1/3] fix storybook --- .../VerticalProfileChart.tsx | 4 +- .../elevation/ElevationService.server.tsx | 52 +---- .../services/elevation/ElevationService.tsx | 72 ------ remix/app/services/elevation/index.tsx | 2 +- .../requestOpenElevationChunkMemoized.tsx | 55 +++-- .../stories/VerticalProfileChart.stories.tsx | 220 ++++++++++-------- 6 files changed, 170 insertions(+), 235 deletions(-) delete mode 100644 remix/app/services/elevation/ElevationService.tsx diff --git a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx index 0ea3480..0fc2a87 100644 --- a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx +++ b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx @@ -33,7 +33,7 @@ export type SetWaypointAltitude = ({ altitude: number | null; }) => void; -type Props = { +export type VerticalProfileChartProps = { route: Route; elevation: ElevationAtPoint; setWaypointAltitude?: SetWaypointAltitude; @@ -53,7 +53,7 @@ export const VerticalProfileChart = ({ setWaypointAltitude, airspaceOverlaps, fitToSpace, -}: Props) => { +}: VerticalProfileChartProps) => { const { width: availableWidth, ref } = useResizeDetector(); const onDragEnd = 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.stories.tsx b/remix/stories/VerticalProfileChart.stories.tsx index 71beae2..05ae238 100644 --- a/remix/stories/VerticalProfileChart.stories.tsx +++ b/remix/stories/VerticalProfileChart.stories.tsx @@ -1,16 +1,18 @@ -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 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 @@ -21,105 +23,136 @@ export default { 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.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 +160,5 @@ ShortRoute.args = { }, (r) => r, ), - ), - airspaceOverlaps: [], + ); }; From 5abaa245ec2cb9515d39b4652be0fa08bcd5d821 Mon Sep 17 00:00:00 2001 From: Thomas Carli Date: Sun, 13 Nov 2022 15:38:47 +0100 Subject: [PATCH 2/3] hide firebase warning in storybook --- remix/.storybook/preview-head.html | 5 +++++ 1 file changed, 5 insertions(+) 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 @@ + From 27587c287140390b024e1330fee840e2d053e0d1 Mon Sep 17 00:00:00 2001 From: Thomas Carli Date: Sun, 13 Nov 2022 21:14:45 +0100 Subject: [PATCH 3/3] elevation done --- remix/app/fb/components/Map/Colors.ts | 1 + .../VerticalProfileChart.new.tsx | 99 ++++++++++ .../VerticalProfileChart.tsx | 4 +- .../VerticalProfileChart.new.stories.tsx | 176 ++++++++++++++++++ .../stories/VerticalProfileChart.stories.tsx | 26 ++- 5 files changed, 297 insertions(+), 9 deletions(-) create mode 100644 remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.new.tsx create mode 100644 remix/stories/VerticalProfileChart.new.stories.tsx diff --git a/remix/app/fb/components/Map/Colors.ts b/remix/app/fb/components/Map/Colors.ts index c7df454..4d69bb0 100644 --- a/remix/app/fb/components/Map/Colors.ts +++ b/remix/app/fb/components/Map/Colors.ts @@ -11,4 +11,5 @@ export const Colors = { sivThinBorder: '#2b8049', red: '#940000', lightRed: '#FF0000', + terrain: '#883d00bc', }; 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 0fc2a87..81ab800 100644 --- a/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx +++ b/remix/app/fb/components/VerticalProfileChart/VerticalProfileChart.tsx @@ -492,7 +492,7 @@ const hashCode = (s: string) => { function spaceBackgroundColor(type: AirspaceType | DangerZoneType) { return type === AirspaceType.CTR - ? '#99ABD1' + ? '#99ABD17d' : type === DangerZoneType.P ? '#ff00009f' : type === DangerZoneType.D @@ -500,7 +500,7 @@ function spaceBackgroundColor(type: AirspaceType | DangerZoneType) { : type === DangerZoneType.R ? '#ff6a003d' : type === AirspaceType.SIV - ? Colors.sivThickBorder + ? '#7398807d' : '#21003f7d'; } 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 05ae238..5b7752e 100644 --- a/remix/stories/VerticalProfileChart.stories.tsx +++ b/remix/stories/VerticalProfileChart.stories.tsx @@ -3,7 +3,7 @@ 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'; @@ -17,7 +17,7 @@ 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: {}, @@ -71,12 +71,24 @@ export const LongRoute = ( )[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]!, + 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]], }, {