{"version":3,"sources":["environment.ts","query/client.ts","query/Query.tsx","utils.ts","current/WindDirection.tsx","components/Loader.tsx","current/Camera.tsx","current/Current.tsx","charts/utils.ts","charts/TimeAxis.tsx","charts/ChartLoader.tsx","charts/WindChart.tsx","charts/WindDirection.tsx","charts/SimpleChart.tsx","charts/Charts.tsx","SitePicker.tsx","App.tsx","reportWebVitals.ts","index.tsx"],"names":["environment","process","client","TimestreamQuery","region","credentials","accessKeyId","secretAccessKey","defaultState","error","rows","loading","Query","props","currentQuery","loadedRows","onRefresh","query","makeQuery","state","this","prevProps","refreshKey","nextToken","queryPromise","QueryString","NextToken","promise","then","response","onQueryResult","err","onQueryError","setState","message","String","columns","ColumnInfo","Rows","map","row","record","Data","forEach","data","i","Name","concat","children","refresh","React","Component","WIND_DIRECTIONS","getDataKey","match","params","site","WindDirection","wind","legend","ResponsiveContainer","RadarChart","value","direction","PolarGrid","PolarAngleAxis","dataKey","tickFormatter","label","length","Radar","stroke","fill","fillOpacity","Legend","content","Loader","className","rest","classNames","role","Woodside","useState","loaded","setLoaded","window","location","protocol","href","src","alt","onLoad","Camera","useParams","measures","_","range","getRecord","measure","find","ScalarValue","parseFloat","getWindDirections","filter","startsWith","getDominantDirection","windDirections","dominantDirection","reduce","best","current","array","Age","ts","moment","utc","diff","duration","hours","minutes","CurrentBase","renderInterval","setInterval","forceUpdate","clearInterval","ctx","recordGroups","values","groupBy","type","onClick","scope","records","join","trim","renderData","bind","Current","withRouter","findMeasure","measureName","valueName","tickFormats","formatRange","startOf","toISOString","timeAxis","tickFormat","XAxis","padding","right","scale","domain","timeStr","format","ChartLoader","CustomTooltip","active","payload","point","time","Math","ceil","avg","max","DotContext","createContext","undefined","Direction","cx","cy","key","Consumer","context","ARROW_SIZE","x","y","width","height","viewBox","transform","d","WindChartBase","dotContext","directions","directionIndex","maxBy","substr","valueOf","points","transformData","Provider","LineChart","CartesianGrid","strokeDasharray","YAxis","tickCount","Tooltip","formatter","labels","Line","dot","WindChart","COLORS","WindDirectionChartBase","AreaChart","stackOffset","tick","labelFormatter","Area","stackId","WindDirectionChart","SimpleChartBase","measurement","SimpleChart","CHART_LABELS","UNITS","IGNORED","ChartPick","onPick","data-bs-toggle","aria-expanded","aria-labelledby","Object","keys","chart","RANGE_LABELS","RangePick","Charts","newRange","includes","sites","SitePicker","useRouteMatch","router","useHistory","useLayoutEffect","document","title","onChange","e","push","target","entries","path","to","Footer","App","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","getElementById"],"mappings":"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","file":"static/js/main.afa42cdb.chunk.js","sourcesContent":["export const environment = {\r\n region: process.env.REACT_APP_AWS_REGION,\r\n accessKeyId: process.env.REACT_APP_AWS_KEY_ID,\r\n secretAccessKey: process.env.REACT_APP_AWS_KEY,\r\n databaseName: process.env.REACT_APP_DATABASE,\r\n};\r\n","\r\nimport TimestreamQuery from 'aws-sdk/clients/timestreamquery';\r\nimport { environment } from '../environment';\r\n\r\nexport const client = new TimestreamQuery({\r\n region: environment.region,\r\n credentials: {\r\n accessKeyId: environment.accessKeyId,\r\n secretAccessKey: environment.secretAccessKey,\r\n },\r\n});\r\n","import React from 'react';\r\nimport TimestreamQuery from 'aws-sdk/clients/timestreamquery';\r\nimport { client } from './client';\r\n\r\nexport declare namespace Query {\r\n export type Row = Record;\r\n export type Rows = Query.Row[];\r\n export interface Context {\r\n loading: boolean;\r\n data?: Query.Rows;\r\n refresh: () => void;\r\n }\r\n}\r\n\r\n\r\ninterface Props {\r\n refreshKey?: string | number;\r\n query?: string;\r\n children: (ctx?: Query.Context) => React.ReactElement;\r\n}\r\n\r\ninterface State {\r\n rows: Query.Rows;\r\n error: string;\r\n loading: boolean;\r\n}\r\n\r\nconst defaultState: State = {\r\n error: null,\r\n rows: null,\r\n loading: false,\r\n};\r\n\r\nexport class Query extends React.Component {\r\n private currentQuery: Promise = null;\r\n private loadedRows: Query.Rows = null;\r\n\r\n constructor(props: Props) {\r\n super(props);\r\n this.state = {\r\n ...defaultState,\r\n };\r\n }\r\n\r\n componentDidMount() {\r\n this.onRefresh();\r\n }\r\n\r\n componentWillUnmount() {\r\n this.currentQuery = null;\r\n }\r\n\r\n componentDidUpdate(prevProps: Props) {\r\n const { query, refreshKey } = this.props;\r\n if (query !== prevProps.query || refreshKey !== prevProps.refreshKey) {\r\n this.makeQuery(query);\r\n }\r\n }\r\n\r\n private makeQuery(query: string, nextToken?: string): void {\r\n this.currentQuery = null;\r\n\r\n if (!query) {\r\n this.setState(defaultState)\r\n return;\r\n }\r\n\r\n const queryPromise = client.query({\r\n QueryString: query,\r\n NextToken: nextToken,\r\n }).promise();\r\n this.currentQuery = queryPromise;\r\n\r\n queryPromise.then(response => {\r\n if (this.currentQuery !== queryPromise) {\r\n return;\r\n }\r\n\r\n this.onQueryResult(query, response)\r\n }, err => {\r\n if (this.currentQuery !== queryPromise) {\r\n return;\r\n }\r\n\r\n this.onQueryError(err);\r\n });\r\n\r\n if (!nextToken) {\r\n this.loadedRows = [];\r\n this.setState({ error: null, loading: true });\r\n }\r\n }\r\n\r\n private onQueryError(err: Error) {\r\n this.setState({ error: err.message || String(err) || 'Unknown Error', loading: false });\r\n };\r\n\r\n private onQueryResult(query: string, response: TimestreamQuery.Types.QueryResponse) {\r\n if (response.NextToken) {\r\n this.makeQuery(query, response.NextToken);\r\n }\r\n\r\n const columns = response.ColumnInfo;\r\n const rows: Query.Rows = response.Rows.map(row => {\r\n const record: Query.Row = {};\r\n row.Data.forEach((data, i) => record[columns[i].Name || 'unknown'] = data);\r\n return record;\r\n });\r\n\r\n this.loadedRows = this.loadedRows.concat(rows);\r\n\r\n if (!response.NextToken) {\r\n this.setState({\r\n rows: this.loadedRows,\r\n loading: false,\r\n });\r\n this.loadedRows = null;\r\n }\r\n };\r\n\r\n private onRefresh = () => {\r\n const { query } = this.props;\r\n this.makeQuery(query);\r\n };\r\n\r\n render() {\r\n const { children } = this.props;\r\n const { error, rows, loading } = this.state;\r\n return <>\r\n {!error && children({ loading, data: rows, refresh: this.onRefresh })}\r\n {!!error &&
This is broken. Error: {error}
}\r\n ;\r\n }\r\n}\r\n","import type { RouteComponentProps } from 'react-router-dom';\r\n\r\nexport type RecursivePartial = {\r\n [P in keyof T]?: RecursivePartial;\r\n};\r\nexport type RecursiveReadonly = {\r\n readonly [P in keyof T]: RecursiveReadonly;\r\n};\r\n\r\n\r\nexport type UnPromisifiedObject = {[k in keyof T]: UnPromisify};\r\nexport type UnPromisify = T extends Promise ? U : T;\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nexport type AnyFunction = (...args: any[]) => any;\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nexport type AnyAsyncFunction = (...args: any[]) => Promise;\r\n\r\nexport type AsyncReturnType = UnPromisify>;\r\n\r\nexport type WindDirection = 'N'|'NE'|'E'|'SE'|'S'|'SW'|'W'|'NW';\r\n\r\nexport const WIND_DIRECTIONS: WindDirection[] = [\r\n 'N',\r\n 'NE',\r\n 'E',\r\n 'SE',\r\n 'S',\r\n 'SW',\r\n 'W',\r\n 'NW',\r\n];\r\n\r\nexport type TopRouteProps = RouteComponentProps<{ site: string }>;\r\n\r\nexport const getDataKey = (match: { params: { site: string } }) => {\r\n switch(match.params.site) {\r\n case 'woodside': return 'bridal';\r\n default: return match.params.site || 'unknown';\r\n }\r\n};\r\n","import React from 'react';\r\nimport { Radar, RadarChart, PolarGrid, PolarAngleAxis, ResponsiveContainer, Legend } from 'recharts';\r\nimport { WIND_DIRECTIONS } from '../utils';\r\n\r\nexport const WindDirection: React.FC<{ wind: number[], legend?: React.ReactElement }> = ({ wind, legend }) => (\r\n \r\n ({ direction: WIND_DIRECTIONS[direction], value }))}>\r\n \r\n label.length === 1 ? label : ''} />\r\n \r\n\r\n {legend && }\r\n \r\n \r\n )\r\n","import React from 'react';\r\nimport classNames from 'classnames';\r\n\r\nexport type Props = React.DetailedHTMLProps, HTMLDivElement>;\r\n\r\nexport const Loader: React.FC = ({ className, children, ...rest }) => (\r\n
\r\n
\r\n Loading...\r\n
\r\n {children}\r\n
\r\n);\r\n","import React, { useState } from 'react';\r\nimport { useParams } from 'react-router-dom';\r\nimport { Loader } from '../components';\r\n\r\nconst Woodside: React.FC = () => {\r\n const [loaded, setLoaded] = useState(false);\r\n\r\n if (window.location.protocol === 'https:') {\r\n return

\r\n You can also take a look at Woodside Camera.\r\n

;\r\n }\r\n\r\n return (<>\r\n {!loaded && Camera loading...}\r\n Woodside Camera setLoaded(true)}/>\r\n );\r\n}\r\n\r\nexport const Camera: React.FC = () => {\r\n const { site } = useParams<{ site: string }>();\r\n\r\n return (\r\n
\r\n
\r\n {site === 'woodside' && }\r\n
\r\n
);\r\n}\r\n","import React from 'react';\r\nimport _ from 'lodash';\r\nimport moment from 'moment';\r\nimport { environment } from '../environment';\r\nimport { Query } from '../query';\r\nimport { WindDirection } from './WindDirection';\r\nimport { getDataKey, WIND_DIRECTIONS, TopRouteProps } from '../utils';\r\nimport { Loader } from '../components';\r\nimport { withRouter } from 'react-router-dom';\r\nimport { Camera } from './Camera';\r\n\r\nconst RECORDS_BACK = 3;\r\n\r\nconst measures = [\r\n 'wind_speed_10m_avg',\r\n 'wind_speed_10m_max',\r\n 'temperature',\r\n 'pressure',\r\n 'humidity',\r\n ..._.range(8).map(i => `wind_direction${i}_10m`),\r\n];\r\n\r\nfunction getRecord(data: Query.Rows, measure: string): number {\r\n const record = data.find(record => record['measure_name'].ScalarValue === measure);\r\n if (!record) {\r\n return null;\r\n }\r\n return parseFloat(record['value'].ScalarValue);\r\n}\r\n\r\nfunction getWindDirections(data: Query.Rows): number[] {\r\n return data.filter(record => record['measure_name'].ScalarValue.startsWith('wind_direction'))\r\n .map(record => parseFloat(record['value'].ScalarValue));\r\n}\r\nfunction getDominantDirection(windDirections: number[]): string {\r\n const dominantDirection = windDirections.reduce((best, current, i, array) => current > array[best] ? i : best, 0);\r\n return WIND_DIRECTIONS[dominantDirection];\r\n}\r\n\r\nconst Age: React.FC<{ row: Query.Row }> = ({ row }) => {\r\n const ts = moment.utc(row['time'].ScalarValue);\r\n const diff = moment.duration(moment.utc().diff(ts));\r\n\r\n return <>{diff.hours() > 0 && <>{diff.hours()} h} {diff.minutes()} m;\r\n}\r\n\r\nexport class CurrentBase extends React.Component {\r\n private renderInterval: ReturnType;\r\n\r\n componentDidMount() {\r\n this.renderInterval = setInterval(() => this.forceUpdate(), 60 * 1000);\r\n }\r\n\r\n componentWillUnmount() {\r\n clearInterval(this.renderInterval);\r\n }\r\n\r\n renderData(ctx: Query.Context) {\r\n const recordGroups = _.values(_.groupBy(ctx.data, record => record['time'].ScalarValue));\r\n const windDirections = recordGroups.map(getWindDirections);\r\n\r\n return <>\r\n
\r\n
\r\n
\r\n Recent\r\n
\r\n {ctx.loading && }\r\n \r\n
\r\n\r\n \r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n {false && <>\r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n \r\n \r\n {recordGroups.map((records, i) => )}\r\n \r\n }\r\n \r\n
Age
Wind Average (10 min){getRecord(records, 'wind_speed_10m_avg')} km/h
Wind Gust (10 min){getRecord(records, 'wind_speed_10m_max')} km/h
Dominant Direction{getDominantDirection(windDirections[i])}
Temperature{getRecord(records, 'temperature').toFixed(1)} °C
Humidity{getRecord(records, 'humidity')} %
Pressure{getRecord(records, 'pressure')} hPa
\r\n
\r\n
\r\n\r\n \r\n\r\n
\r\n {recordGroups.map((records, i) => (\r\n
\r\n ago}/>\r\n
\r\n ))}\r\n {recordGroups.length === 0 &&
}\r\n
\r\n ;\r\n }\r\n\r\n render() {\r\n const query = `\r\nSELECT\r\ntime,\r\nmeasure_name,\r\nmeasure_value::double as value\r\nFROM ${environment.databaseName}.records\r\nWHERE station = '${getDataKey(this.props.match)}' AND time > ago(12h)\r\nAND measure_name IN (${measures.map(measure => `'${measure}'`).join()})\r\nORDER BY time DESC, measure_name ASC\r\nLIMIT ${measures.length * RECORDS_BACK}\r\n `.trim();\r\n\r\n return
\r\n {this.renderData.bind(this)}\r\n
;\r\n }\r\n}\r\n\r\nexport const Current = withRouter(CurrentBase);\r\n","import { Query } from '../query';\r\n\r\nexport function findMeasure(rows: Query.Rows, measureName: string, valueName: string = 'value') {\r\n const measure = rows.find(record => record['name'].ScalarValue === measureName);\r\n return measure && parseFloat(measure[valueName].ScalarValue);\r\n}\r\n\r\nexport type Range = 'today' | '1d' | '2d' | '7d';\r\n","import React from 'react';\r\nimport moment from 'moment';\r\nimport { Range } from './utils';\r\nimport { XAxis } from 'recharts';\r\n\r\nconst tickFormats = {\r\n 'today': 'HH:mm',\r\n '1d': 'HH:mm',\r\n '2d': 'dd HH:mm',\r\n '7d': 'dd HH:mm',\r\n};\r\n\r\nexport const formatRange = (range: string) => {\r\n if (range === 'today') {\r\n return `from_iso8601_timestamp('${moment().startOf('day').toISOString()}')`;\r\n }\r\n return `ago(${range})`;\r\n}\r\n\r\n// recharts do not support wrapping components for some reason\r\n// https://github.com/recharts/recharts/issues/412\r\nexport const timeAxis = (range: Range) => {\r\n const tickFormat = tickFormats[range];\r\n return ( moment(timeStr).format(tickFormat)}\r\n />);\r\n}\r\n","import React from 'react';\r\nimport { Loader } from '../components';\r\n\r\nexport const ChartLoader: React.FC = () => ;\r\n","import React, { createContext } from 'react';\r\nimport _ from 'lodash';\r\nimport moment from 'moment';\r\nimport { CartesianGrid, DotProps, Legend, Line, LineChart, ResponsiveContainer, Tooltip, TooltipProps, YAxis } from 'recharts';\r\nimport { withRouter } from 'react-router-dom';\r\n\r\nimport { environment } from '../environment';\r\nimport { Query } from '../query';\r\nimport { getDataKey, WIND_DIRECTIONS, TopRouteProps } from '../utils';\r\nimport { findMeasure, Range } from './utils';\r\nimport { formatRange, timeAxis } from './TimeAxis';\r\nimport { ChartLoader } from './ChartLoader';\r\n\r\nconst measures = [\r\n 'wind_speed_1h_avg',\r\n 'wind_speed_1h_max',\r\n ..._.range(8).map(i => `wind_direction${i}_1h`),\r\n];\r\n\r\ninterface Point {\r\n time: number;\r\n avg: number;\r\n max: number;\r\n direction: number;\r\n}\r\n\r\ninterface Props extends TopRouteProps {\r\n refreshKey?: number | string;\r\n range: Range;\r\n}\r\n\r\nconst CustomTooltip: React.FC> = ({ active, payload }) => {\r\n if (!active || !payload) {\r\n return null;\r\n }\r\n\r\n const point = payload[0].payload as Point;\r\n return (\r\n
\r\n
{moment(point.time).format('llll')}
\r\n
Average: {Math.ceil(point.avg)} km/h
\r\n
Gust: {Math.ceil(point.max)} km/h
\r\n
Direction: {WIND_DIRECTIONS[point.direction]}
\r\n
\r\n );\r\n};\r\n\r\nconst ARROW_SIZE = 30;\r\nconst DotContext = createContext<{\r\n cx: number;\r\n cy: number;\r\n}>(undefined);\r\n\r\nconst Direction: React.FC<{ payload: Point } & DotProps> = ({ cx, cy, payload, key }) => (\r\n {context => {\r\n if ((cx - context.cx) ** 2 + (cy - context.cy) ** 2 < (ARROW_SIZE * 0.75) ** 2) {\r\n return null;\r\n }\r\n context.cx = cx;\r\n context.cy = cy;\r\n\r\n return \r\n \r\n \r\n \r\n ;\r\n }});\r\n\r\nclass WindChartBase extends React.Component {\r\n transformData(data: Query.Rows): Point[] { // TODO: memoize\r\n const points = _.values(_.groupBy(data, record => record['time'].ScalarValue))\r\n .map((records): Point => {\r\n const directions = records.filter(record => record['name'].ScalarValue.startsWith('wind_direction'));\r\n const dominantDirection = _.maxBy(directions, record => +record['value'].ScalarValue);\r\n const directionIndex = +dominantDirection['name'].ScalarValue.substr('wind_direction'.length, 1)\r\n return {\r\n time: moment.utc(records[0]['time'].ScalarValue).valueOf(),\r\n avg: findMeasure(records, 'wind_speed_1h_avg'),\r\n max: findMeasure(records, 'wind_speed_1h_max'),\r\n direction: directionIndex,\r\n };\r\n });\r\n return points;\r\n }\r\n\r\n readonly dotContext = { cx: 0, cy: 0 };\r\n\r\n renderData(ctx: Query.Context) {\r\n const { range } = this.props;\r\n const points = this.transformData(ctx.data);\r\n return \r\n {ctx.loading && }\r\n \r\n \r\n \r\n {timeAxis(range)}\r\n \r\n \r\n WindChartBase.labels[key]}/>\r\n \r\n \r\n \r\n \r\n ;\r\n }\r\n\r\n static labels: Record = {\r\n 'avg': 'Average Wind [km/h]',\r\n 'max': 'Gust [km/h]',\r\n }\r\n\r\n render() {\r\n const { range, refreshKey, match } = this.props;\r\n\r\n const query = `\r\nSELECT\r\nelement_at(array_agg(time), -1) as time,\r\nmeasure_name as name,\r\nelement_at(array_agg(measure_value::double), -1) as value\r\nFROM ${environment.databaseName}.records\r\nWHERE station = '${getDataKey(match)}' AND time > ${formatRange(range)}\r\nAND measure_name IN (${measures.map(measure => `'${measure}'`).join()})\r\nGROUP BY bin(time, 1h), measure_name\r\nORDER BY 1\r\n `.trim();\r\n\r\n return {this.renderData.bind(this)};\r\n }\r\n}\r\n\r\nexport const WindChart = withRouter(WindChartBase);\r\n","import React from 'react';\r\nimport _ from 'lodash';\r\nimport moment from 'moment';\r\nimport { Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, YAxis } from 'recharts';\r\nimport { withRouter } from 'react-router-dom';\r\n\r\nimport { environment } from '../environment';\r\nimport { Query } from '../query';\r\nimport { getDataKey, WindDirection, WIND_DIRECTIONS, TopRouteProps } from '../utils';\r\nimport { Range } from './utils';\r\nimport { formatRange, timeAxis } from './TimeAxis';\r\nimport { ChartLoader } from './ChartLoader';\r\n\r\nconst measures = _.range(8).map(i => `wind_direction${i}_1h`);\r\nconst COLORS = [\r\n '#F55A00',\r\n '#F51818',\r\n '#F50CF5',\r\n '#0C21F5',\r\n '#0AB8F5',\r\n '#18F557',\r\n '#D6F518',\r\n '#F5C518',\r\n];\r\n\r\ninterface Point extends Record {\r\n time: number;\r\n}\r\n\r\ninterface Props extends TopRouteProps {\r\n refreshKey?: number | string;\r\n range: Range;\r\n}\r\n\r\nexport class WindDirectionChartBase extends React.Component {\r\n transformData(data: Query.Rows): Point[] { // TODO: memoize\r\n const points = _.values(_.groupBy(data, record => record['time'].ScalarValue))\r\n .map((records): Point => {\r\n const directions = records.reduce((rest, record) => {\r\n const directionIndex = +record['name'].ScalarValue.substr('wind_direction'.length, 1);\r\n return {\r\n ...rest,\r\n [WIND_DIRECTIONS[directionIndex]]: +record['value'].ScalarValue,\r\n };\r\n }, {} as Record);\r\n return {\r\n time: moment.utc(records[0]['time'].ScalarValue).valueOf(),\r\n ...directions,\r\n };\r\n });\r\n return points;\r\n }\r\n\r\n renderData(ctx: Query.Context) {\r\n const { range } = this.props;\r\n const points = this.transformData(ctx.data);\r\n return <>\r\n {ctx.loading && }\r\n \r\n \r\n \r\n {timeAxis(range)}\r\n `${tick * 100}%`} tickCount={5} width={40}/>\r\n \r\n `${value}%`}\r\n labelFormatter={(value: number) => moment(value).format('llll')}/>\r\n {WIND_DIRECTIONS.map((direction, i) =>\r\n \r\n )}\r\n \r\n \r\n ;\r\n }\r\n\r\n render() {\r\n const { range, refreshKey, match } = this.props;\r\n\r\n const query = `\r\nSELECT\r\nelement_at(array_agg(time), -1) as time,\r\nmeasure_name as name,\r\nelement_at(array_agg(measure_value::double), -1) as value\r\nFROM ${environment.databaseName}.records\r\nWHERE station = '${getDataKey(match)}' AND time > ${formatRange(range)}\r\nAND measure_name IN (${measures.map(measure => `'${measure}'`).join()})\r\nGROUP BY bin(time, 1h), measure_name\r\nORDER BY 1\r\n `.trim();\r\n\r\n return {this.renderData.bind(this)};\r\n }\r\n}\r\n\r\nexport const WindDirectionChart = withRouter(WindDirectionChartBase);\r\n","import React from 'react';\r\nimport moment from 'moment';\r\nimport { withRouter } from 'react-router-dom';\r\nimport { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, YAxis } from 'recharts';\r\n\r\nimport { environment } from '../environment';\r\nimport { Query } from '../query';\r\nimport { Range } from './utils';\r\nimport { formatRange, timeAxis } from './TimeAxis';\r\nimport { ChartLoader } from './ChartLoader';\r\nimport { getDataKey, TopRouteProps } from '../utils';\r\n\r\ninterface Point {\r\n time: number;\r\n value: number;\r\n}\r\n\r\ninterface Props extends TopRouteProps {\r\n refreshKey?: number | string;\r\n measurement: string;\r\n range: Range;\r\n label?: string;\r\n}\r\n\r\nclass SimpleChartBase extends React.Component {\r\n transformData(data: Query.Rows): Point[] { // TODO: memoize\r\n return data?.map((record): Point => ({\r\n time: moment.utc(record['time'].ScalarValue).valueOf(),\r\n value: parseFloat(record['value'].ScalarValue),\r\n }));\r\n }\r\n\r\n renderData(ctx: Query.Context) {\r\n const { range, measurement, label } = this.props;\r\n const points = this.transformData(ctx.data);\r\n return <>\r\n {ctx.loading && }\r\n \r\n \r\n \r\n {timeAxis(range)}\r\n \r\n label || measurement}/>\r\n moment(value).format('llll')}/>\r\n \r\n \r\n \r\n ;\r\n }\r\n\r\n render() {\r\n const { range, refreshKey, measurement, match } = this.props;\r\n\r\n const query = `\r\nSELECT\r\ntime,\r\nmeasure_name as name,\r\nmeasure_value::double as value\r\nFROM ${environment.databaseName}.records\r\nWHERE station = '${getDataKey(match)}' AND time > ${formatRange(range)}\r\nAND measure_name = '${measurement}'\r\nORDER BY time\r\n `.trim();\r\n\r\n return {this.renderData.bind(this)};\r\n }\r\n}\r\n\r\nexport const SimpleChart = withRouter(SimpleChartBase);\r\n","/* eslint-disable jsx-a11y/anchor-is-valid */\r\nimport React from 'react';\r\nimport { SimpleChart } from './SimpleChart';\r\nimport { Range } from './utils';\r\nimport { WindChart } from './WindChart';\r\nimport { WindDirectionChart } from './WindDirection';\r\n\r\ntype AllCharts = 'wind' | 'direction' | 'temperature' | 'pressure' | 'humidity' | 'precipitation' | 'solar_voltage' | 'battery_voltage2' | 'battery_temperature_max';\r\n\r\ntype Props = unknown;\r\n\r\ninterface State {\r\n current: AllCharts;\r\n range: Range;\r\n refreshKey: number;\r\n}\r\n\r\nconst CHART_LABELS = {\r\n 'wind': 'Wind',\r\n 'direction': 'Wind Direction',\r\n 'temperature': 'Temperature',\r\n 'pressure': 'Pressure',\r\n 'humidity': 'Humidity',\r\n 'precipitation': 'Precipitation',\r\n 'solar_voltage': 'Solar Voltage',\r\n 'battery_voltage2': 'Battery Voltage',\r\n 'battery_temperature_max': 'Battery Temperature',\r\n};\r\nconst UNITS: Partial> = {\r\n 'temperature': '°C',\r\n 'pressure': 'hPa',\r\n 'humidity': '%',\r\n 'precipitation': 'mm',\r\n 'solar_voltage': 'V',\r\n 'battery_voltage2': 'V',\r\n 'battery_temperature_max': '°C',\r\n};\r\nconst IGNORED: Partial> = {\r\n 'temperature': true,\r\n 'pressure': true,\r\n 'humidity': true,\r\n 'precipitation': true,\r\n};\r\n\r\nconst ChartPick: React.FC<{ current: AllCharts, onPick: (chart: AllCharts) => void }> = ({ current, onPick }) => (\r\n
\r\n \r\n \r\n
\r\n);\r\n\r\nconst RANGE_LABELS = {\r\n 'today': 'Today',\r\n '1d': '1 day',\r\n '2d': '2 days',\r\n '7d': '1 week',\r\n}\r\n\r\nconst RangePick: React.FC<{ range: Range, onPick: (chart: Range) => void }> = ({ range, onPick }) => (\r\n
\r\n \r\n \r\n
\r\n);\r\n\r\nexport class Charts extends React.Component {\r\n constructor(props: Props) {\r\n super(props);\r\n\r\n this.state = {\r\n current: 'wind',\r\n range: 'today',\r\n refreshKey: 0,\r\n };\r\n }\r\n\r\n render() {\r\n const { current, range, refreshKey } = this.state;\r\n return (
\r\n
\r\n
\r\n Charts\r\n
\r\n
\r\n
\r\n this.setState({ current: chart })}/>\r\n this.setState({ range: newRange })}/>\r\n \r\n
\r\n
\r\n
\r\n
\r\n {current === 'wind' && }\r\n {current === 'direction' && }\r\n {[\r\n 'temperature',\r\n 'pressure',\r\n 'humidity',\r\n 'precipitation',\r\n 'battery_voltage2',\r\n 'solar_voltage',\r\n 'battery_temperature_max',\r\n ].includes(current) &&\r\n \r\n }\r\n
\r\n
\r\n
);\r\n }\r\n}\r\n","import React, { useLayoutEffect } from 'react';\r\nimport { useRouteMatch } from 'react-router';\r\nimport { Redirect, Route, Switch, useHistory } from 'react-router-dom';\r\n\r\nconst sites: Record = {\r\n 'woodside': 'Mt. Woodside',\r\n // 'bridal-falls-lz': 'Bridal Falls LZ',\r\n};\r\n\r\nexport const SitePicker: React.FC = () => {\r\n const { params: { site } } = useRouteMatch<{ site: string }>();\r\n const router = useHistory();\r\n\r\n useLayoutEffect(() => {\r\n document.title = `${sites[site]} Weather Station`;\r\n }, [site]);\r\n\r\n return (\r\n
\r\n
\r\n \r\n \r\n {Object.keys(sites).map(key => )}\r\n \r\n \r\n
\r\n
\r\n );\r\n};\r\n","import React from 'react';\r\nimport {\r\n BrowserRouter as Router,\r\n Switch,\r\n Route,\r\n Redirect,\r\n} from 'react-router-dom';\r\nimport { Current } from './current';\r\nimport { Charts } from './charts';\r\nimport { SitePicker } from './SitePicker';\r\n\r\nconst Footer: React.FC = () => (<>\r\n
\r\n \r\n Disclaimer: The data are provided without any warranty. Do not rely on the data to assess the risk of flying.\r\n \r\n
\r\n
\r\n \r\n Camera image kindly provided by Richard.\r\n \r\n
\r\n);\r\n\r\nfunction App(): React.ReactElement {\r\n return (\r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n );\r\n}\r\n\r\nexport default App;\r\n","import { ReportHandler } from 'web-vitals';\r\n\r\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\r\n if (onPerfEntry && onPerfEntry instanceof Function) {\r\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\r\n getCLS(onPerfEntry);\r\n getFID(onPerfEntry);\r\n getFCP(onPerfEntry);\r\n getLCP(onPerfEntry);\r\n getTTFB(onPerfEntry);\r\n });\r\n }\r\n};\r\n\r\nexport default reportWebVitals;\r\n","import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\nimport 'bootstrap';\r\nimport './index.scss';\r\nimport App from './App';\r\nimport reportWebVitals from './reportWebVitals';\r\n\r\nReactDOM.render(\r\n \r\n \r\n ,\r\n document.getElementById('root')\r\n);\r\n\r\n// If you want to start measuring performance in your app, pass a function\r\n// to log results (for example: reportWebVitals(console.log))\r\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\r\nreportWebVitals();\r\n"],"sourceRoot":""}