- { state.weatherStation &&
+ { weatherStation &&
<>
- {state.weatherStationDistance &&
+ {weatherStationDistance &&
hide this for now until implemented*/}
>
}
@@ -200,10 +231,10 @@ export const LocationPicker = () => {
- { selectedLocation && !isEditing
+ { location && !isEditing
?
Stations near
- {state.location?.name}
+ {location?.name}
:
diff --git a/src/constants.ts b/src/constants.ts
index 72cee98..b833808 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -145,3 +145,13 @@ export const kWeatherStationCollectionAttrs = [
},
}
];
+
+export const kOffsetMap = {
+ "-4": "AST",
+ "-5": "EST",
+ "-6": "CST",
+ "-7": "MST",
+ "-8": "PST",
+ "-9": "AKST",
+ "-10": "HST"
+};
diff --git a/src/hooks/use-codap-api.tsx b/src/hooks/use-codap-api.tsx
index a1348f2..3cc3073 100644
--- a/src/hooks/use-codap-api.tsx
+++ b/src/hooks/use-codap-api.tsx
@@ -1,4 +1,4 @@
-import { Attribute, Collection, DataContext, IDataType } from "../types";
+import { Attribute, Collection, DataContext, IDataType, IItem } from "../types";
import { IResult, codapInterface, createItems, getDataContext } from "@concord-consortium/codap-plugin-api";
import { DSCollection1, DSCollection2, DSName } from "../constants";
import { useStateContext } from "./use-state";
@@ -138,17 +138,55 @@ export const useCODAPApi = () => {
return Promise.all(promises);
};
- const arrayify = (value: any) => {
- return Array.isArray(value) ? value : [value];
+ const filterItems = (items: IItem[]) => {
+ const { selectedFrequency, frequencies } = state;
+ const { attrs, filters } = frequencies[selectedFrequency];
+ const filteredItems = items.filter((item: IItem) => {
+ const allFiltersMatch: boolean[] = [];
+ filters.forEach((filter) => {
+ const { attribute, operator } = filter;
+ const attrKey = attrs.find((attr) => attr.name === attribute)?.abbr;
+ if (attrKey) {
+ const itemValue = Number(item[attrKey]);
+ if (operator === "equals") {
+ allFiltersMatch.push(itemValue === filter.value);
+ } else if (operator === "doesNotEqual") {
+ allFiltersMatch.push(itemValue !== filter.value);
+ } else if (operator === "greaterThan") {
+ allFiltersMatch.push(itemValue > filter.value);
+ } else if (operator === "lessThan") {
+ allFiltersMatch.push(itemValue < filter.value);
+ } else if (operator === "greaterThanOrEqualTo") {
+ allFiltersMatch.push(itemValue >= filter.value);
+ } else if (operator === "lessThanOrEqualTo") {
+ allFiltersMatch.push(itemValue <= filter.value);
+ } else if (operator === "between") {
+ const { lowerValue, upperValue } = filter;
+ allFiltersMatch.push(itemValue > lowerValue && itemValue < upperValue);
+ } else if (operator === "top" || operator === "bottom") {
+ const sortedItems = items.sort((a, b) => {
+ return Number(b[attrKey]) - Number(a[attrKey]);
+ });
+ const end = operator === "top" ? filter.value : sortedItems.length;
+ const itemsToCheck = sortedItems.slice(end - filter.value, end);
+ allFiltersMatch.push(itemsToCheck.includes(item));
+ } else if (operator === "aboveMean" || operator === "belowMean") {
+ const mean = items.reduce((acc, i) => acc + Number(i[attrKey]), 0) / items.length;
+ const expression = operator === "aboveMean" ? itemValue > mean : itemValue < mean;
+ allFiltersMatch.push(expression);
+ }
+ }
+ });
+ return allFiltersMatch.every((match) => match === true);
+ });
+ return filteredItems;
};
- const createNOAAItems = async (dataRecords: any, dataTypes: IDataType[]) => {
+ const createNOAAItems = async (items: IItem[], dataTypes: IDataType[]) => {
await updateWeatherDataset(dataTypes);
- const items = arrayify(dataRecords);
// eslint-disable-next-line no-console
- console.log("noaa-cdo ... createNOAAItems with " + dataRecords.length + " case(s)");
+ console.log("noaa-cdo ... createNOAAItems with " + items.length + " case(s)");
await createItems(DSName, items);
-
await codapInterface.sendRequest({
"action": "create",
"resource": "component",
@@ -161,6 +199,7 @@ export const useCODAPApi = () => {
};
return {
+ filterItems,
createNOAAItems
};
};
diff --git a/src/types.ts b/src/types.ts
index 3093a1c..15274a3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -100,6 +100,11 @@ interface IWeatherStationID {
id: string;
}
+export interface ITimeZone {
+ gmtOffset: string;
+ name: string;
+}
+
export interface IState {
location?: IPlace;
weatherStation?: IWeatherStation;
@@ -112,8 +117,7 @@ export interface IState {
endDate?: Date;
units: IUnits;
showModal?: "info" | "data-return-warning";
- stationTimezoneOffset?: number;
- stationTimezoneName?: string;
+ timezone?: ITimeZone;
didUserSelectDate: boolean;
}
@@ -133,7 +137,7 @@ export const dailyMonthlyAttrMap: AttrType[] = [
{name: "Average temperature", abbr: "tAvg", unit: unitMap.temperature},
{name: "Precipitation", abbr: "precip", unit: unitMap.precipitation},
{name: "Snowfall", abbr: "snow", unit: unitMap.precipitation},
- {name: "Average windspeed", abbr: "avgWind", unit: unitMap.speed}
+ {name: "Average wind speed", abbr: "avgWind", unit: unitMap.speed}
];
export const hourlyAttrMap: AttrType[] = [
@@ -148,9 +152,9 @@ export const hourlyAttrMap: AttrType[] = [
export const DefaultState: IState = {
selectedFrequency: "daily",
- frequencies: {hourly: {attrs: [], filters: []},
+ frequencies: {hourly: {attrs: hourlyAttrMap, filters: []},
daily: {attrs: dailyMonthlyAttrMap, filters: []},
- monthly: {attrs: [], filters: []}},
+ monthly: {attrs: dailyMonthlyAttrMap, filters: []}},
units: "standard",
didUserSelectDate: false,
};
@@ -243,3 +247,7 @@ export interface UnitMap {
export interface IRecord {
[key: string]: number | string | Date | IWeatherStation | IFrequency;
}
+
+export interface IItem {
+ [key: string]: string;
+}
diff --git a/src/utils/noaaApiHelper.ts b/src/utils/noaaApiHelper.ts
index 2e5cb96..1d5aa59 100644
--- a/src/utils/noaaApiHelper.ts
+++ b/src/utils/noaaApiHelper.ts
@@ -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";
@@ -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);
@@ -51,7 +50,7 @@ export const decodeData = (iField: string, iValue: any, database: string) => {
};
export const convertNOAARecordToValue = (iRecord: IRecord, weatherStation: IWeatherStation, database: string) => {
- let out: IRecord = {}; // to-do: add interface / type
+ let out: IRecord = {};
Object.keys(iRecord).forEach(function (key: any) {
let value = iRecord[key];
let dataTypeName;
@@ -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);