Skip to content

Commit

Permalink
Merge pull request #28 from concord-consortium/186898467-get-timezone
Browse files Browse the repository at this point in the history
Set timezone when station is selected. (PT-186898467)
  • Loading branch information
lublagg authored Jan 26, 2024
2 parents 03779b5 + 9e97bca commit ddcb8df
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 37 deletions.
21 changes: 14 additions & 7 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@ export const App = () => {
};

const fetchSuccessHandler = async (data: any) => {
const {stationTimezoneOffset, weatherStation, selectedFrequency, startDate, endDate, units} = state;
if (data && weatherStation) {
const {startDate, endDate, units, selectedFrequency,
weatherStation, timezone} = state;
const allDefined = (startDate && endDate && units && selectedFrequency &&
weatherStation && timezone);

if (data && allDefined) {
const formatDataProps = {
data,
stationTimezoneOffset,
timezone,
weatherStation,
frequency: selectedFrequency,
startDate,
Expand Down Expand Up @@ -102,9 +106,12 @@ export const App = () => {
};

const handleGetData = async () => {
const { location, startDate, endDate, selectedFrequency, weatherStation, stationTimezoneOffset } = state;
const attributes = state.frequencies[selectedFrequency].attrs.map(attr => attr.name);
if (location && attributes && startDate && endDate && weatherStation && selectedFrequency) {
const { location, startDate, endDate, weatherStation, frequencies,
selectedFrequency, timezone } = state;
const attributes = frequencies[selectedFrequency].attrs.map(attr => attr.name);
const allDefined = (startDate && endDate && location && weatherStation && timezone);

if (allDefined) {
const isEndDateAfterStartDate = endDate.getTime() >= startDate.getTime();
if (isEndDateAfterStartDate) {
setStatusMessage("Fetching weather records from NOAA");
Expand All @@ -114,7 +121,7 @@ export const App = () => {
frequency: selectedFrequency,
weatherStation,
attributes,
stationTimezoneOffset
gmtOffset: timezone.gmtOffset
});
try {
const tRequest = new Request(tURL);
Expand Down
58 changes: 41 additions & 17 deletions src/components/location-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import classnames from "classnames";
import { createMap, selectStations } from "../utils/codapHelpers";
import { autoComplete, geoLocSearch } from "../utils/geonameSearch";
import { kStationsCollectionName } from "../constants";
import { kStationsCollectionName, geonamesUser, kOffsetMap, timezoneServiceURL } from "../constants";
import { useStateContext } from "../hooks/use-state";
import { IPlace } from "../types";
import { findNearestActiveStation } from "../utils/getWeatherStations";
Expand All @@ -14,8 +14,8 @@ import CurrentLocationIcon from "../assets/images/icon-current-location.svg";
import "./location-picker.scss";

export const LocationPicker = () => {
const {state, setState} = useStateContext();
const {location, units, weatherStation} = state;
const { state, setState } = useStateContext();
const { location, units, weatherStation, weatherStationDistance } = state;
const [showMapButton, setShowMapButton] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [locationPossibilities, setLocationPossibilities] = useState<IPlace[]>([]);
Expand All @@ -25,16 +25,14 @@ export const LocationPicker = () => {
const locationDivRef = useRef<HTMLDivElement>(null);
const locationInputEl = useRef<HTMLInputElement>(null);
const locationSelectionListEl = useRef<HTMLUListElement>(null);
const selectedLocation = location;
const unitDistanceText = units === "standard" ? "mi" : "km";
const stationDistance = state.weatherStationDistance && units === "standard"
? Math.round((state.weatherStationDistance * 0.6 * 10) / 10)
:state.weatherStationDistance && Math.round(state.weatherStationDistance * 10) / 10;
const stationDistance = weatherStationDistance && units === "standard"
? Math.round((weatherStationDistance * 0.6 * 10) / 10)
: weatherStationDistance && Math.round(weatherStationDistance * 10) / 10;

useEffect(() => {
if (locationInputEl.current?.value === "") {
setShowSelectionList(false);
// setSelectedLocation(undefined);
}
}, [locationInputEl.current?.value]);

Expand All @@ -45,19 +43,45 @@ export const LocationPicker = () => {
}, [isEditing]);

useEffect(() => {
if (selectedLocation) {
findNearestActiveStation(selectedLocation.latitude, selectedLocation.longitude, 80926000, "present")
if (location) {
findNearestActiveStation(location.latitude, location.longitude, 80926000, "present")
.then(({station, distance}) => {
if (station) {
setState((draft) => {
draft.weatherStation = station;
draft.weatherStationDistance = distance;
});

const fetchTimezone = async (lat: number, long: number) => {
let url = `${timezoneServiceURL}?lat=${lat}&lng=${long}&username=${geonamesUser}`;
let res = await fetch(url);
if (res) {
if (res.ok) {
const timezoneData = await res.json();
const { gmtOffset } = timezoneData as { gmtOffset: keyof typeof kOffsetMap };
setState((draft) => {
draft.timezone = {
gmtOffset,
name: kOffsetMap[gmtOffset]
};
});
} else {
console.warn(res.statusText);
}
} else {
console.warn(`Failed to fetch timezone data for ${location}`);
}
};
fetchTimezone(location.latitude, location.longitude);
}
});
} else {
setState((draft) => {
draft.timezone = undefined;
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},[selectedLocation]);
}, [location]);

const getLocationList = () => {
if (locationInputEl.current) {
Expand Down Expand Up @@ -189,13 +213,13 @@ export const LocationPicker = () => {
<div className="location-picker-container">
<div className="location-header">
<span className="location-title">Location</span>
{ selectedLocation && !isEditing &&
{ location && !isEditing &&
<div className="selected-weather-station">
{ state.weatherStation &&
{ weatherStation &&
<>
{state.weatherStationDistance &&
{weatherStationDistance &&
<span className="station-distance">({stationDistance} {unitDistanceText}) </span>}
<span className="station-name">{state.weatherStation?.name}</span>
<span className="station-name">{weatherStation?.name}</span>
{/* <EditIcon /> hide this for now until implemented*/}
</>
}
Expand All @@ -207,10 +231,10 @@ export const LocationPicker = () => {
<div ref={locationDivRef} className={classnames("location-input-wrapper", {"short" : showMapButton, "editing": isEditing})}
onClick={handleLocationInputClick}>
<LocationIcon />
{ selectedLocation && !isEditing
{ location && !isEditing
? <div>
<span className="selected-loc-intro">Stations near </span>
<span className="selected-loc-name">{state.location?.name}</span>
<span className="selected-loc-name">{location?.name}</span>
</div>
: <input ref={locationInputEl} className="location-input" type="text" placeholder={"Enter location or identifier here"}
onChange={handleLocationInputChange} onKeyDown={handleInputKeyDown} onBlur={handleLocationInputBlur}/>
Expand Down
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,13 @@ export const kWeatherStationCollectionAttrs = [
},
}
];

export const kOffsetMap = {
"-4": "AST",
"-5": "EST",
"-6": "CST",
"-7": "MST",
"-8": "PST",
"-9": "AKST",
"-10": "HST"
};
8 changes: 6 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ interface IWeatherStationID {
id: string;
}

export interface ITimeZone {
gmtOffset: string;
name: string;
}

export interface IState {
location?: IPlace;
weatherStation?: IWeatherStation;
Expand All @@ -112,8 +117,7 @@ export interface IState {
endDate?: Date;
units: IUnits;
showModal?: "info" | "data-return-warning";
stationTimezoneOffset?: number;
stationTimezoneName?: string;
timezone?: ITimeZone;
didUserSelectDate: boolean;
}

Expand Down
22 changes: 11 additions & 11 deletions src/utils/noaaApiHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dayjs from "dayjs";
import { IFrequency, IRecord, IUnits, IWeatherStation } from "../types";
import { IFrequency, IRecord, ITimeZone, IUnits, IWeatherStation } from "../types";
import { frequencyToReportTypeMap, nceiBaseURL } from "../constants";
import { dataTypeStore } from "./noaaDataTypes";

Expand All @@ -19,23 +19,22 @@ export const convertUnits = (fromUnitSystem: IUnits, toUnitSystem: IUnits, data:

interface IFormatData {
data: IRecord[];
stationTimezoneOffset?: number;
stationTimezoneName?: string;
units: IUnits;
frequency: IFrequency;
weatherStation: IWeatherStation;
timezone: ITimeZone;
}

export const formatData = (props: IFormatData) => {
const {data, stationTimezoneOffset, stationTimezoneName, units, frequency, weatherStation} = props;
const {data, timezone, units, frequency, weatherStation} = props;
const database = frequencyToReportTypeMap[frequency];
let dataRecords: any[] = [];
data.forEach((r: any) => {
const aValue = convertNOAARecordToValue(r, weatherStation, database);
aValue.latitude = weatherStation.latitude;
aValue.longitude = weatherStation.longitude;
aValue["UTC offset"] = stationTimezoneOffset || "";
aValue.timezone = stationTimezoneName || "";
aValue["UTC offset"] = timezone.gmtOffset;
aValue.timezone = timezone.name;
aValue.elevation = weatherStation.elevation;
aValue["report type"] = frequency;
dataRecords.push(aValue);
Expand Down Expand Up @@ -84,19 +83,20 @@ interface IComposeURL {
frequency: IFrequency;
attributes: string[];
weatherStation: IWeatherStation;
stationTimezoneOffset?: number;
gmtOffset: string;
}

export const composeURL = (props: IComposeURL) => {
const { startDate, endDate, frequency, attributes, weatherStation, stationTimezoneOffset } = props;
const { startDate, endDate, frequency, attributes, weatherStation, gmtOffset } = props;
const database = frequencyToReportTypeMap[frequency];
const format = "YYYY-MM-DDThh:mm:ss";
let sDate = dayjs(startDate);
let eDate = dayjs(endDate);

// adjust for local station time
if (database === "global-hourly" && stationTimezoneOffset) {
sDate = dayjs(startDate).subtract(stationTimezoneOffset, "hour");
eDate = dayjs(endDate).subtract(stationTimezoneOffset, "hour").add(1, "day");
if (database === "global-hourly") {
sDate = dayjs(startDate).subtract(Number(gmtOffset), "hour");
eDate = dayjs(endDate).subtract(Number(gmtOffset), "hour").add(1, "day");
}
const startDateString = dayjs(sDate).format(format);
const endDateString = dayjs(eDate).format(format);
Expand Down

0 comments on commit ddcb8df

Please sign in to comment.