tils-first-weather-widget
Version:
An example development EXB widget (Grant Caroll, spatial-innovation.co.nz)
545 lines (516 loc) • 15.2 kB
text/typescript
import Accessor from "@arcgis/core/core/Accessor.js";
import { property } from "@arcgis/core/core/accessorSupport/decorators.js";
import WeatherResponse from "../model/weather-response";
import esriRequest from "@arcgis/core/request.js";
//Interface for the parameters
export interface IWeatherWidgetParameters {
weatherAPI: string | undefined;
weatherURL: string | undefined;
view: __esri.MapView | __esri.SceneView | undefined;
}
//constant for weather types in Esri JSAPI
export const weatherType = {
SUNNY: 'sunny',
FOGGY: 'foggy',
CLOUDY: 'cloudy',
RAINY: 'rainy',
SNOWY: 'snowy'
}
//interface for conditions returned from weatherAPI.com
interface IWeatherCondition {
code: number,
day: string,
night: string,
icon: number,
esriCode: string
}
//Constant that matches result codes from weatherAPI.com to esri weather type
//rainy,snowy,foggy,sunny,cloudy
export const weatherMatch: Array<IWeatherCondition> = [
{
"code": 1000,
"day": "Sunny",
"night": "Clear",
"icon": 113,
"esriCode": weatherType.SUNNY
},
{
"code": 1003,
"day": "Partly cloudy",
"night": "Partly cloudy",
"icon": 116,
"esriCode": weatherType.SUNNY
},
{
"code": 1006,
"day": "Cloudy",
"night": "Cloudy",
"icon": 119,
"esriCode": weatherType.CLOUDY
},
{
"code": 1009,
"day": "Overcast",
"night": "Overcast",
"icon": 122,
"esriCode": weatherType.CLOUDY
},
{
"code": 1030,
"day": "Mist",
"night": "Mist",
"icon": 143,
"esriCode": weatherType.FOGGY
},
{
"code": 1063,
"day": "Patchy rain possible",
"night": "Patchy rain possible",
"icon": 176,
"esriCode": weatherType.RAINY
},
{
"code": 1066,
"day": "Patchy snow possible",
"night": "Patchy snow possible",
"icon": 179,
"esriCode": weatherType.SNOWY
},
{
"code": 1069,
"day": "Patchy sleet possible",
"night": "Patchy sleet possible",
"icon": 182,
"esriCode": weatherType.SNOWY
},
{
"code": 1072,
"day": "Patchy freezing drizzle possible",
"night": "Patchy freezing drizzle possible",
"icon": 185,
"esriCode": weatherType.SNOWY
},
{
"code": 1087,
"day": "Thundery outbreaks possible",
"night": "Thundery outbreaks possible",
"icon": 200,
"esriCode": weatherType.RAINY
},
{
"code": 1114,
"day": "Blowing snow",
"night": "Blowing snow",
"icon": 227,
"esriCode": weatherType.SNOWY
},
{
"code": 1117,
"day": "Blizzard",
"night": "Blizzard",
"icon": 230,
"esriCode": weatherType.SNOWY
},
{
"code": 1135,
"day": "Fog",
"night": "Fog",
"icon": 248,
"esriCode": weatherType.FOGGY
},
{
"code": 1147,
"day": "Freezing fog",
"night": "Freezing fog",
"icon": 260,
"esriCode": weatherType.FOGGY
},
{
"code": 1150,
"day": "Patchy light drizzle",
"night": "Patchy light drizzle",
"icon": 263,
"esriCode": weatherType.RAINY
},
{
"code": 1153,
"day": "Light drizzle",
"night": "Light drizzle",
"icon": 266,
"esriCode": weatherType.RAINY
},
{
"code": 1168,
"day": "Freezing drizzle",
"night": "Freezing drizzle",
"icon": 281,
"esriCode": weatherType.SNOWY
},
{
"code": 1171,
"day": "Heavy freezing drizzle",
"night": "Heavy freezing drizzle",
"icon": 284,
"esriCode": weatherType.SNOWY
},
{
"code": 1180,
"day": "Patchy light rain",
"night": "Patchy light rain",
"icon": 293,
"esriCode": weatherType.RAINY
},
{
"code": 1183,
"day": "Light rain",
"night": "Light rain",
"icon": 296,
"esriCode": weatherType.RAINY
},
{
"code": 1186,
"day": "Moderate rain at times",
"night": "Moderate rain at times",
"icon": 299,
"esriCode": weatherType.RAINY
},
{
"code": 1189,
"day": "Moderate rain",
"night": "Moderate rain",
"icon": 302,
"esriCode": weatherType.RAINY
},
{
"code": 1192,
"day": "Heavy rain at times",
"night": "Heavy rain at times",
"icon": 305,
"esriCode": weatherType.RAINY
},
{
"code": 1195,
"day": "Heavy rain",
"night": "Heavy rain",
"icon": 308,
"esriCode": weatherType.RAINY
},
{
"code": 1198,
"day": "Light freezing rain",
"night": "Light freezing rain",
"icon": 311,
"esriCode": weatherType.RAINY
},
{
"code": 1201,
"day": "Moderate or heavy freezing rain",
"night": "Moderate or heavy freezing rain",
"icon": 314,
"esriCode": weatherType.RAINY
},
{
"code": 1204,
"day": "Light sleet",
"night": "Light sleet",
"icon": 317,
"esriCode": weatherType.SNOWY
},
{
"code": 1207,
"day": "Moderate or heavy sleet",
"night": "Moderate or heavy sleet",
"icon": 320,
"esriCode": weatherType.SNOWY
},
{
"code": 1210,
"day": "Patchy light snow",
"night": "Patchy light snow",
"icon": 323,
"esriCode": weatherType.SNOWY
},
{
"code": 1213,
"day": "Light snow",
"night": "Light snow",
"icon": 326,
"esriCode": weatherType.SNOWY
},
{
"code": 1216,
"day": "Patchy moderate snow",
"night": "Patchy moderate snow",
"icon": 329,
"esriCode": weatherType.SNOWY
},
{
"code": 1219,
"day": "Moderate snow",
"night": "Moderate snow",
"icon": 332,
"esriCode": weatherType.SNOWY
},
{
"code": 1222,
"day": "Patchy heavy snow",
"night": "Patchy heavy snow",
"icon": 335,
"esriCode": weatherType.SNOWY
},
{
"code": 1225,
"day": "Heavy snow",
"night": "Heavy snow",
"icon": 338,
"esriCode": weatherType.SNOWY
},
{
"code": 1237,
"day": "Ice pellets",
"night": "Ice pellets",
"icon": 350,
"esriCode": weatherType.SNOWY
},
{
"code": 1240,
"day": "Light rain shower",
"night": "Light rain shower",
"icon": 353,
"esriCode": weatherType.RAINY
},
{
"code": 1243,
"day": "Moderate or heavy rain shower",
"night": "Moderate or heavy rain shower",
"icon": 356,
"esriCode": weatherType.RAINY
},
{
"code": 1246,
"day": "Torrential rain shower",
"night": "Torrential rain shower",
"icon": 359,
"esriCode": weatherType.RAINY
},
{
"code": 1249,
"day": "Light sleet showers",
"night": "Light sleet showers",
"icon": 362,
"esriCode": weatherType.RAINY
},
{
"code": 1252,
"day": "Moderate or heavy sleet showers",
"night": "Moderate or heavy sleet showers",
"icon": 365,
"esriCode": weatherType.RAINY
},
{
"code": 1255,
"day": "Light snow showers",
"night": "Light snow showers",
"icon": 368,
"esriCode": weatherType.RAINY
},
{
"code": 1258,
"day": "Moderate or heavy snow showers",
"night": "Moderate or heavy snow showers",
"icon": 371,
"esriCode": weatherType.RAINY
},
{
"code": 1261,
"day": "Light showers of ice pellets",
"night": "Light showers of ice pellets",
"icon": 374,
"esriCode": weatherType.RAINY
},
{
"code": 1264,
"day": "Moderate or heavy showers of ice pellets",
"night": "Moderate or heavy showers of ice pellets",
"icon": 377,
"esriCode": weatherType.RAINY
},
{
"code": 1273,
"day": "Patchy light rain with thunder",
"night": "Patchy light rain with thunder",
"icon": 386,
"esriCode": weatherType.RAINY
},
{
"code": 1276,
"day": "Moderate or heavy rain with thunder",
"night": "Moderate or heavy rain with thunder",
"icon": 389,
"esriCode": weatherType.SNOWY
},
{
"code": 1279,
"day": "Patchy light snow with thunder",
"night": "Patchy light snow with thunder",
"icon": 392,
"esriCode": weatherType.SNOWY
},
{
"code": 1282,
"day": "Moderate or heavy snow with thunder",
"night": "Moderate or heavy snow with thunder",
"icon": 395,
"esriCode": weatherType.SNOWY
}
]
//The viewmodel, extends from Accessor
export default class WeatherWidgetViewModel extends Accessor {
currentWeather: WeatherResponse | undefined;
weatherAPI: string | undefined;
weatherURL: string | undefined;
environment: {} | undefined;
view: __esri.MapView | __esri.SceneView | undefined;
time: string;
constructor(params?: IWeatherWidgetParameters) {
super();
this.currentWeather = undefined;
this.weatherAPI = params?.weatherAPI;
this.weatherURL = params?.weatherURL;
this.view = params?.view;
this.time = "";
}
//Set the environment, only available on a web scene
setEnvironment = () => {
//Use the current time to set the lighting condtions
let lighting = {
type: "sun",
date: this.currentWeather ? new Date(this.currentWeather.location.localtime_epoch * 1000) : new Date(),
directShadowsEnabled: true
};
//set the environment based on the weather.
this.environment = {
lighting: lighting,
weather: this.getWeather()
}
//we can only set the environment on a scene.
if (this.view?.type === "3d") {
let sceneView = this.view as __esri.SceneView;
sceneView.environment = this.environment;
}
}
//Get the percentage of cloud cover
getCloud = () => {
if (this.currentWeather?.current.cloud) {
return this.currentWeather?.current.cloud / 100
}
return 0;
}
getFogCover = () => {
//TODO translate visibility in to a percentage...
return 0.50
}
//get the level of rainfall based on the result from the weatherAPI call.
getPrecipitation = (weatherCondition: IWeatherCondition) => {
if (weatherCondition.day.toLowerCase().indexOf('torrential') > -1 || weatherCondition.night.toLowerCase().indexOf('torrential') > -1) {
return 100;
}
if (weatherCondition.day.toLowerCase().indexOf('heavy') > -1 || weatherCondition.night.toLowerCase().indexOf('heavy') > -1) {
return 90;
}
if (weatherCondition.day.toLowerCase().indexOf('moderate') > -1 || weatherCondition.night.toLowerCase().indexOf('moderate') > -1) {
return 60;
}
if (weatherCondition.day.toLowerCase().indexOf('light') > -1 || weatherCondition.night.toLowerCase().indexOf('light') > -1) {
return 30;
}
if (weatherCondition.day.toLowerCase().indexOf('patchy') > -1 || weatherCondition.night.toLowerCase().indexOf('patchy') > -1) {
return 30;
}
if (weatherCondition.day.toLowerCase().indexOf('drizzle') > -1 || weatherCondition.night.toLowerCase().indexOf('drizzle') > -1) {
return 30;
}
if (weatherCondition.day.toLowerCase().indexOf('thundery') > -1 || weatherCondition.night.toLowerCase().indexOf('thundery') > -1) {
return 30;
}
if (weatherCondition.day.toLowerCase().indexOf('blizzard') > -1 || weatherCondition.night.toLowerCase().indexOf('blizzard') > -1) {
return 100;
}
if (weatherCondition.day.toLowerCase().indexOf('blowing') > -1 || weatherCondition.night.toLowerCase().indexOf('blowing') > -1) {
return 30;
}
if (weatherCondition.day.toLowerCase().indexOf('pellets') > -1 || weatherCondition.night.toLowerCase().indexOf('pellets') > -1) {
return 30;
}
return 0;
}
//get the weather conditons based on the current weather
getWeather = () => {
let weatherCondition = weatherMatch.find(f => {
if (this.currentWeather?.current.condition.code === f.code) {
return f;
}
return undefined;
});
if (!weatherCondition) {
return {
type: weatherType.SUNNY,
cloudCover: this.getCloud()
}
}
switch (weatherCondition.esriCode) {
case weatherType.CLOUDY:
return {
type: weatherType.CLOUDY,
cloudCover: this.getCloud()
}
case weatherType.FOGGY:
return {
type: weatherType.FOGGY,
fogStrength: this.getFogCover()
}
case weatherType.RAINY:
return {
type: weatherType.RAINY,
cloudCover: this.getCloud(),
precipitation: this.getPrecipitation(weatherCondition)
}
case weatherType.SNOWY:
return {
type: weatherType.SNOWY,
snowCover: "enabled",
cloudCover: this.getCloud(),
precipitation: this.getPrecipitation(weatherCondition)
}
case weatherType.SUNNY:
return {
type: weatherType.SUNNY,
cloudCover: this.getCloud()
}
default:
return {
type: weatherType.SUNNY,
cloudCover: this.getCloud()
}
}
}
//call the weather api to get the latest weather
getLatestWeather = (lat: number, long: number): Promise<boolean> => {
let request = `${this.weatherURL}/forecast.json?key=${this.weatherAPI}&q=${lat},${long}&aqi=yes`;
return esriRequest(request, { responseType: "json" }).then(response => {
this.currentWeather = new WeatherResponse(response.data);
this.time = this.currentWeather.location.localtime.split(' ')[1];
this.setEnvironment();
return true;
}).catch(error => {
console.error(error)
return false;
})
}
}