Skip to content

Commit

Permalink
ClimaCell API v4 (#8)
Browse files Browse the repository at this point in the history
* gets lan/lon from map geo

* climacell v4 endpoints

* Uses ClimaCell API v4, adds Sunrise-Sunset API
  • Loading branch information
elewin authored Jan 29, 2021
1 parent 6456d30 commit 5882f41
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 135 deletions.
18 changes: 9 additions & 9 deletions client/dist/bundle.min.js

Large diffs are not rendered by default.

77 changes: 57 additions & 20 deletions client/src/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export function AppContextProvider({ children }) {
const [customLat, setCustomLat] = useState(null);
const [customLon, setCustomLon] = useState(null);
const [mouseHide, setMouseHide] = useState(false);
const [sunriseTime, setSunriseTime] = useState(null);
const [sunsetTime, setSunsetTime] = useState(null);

/**
* Save mouse hide state
Expand Down Expand Up @@ -307,10 +309,10 @@ export function AppContextProvider({ children }) {
setHourlyWeatherDataErrMsg(null);
const { latitude, longitude } = coords;
const fields = [
"temp",
"precipitation_probability",
"precipitation",
"wind_speed",
"temperature",
"precipitationProbability",
"precipitationIntensity",
"windSpeed",
].join("%2c");

const endTime = new Date(
Expand All @@ -330,7 +332,7 @@ export function AppContextProvider({ children }) {

axios
.get(
`https://api.climacell.co/v3/weather/forecast/hourly?unit_system=si&fields=${fields}&apikey=${weatherApiKey}&lat=${latitude}&lon=${longitude}&end_time=${endTime}`
`https://data.climacell.co/v4/timelines?location=${latitude}%2C${longitude}&fields=${fields}&timesteps=1h&apikey=${weatherApiKey}&endTime=${endTime}`
)
.then((res) => {
if (!res) {
Expand Down Expand Up @@ -365,10 +367,10 @@ export function AppContextProvider({ children }) {
setDailyWeatherDataErrMsg(null);
const { latitude, longitude } = coords;
const fields = [
"temp",
"precipitation_probability",
"precipitation",
"wind_speed",
"temperature",
"precipitationProbability",
"precipitationIntensity",
"windSpeed",
].join("%2c");

const endTime = new Date(
Expand All @@ -387,7 +389,7 @@ export function AppContextProvider({ children }) {
}
axios
.get(
`https://api.climacell.co/v3/weather/forecast/daily?unit_system=si&fields=${fields}&apikey=${weatherApiKey}&lat=${latitude}&lon=${longitude}&end_time=${endTime}`
`https://data.climacell.co/v4/timelines?location=${latitude}%2C${longitude}&fields=${fields}&timesteps=1d&apikey=${weatherApiKey}&endTime=${endTime}`
)
.then((res) => {
if (!res) {
Expand All @@ -407,6 +409,39 @@ export function AppContextProvider({ children }) {
});
}

function updateSunriseSunset(coords) {
return new Promise((resolve, reject) => {
if (!coords) {
setSunriseTime(null);
setSunsetTime(null);
return reject("No coords");
}
const { latitude, longitude } = coords;

axios
.get(
`https://api.sunrise-sunset.org/json?lat=${latitude}&lng=${longitude}&formatted=0`
)
.then((res) => {
const { results } = res?.data;
if (results) {
const { sunrise, sunset } = results;
setSunriseTime(sunrise);
setSunsetTime(sunset);
} else {
setSunriseTime(null);
setSunsetTime(null);
}
resolve(results);
})
.catch((err) => {
setSunriseTime(null);
setSunsetTime(null);
reject(err);
});
});
}

/**
* Updates current weather data
*
Expand All @@ -421,17 +456,15 @@ export function AppContextProvider({ children }) {
setCurrentWeatherDataErrMsg(null);
const { latitude, longitude } = coords;

// https://developer.climacell.co/v3/reference#data-layers-weather
const fields = [
"temp",
"temperature",
"humidity",
"wind_speed",
"precipitation",
"precipitation_type",
"sunrise",
"sunset",
"cloud_cover",
"weather_code",
"windSpeed",
"precipitationIntensity",
"precipitationType",
"precipitationProbability",
"cloudCover",
"weatherCode",
].join("%2c");
return new Promise((resolve, reject) => {
if (!coords) {
Expand All @@ -443,9 +476,10 @@ export function AppContextProvider({ children }) {
setSettingsMenuOpen(true);
return reject("Missing weather API key");
}

axios
.get(
`https://api.climacell.co/v3/weather/realtime?unit_system=si&fields=${fields}&apikey=${weatherApiKey}&lat=${latitude}&lon=${longitude};`
`https://data.climacell.co/v4/timelines?location=${latitude}%2C${longitude}&fields=${fields}&timesteps=current&apikey=${weatherApiKey}`
)
.then((res) => {
if (!res) {
Expand Down Expand Up @@ -572,6 +606,9 @@ export function AppContextProvider({ children }) {
dailyWeatherDataErrMsg,
mouseHide,
saveMouseHide,
updateSunriseSunset,
sunriseTime,
sunsetTime,
};

return (
Expand Down
104 changes: 54 additions & 50 deletions client/src/components/CurrentWeather/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import styles from "./styles.css";
import {
convertTemp,
convertSpeed,
convertLength,
} from "~/services/conversions";

import { InlineIcon } from "@iconify/react";
Expand Down Expand Up @@ -33,29 +32,29 @@ import daySunnyOvercast from "@iconify/icons-wi/day-sunny-overcast";
* @returns {JSX.Element} Current weather conditions component
*/
const CurrentWeather = () => {
const { currentWeatherData, tempUnit, speedUnit, lengthUnit } = useContext(
const { currentWeatherData, tempUnit, speedUnit, sunriseTime, sunsetTime } = useContext(
AppContext
);
if (currentWeatherData) {
const weatherData =
currentWeatherData?.data?.timelines?.[0]?.intervals[0]?.values;
if (weatherData) {
const {
cloud_cover: { value: cloudCover },
humidity: { value: humidity },
precipitation: { value: precipitation },
precipitation_type: { value: precipitationType },
temp: { value: temp },
weather_code: { value: weatherCode },
wind_speed: { value: windSpeed },
sunrise: { value: sunrise },
sunset: { value: sunset },
} = currentWeatherData;

cloudCover,
humidity,
precipitationType,
precipitationProbability,
temperature,
weatherCode,
windSpeed,
} = weatherData;
const daylight = sunriseTime && sunsetTime ? isDaylight(new Date(sunriseTime), new Date(sunsetTime)) : true;
const { icon: weatherIcon, desc: weatherDesc } =
parseWeatherCode(weatherCode, isDaylight(sunrise, sunset)) || {};
parseWeatherCode(weatherCode, daylight) || {};

return (
<div className={styles.container}>
<div className={styles.currentTemp}>
{convertTemp(temp, tempUnit)}
{convertTemp(temperature, tempUnit)}
<InlineIcon icon={degreesIcon} />
</div>
<div className={styles.iconContainer}>
Expand All @@ -65,6 +64,14 @@ const CurrentWeather = () => {
</div>
<div className={styles.stats}>
<div>
<div className={styles.statItem}>
<div>
<InlineIcon
icon={precipitationType === 2 ? snowIcon : rainIcon}
/>
</div>
<div>{precipitationProbability}%</div>
</div>
<div className={styles.statItem}>
<div>
<InlineIcon icon={cloudIcon} />
Expand All @@ -88,17 +95,6 @@ const CurrentWeather = () => {
</div>
<div>{parseInt(humidity)}%</div>
</div>
<div className={styles.statItem}>
<div>
<InlineIcon
icon={precipitationType === "snow" ? snowIcon : rainIcon}
/>
</div>
<div className={styles.textUnit}>
<div>{convertLength(precipitation, lengthUnit)}</div>
<div>{lengthUnit}</div>
</div>
</div>
</div>
</div>
<div className={styles.description}>{weatherDesc || ""}</div>
Expand All @@ -112,61 +108,69 @@ const CurrentWeather = () => {
/**
* Parse weather code
*
* https://docs.climacell.co/reference/data-layers-overview
*
* @param {String} code
* @param {Boolean} [isDay] if it is currently day
* @returns {Object} weather description and icon
*/
const parseWeatherCode = (code, isDay) => {
switch (code) {
case "freezing_rain_heavy":
case 6201:
return { desc: "Heavy freezing rain", icon: isDay ? dayRain : nightRain };
case "freezing_rain":
case 6001:
return { desc: "Freezing rain", icon: isDay ? dayRain : nightRain };
case "freezing_rain_light":
case 6200:
return { desc: "Light freezing rain", icon: isDay ? dayRain : nightRain };
case "freezing_drizzle":
case 6000:
return { desc: "Freezing drizzle", icon: rainMix };
case "ice_pellets_heavy":
case 7101:
return { desc: "Heavy ice pellets", icon: rainMix };
case "ice_pellets":
case 7000:
return { desc: "Ice pellets", icon: rainMix };
case "ice_pellets_light":
case 7102:
return { desc: "Light ice pellets", icon: rainMix };
case "snow_heavy":
case 5101:
return { desc: "Heavy snow", icon: snowIcon };
case "snow":
case 5000:
return { desc: "Show", icon: snowIcon };
case "snow_light":
case 5100:
return { desc: "Light snow", icon: snowIcon };
case "flurries":
case 5001:
return { desc: "Flurries", icon: snowIcon };
case "tstorm":
case 8000:
return { desc: "Thunder storm", icon: thunderstormIcon };
case "rain_heavy":
case 4201:
return { desc: "Heavy rain", icon: isDay ? dayRain : nightRain };
case "rain":
case 4001:
return { desc: "Rain", icon: isDay ? dayRain : nightRain };
case "rain_light":
case 4200:
return { desc: "Light rain", icon: isDay ? dayRain : nightRain };
case "drizzle":
case 4000:
return { desc: "Drizzle", icon: rainMix };
case "fog_light":
case 2100:
return { desc: "Light fog", icon: fogIcon };
case "fog":
case 2000:
return { desc: "Fog", icon: fogIcon };
case "cloudy":
case 1001:
return { desc: "Cloudy", icon: cloudyIcon };
case "mostly_cloudy":
case 1102:
return { desc: "Mostly cloudy", icon: cloudyIcon };
case "partly_cloudy":
case 1101:
return {
desc: "Partly cloudy",
icon: isDay ? daySunnyOvercast : nightAltCloudy,
};
case "mostly_clear":
case 1100:
return { desc: "Mostly clear", icon: isDay ? dayCloudy : nightAltCloudy };
case "clear":
case 1000:
return { desc: "Clear", icon: isDay ? daySunny : nightClear };
case 3001:
return { desc: "Wind", icon: strongWind };
case 3000:
return { desc: "Light wind", icon: strongWind };
case 3002:
return { desc: "Strong wind", icon: strongWind };
}
};

Expand Down
2 changes: 1 addition & 1 deletion client/src/components/CurrentWeather/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
}

.icon-container {
grid-area: weather-icon;
grid-area: weather-icon;
display: flex;
justify-content: flex-start;
align-items: center;
Expand Down
14 changes: 7 additions & 7 deletions client/src/components/LocationName/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import styles from "./styles.css";
* @returns {JSX.Element} Location name
*/
const LocationName = () => {
const { currentWeatherData, reverseGeoApiKey } = useContext(AppContext);
const { mapGeo, reverseGeoApiKey } = useContext(AppContext);
const [name, setName] = useState(null);

useEffect(() => {
if (currentWeatherData && reverseGeoApiKey) {
const { lat, lon } = currentWeatherData;
if (mapGeo && reverseGeoApiKey) {
const { latitude: lat, longitude: lon } = mapGeo;
reverseGeocode({ lat, lon, apiKey: reverseGeoApiKey })
.then((res) => {
setName(getName(res));
Expand All @@ -25,11 +25,11 @@ const LocationName = () => {
setName(`${lat}, ${lon}`);
console.log("err!", err);
});
} else if (currentWeatherData && !reverseGeoApiKey) {
const { lat, lon } = currentWeatherData;
} else if (mapGeo && !reverseGeoApiKey) {
const { latitude: lat, longitude: lon } = mapGeo;
setName(`${lat}, ${lon}`);
}
}, [currentWeatherData, reverseGeoApiKey]);
}, [mapGeo, reverseGeoApiKey]);

return (
<div className={`${styles.container}`}>
Expand All @@ -48,7 +48,7 @@ const LocationName = () => {
* @param {Object} res
* @returns {String} Display name
*/
const getName = (res) => {
const getName = (res) => {
// eslint-disable-next-line babel/camelcase
const { city, country, state, country_code, county, region } = res.address;
// eslint-disable-next-line babel/camelcase
Expand Down
Loading

0 comments on commit 5882f41

Please sign in to comment.