Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@

Weather report tailored for paramotor pilots. Consolidates data from multiple sources. Worldwide coverage, with extra information within the United States.

1. 🌏 [Open-Meteo](https://Open-Meteo.com/) for international winds aloft and hourly weather forecasts
1. 🌏 [Open-Meteo](https://Open-Meteo.com/) for winds aloft and hourly weather forecasts
2. 🌏 Nearby [Terminal Aerodrome Forecasts](https://aviationweather.gov/gfa/#taf), if available
3. 🌏 Aviation Weather Center [SIGMETs](https://aviationweather.gov/gfa/#sigmet) (international support), [G‑AIRMETs](https://aviationweather.gov/gfa/#gairmet), and [CWAs](https://aviationweather.gov/gfa/#cwa)
4. 🇺🇸 The [NOAA Rapid Refresh Op40 analysis](https://rucsoundings.noaa.gov/)
5. 🇺🇸 NWS [hourly weather forecast](https://www.weather.gov/documentation/services-web-api)
6. 🇺🇸 National Weather Service [active alerts](https://alerts.weather.gov/cap/us.php?x=1)
7. 🇺🇸 Federal Aviation Administration [TFRs](https://tfr.faa.gov)
4. 🇺🇸 NWS [hourly weather forecast](https://www.weather.gov/documentation/services-web-api)
5. 🇺🇸 National Weather Service [active alerts](https://alerts.weather.gov/cap/us.php?x=1)
6. 🇺🇸 Federal Aviation Administration [TFRs](https://tfr.faa.gov)

![Screenshot of PPG.report website](https://user-images.githubusercontent.com/2166114/166601608-42c74bed-7c87-41ef-bd55-0911b470a9c4.png)

Expand Down Expand Up @@ -55,7 +54,6 @@ Using a reverse proxy such as Nginx, configure the following:
- GET `/api/timezone` ➡ `http://api.timezonedb.com/v2.1/get-time-zone` (You will need to attach an API key. Note: This API is only used as a fallback for when the `/api/weather` endpoint fails, or when using Open-Meteo.)
- GET `/api/openmeteo/{proxy+}` ➡ `https://api.open-meteo.com/v1/{proxy}` Get worldwide winds aloft and forecast information
- OPTIONAL endpoints (to further enhance basic global support):
- GET `/api/rap` ➡ `https://rucsoundings.noaa.gov/get_soundings.cgi`
- GET `/api/aviationweather` ➡ `https://www.aviationweather.gov/api/data/taf`
- GET `/api/weather/{proxy+}` ➡ `https://api.weather.gov/{proxy}` Greedy path capturing, forwards to api.weather.gov.
- GET `/api/pqs` ➡ `https://epqs.nationalmap.gov/v1/json` Get United States altitude information for a given geolocation.
Expand All @@ -64,7 +62,7 @@ Using a reverse proxy such as Nginx, configure the following:
- GET `/api/aviationalerts` ➡ self-hosted [aviation-wx](https://github.com/aeharding/aviation-wx)
- **IMPORTANT!** For each outgoing API request, make sure to:
- Attach a `User-Agent` header, as per [NOAA](https://www.weather.gov/documentation/services-web-api) and [Nominatim](https://operations.osmfoundation.org/policies/nominatim/) usage policies.
- **Keep these free APIs free - be a good API consumer!** Add caching for each route - I recommend at least 10 minutes for `rucsoundings.noaa.gov`, and one week for `nominatim.openstreetmap.org`.
- **Keep these free APIs free - be a good API consumer!** Add caching for each route - I recommend at least one week for `nominatim.openstreetmap.org`.

## Linking to ppg.report

Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"emotion": "^11.0.0",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"geolib": "^3.3.4",
"gsl-parser": "^3.0.1",
"i18next": "^25.6.0",
"i18next-browser-languagedetector": "^8.2.0",
"iso8601-duration": "^2.1.3",
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions src/features/rap/Hours.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import ReportElevationDiscrepancy, {
import Extra from "./extra/Extra";
import Scrubber from "./Scrubber";
import { isEqual, startOfHour } from "date-fns";
import ReportStale from "./warnings/ReportStale";
import LocalTimeWarning from "./warnings/LocalTimeWarning";
import Errors from "./Errors";
import { useAppSelector } from "../../hooks";
Expand Down Expand Up @@ -359,7 +358,6 @@ export default function Hours({ hours }: TableProps) {
return (
<>
<ReportElevationDiscrepancy />
<ReportStale />
<LocalTimeWarning />

<Scrubber scrollViewRef={scrollViewRef}>
Expand Down
17 changes: 1 addition & 16 deletions src/features/rap/extra/reportMetadata/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ const YourLocation = styled.div`
${outputP3ColorFromRGB([0, 255, 0], "background")}
`;

const WindsAloft = styled.div`
width: 1rem;
height: 1rem;
border-radius: 2px;
background: rgba(50, 0, 255, 0.3);
border: 2px solid rgb(50, 0, 255);
`;

const NWS = styled.div`
width: 1rem;
height: 1rem;
Expand All @@ -49,22 +41,15 @@ const StyledPlaneSvg = styled(PlaneSvg)`
interface LegendProps {
showTaf: boolean;
showNws: boolean;
showOp40: boolean;
}

export default function Legend({ showTaf, showNws, showOp40 }: LegendProps) {
export default function Legend({ showTaf, showNws }: LegendProps) {
return (
<Container>
<LegendItem>
<YourLocation />
Selected location
</LegendItem>
{showOp40 && (
<LegendItem>
<WindsAloft />
Op40 Winds Aloft Gridpoint (approx)
</LegendItem>
)}
{showNws && (
<LegendItem>
<NWS />
Expand Down
58 changes: 12 additions & 46 deletions src/features/rap/extra/reportMetadata/PointInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,18 @@ export default function PointInfo() {
throw new Error("RAP not defined");
if (!timeZone) throw new Error("timeZone not defined");

const altitudeInM = windsAloft.hours[0].altitudes[0].altitudeInM;

const showOp40 =
typeof windsAloft === "object" && windsAloft.source === "rucSounding";

const aloftSource = (() => {
switch (windsAloft.source) {
case "openMeteo":
return (
<>
<a
href="https://open-meteo.com"
target="_blank"
rel="noopener noreferrer"
>
open-meteo.com
</a>{" "}
/ Best model
</>
);
case "rucSounding":
return (
<>
<a
href="https://rucsoundings.noaa.gov"
target="_blank"
rel="noopener noreferrer"
>
rucsoundings.noaa.gov
</a>{" "}
/ Op40
</>
);
}
})();
const aloftSource = (
<>
<a
href="https://open-meteo.com"
target="_blank"
rel="noopener noreferrer"
>
open-meteo.com
</a>{" "}
/ Best model
</>
);

const hourlySource = (() => {
if (!weather || typeof weather !== "object") return;
Expand Down Expand Up @@ -104,17 +81,6 @@ export default function PointInfo() {
{heightUnitLabel}
</div>
</DataListItem>
{showOp40 && (
<DataListItem>
<div>Winds aloft gridpoint elevation</div>
<div>
{Math.round(
heightValueFormatter(altitudeInM, heightUnit),
).toLocaleString()}
{heightUnitLabel}
</div>
</DataListItem>
)}
<DataListItem>
<div>Winds aloft</div>
<div>{aloftSource}</div>
Expand Down
40 changes: 1 addition & 39 deletions src/features/rap/extra/reportMetadata/ReportMetadata.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import styled from "@emotion/styled";
import { latLng, LatLngExpression, divIcon } from "leaflet";
import { LatLngExpression, divIcon } from "leaflet";
import { useEffect, useRef } from "react";
import {
MapContainer,
GeoJSON,
useMap,
Circle,
FeatureGroup,
Rectangle,
Marker,
} from "react-leaflet";
import { useAppSelector } from "../../../../hooks";
Expand All @@ -17,7 +15,6 @@ import Legend from "./Legend";
import RefreshInformation from "./RefreshInformation";
import { DataList } from "../../../../DataList";
import { outputP3ColorFromRGB } from "../../../../helpers/colors";
import { css } from "@emotion/react";
import OSMAttribution from "../../../../map/OSMAttribution";
import MyPosition from "../../../../map/MyPosition";
import Parallax from "../../../../shared/Parallax";
Expand Down Expand Up @@ -62,12 +59,8 @@ export default function ReportMetadata() {
const aviationWeather = useAppSelector(
(state) => state.weather.aviationWeather,
);
const windsAloft = useAppSelector((state) => state.weather.windsAloft);
const weather = useAppSelector((state) => state.weather.weather);

const showOp40 =
typeof windsAloft === "object" && windsAloft.source === "rucSounding";

return (
<Container>
<Parallax>
Expand Down Expand Up @@ -96,7 +89,6 @@ export default function ReportMetadata() {
showNws={
!!(weather && typeof weather === "object" && "geometry" in weather)
}
showOp40={showOp40}
/>

<StyledDataList>
Expand Down Expand Up @@ -125,17 +117,10 @@ function MapController() {
if (!windsAloft || typeof windsAloft !== "object")
throw new Error("RAP report must be defined");

const showOp40 =
typeof windsAloft === "object" && windsAloft.source === "rucSounding";

const map = useMap();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const groupRef = useRef<any>(null);

const rapPosition: LatLngExpression = [
windsAloft.latitude,
windsAloft.longitude,
];
const airportPosition: LatLngExpression | undefined =
aviationWeather && typeof aviationWeather === "object"
? [aviationWeather.lat, aviationWeather.lon]
Expand All @@ -148,31 +133,8 @@ function MapController() {
});
}, [map, groupRef]);

const bounds = latLng(rapPosition).toBounds(40000); // 13km for op40 analysis

return (
<FeatureGroup ref={groupRef}>
{showOp40 && (
<>
<Rectangle
bounds={bounds}
css={css`
${outputP3ColorFromRGB([0, 0, 255], "fill")}
${outputP3ColorFromRGB([0, 0, 255], "stroke")}
`}
/>
<Circle
center={rapPosition}
fillOpacity={1}
radius={500}
css={css`
${outputP3ColorFromRGB([0, 0, 255], "fill")}
${outputP3ColorFromRGB([0, 0, 255], "stroke")}
`}
/>
</>
)}

{airportPosition && (
<Marker position={airportPosition} icon={planeIcon} pane="markerPane" />
)}
Expand Down
46 changes: 0 additions & 46 deletions src/features/rap/warnings/ReportStale.tsx

This file was deleted.

Loading