Skip to content

Commit 60cbd4b

Browse files
committed
First commit with all functional
0 parents  commit 60cbd4b

25 files changed

+9682
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
twitteroauth/
2+
aqi/vendor
3+
aqi/composer.json
4+
aqi/composer.lock
5+
__pycache__
6+
.idea
7+
venv/

action.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
require "aqi.php";
3+
require "status.php";
4+
require "painter.php";
5+
require "name.php";
6+
require "avatar.php";
7+
8+
9+
$aqi = currentAQI();
10+
11+
if (!is_numeric($aqi) || is_nan($aqi)) {
12+
exit();
13+
}
14+
15+
createMap();
16+
$mapFilePath = "./outputs/map.png";
17+
$nameAndEmoji = nameFor($aqi);
18+
$statusDetails = statusFor($aqi);
19+
$thaiStatus = statusThFor($aqi);
20+
$engStatus = statusEnFor($aqi);
21+
$avatarFilePath = avatarFor($aqi);
22+
23+
$jsonData = [
24+
"name" => $nameAndEmoji,
25+
"th_en_status" => $statusDetails,
26+
"thai_status" => $thaiStatus,
27+
"eng_status" => $engStatus,
28+
"avatar" => $avatarFilePath,
29+
"map" => $mapFilePath
30+
];
31+
32+
$path = './outputs/aqi-outputs.json';
33+
$jsonString = json_encode($jsonData, JSON_UNESCAPED_UNICODE);
34+
$fp = fopen($path, 'w');
35+
fwrite($fp, $jsonString);
36+
fclose($fp);
37+
38+
?>

aqi.php

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
// Wishlist:
3+
// - river color changes throughout the day (dark at night, light during the day)
4+
// - (accessibility feature) text color turns black when background is too light
5+
define("LIMIT_GOOD", 50);
6+
define("LIMIT_MODERATE", 100);
7+
define("LIMIT_UNHEALTHY_SENSITIVE", 150);
8+
define("LIMIT_UNHEALTHY", 200);
9+
define("LIMIT_VERY_UNHEALTY", 300);
10+
define("AQICN_TOKEN", getenv('AQICN_TOKEN'));
11+
12+
function emojiFor($aqi) {
13+
switch (true) {
14+
case $aqi <= LIMIT_GOOD: return "💚";
15+
case $aqi <= LIMIT_MODERATE: return "💛";
16+
case $aqi <= LIMIT_UNHEALTHY_SENSITIVE: return "🔶";
17+
case $aqi <= LIMIT_UNHEALTHY: return "🛑";
18+
case $aqi <= LIMIT_VERY_UNHEALTY: return "😈";
19+
default: return "👿";
20+
}
21+
}
22+
23+
// returns a color based on the AQI passed
24+
function colorFor($aqi) {
25+
$baseColor = baseColorFor($aqi);
26+
$nextLevelColor = nextLevelColorFor($aqi);
27+
$percentage = percentageFor($aqi);
28+
29+
$newColor = [];
30+
for ($i=0; $i < count($baseColor); $i++) {
31+
$colorComponentsDifference = $nextLevelColor[$i] - $baseColor[$i];
32+
$proposedColor = $baseColor[$i] + round(($percentage / 100) * $colorComponentsDifference);
33+
$newColor[$i] = min(255, max(0, $proposedColor));
34+
}
35+
36+
return $newColor;
37+
}
38+
39+
function baseColorFor($aqi) {
40+
switch (true) {
41+
case $aqi <= LIMIT_GOOD: return [0, 153, 102];
42+
case $aqi <= LIMIT_MODERATE: return [255, 222, 51];
43+
case $aqi <= LIMIT_UNHEALTHY_SENSITIVE: return [255, 153, 51];
44+
case $aqi <= LIMIT_UNHEALTHY: return [204, 0, 51];
45+
case $aqi <= LIMIT_VERY_UNHEALTY: return [102, 0 ,153];
46+
case $aqi > LIMIT_VERY_UNHEALTY: return [126, 0, 35];
47+
default: return [170, 170, 170];
48+
}
49+
}
50+
51+
function nextLevelColorFor($aqi) {
52+
switch (true) {
53+
case $aqi <= LIMIT_GOOD: return [255, 222, 51];
54+
case $aqi <= LIMIT_MODERATE: return [255, 153, 51];
55+
case $aqi <= LIMIT_UNHEALTHY_SENSITIVE: return [204, 0, 51];
56+
case $aqi <= LIMIT_UNHEALTHY: return [102, 0 ,153];
57+
case $aqi <= LIMIT_VERY_UNHEALTY: return [126, 0, 35];
58+
case $aqi > LIMIT_VERY_UNHEALTY: return [0, 0, 0];
59+
}
60+
}
61+
62+
// % to the next limit
63+
// (nextLevelLimit - currentLimitBase) : 100 = (value - currentLimitBase) : x
64+
function percentageFor($aqi) {
65+
switch (true) {
66+
case $aqi <= LIMIT_GOOD: return percentageFormula(LIMIT_GOOD, 0, $aqi);
67+
case $aqi <= LIMIT_MODERATE: return percentageFormula(LIMIT_MODERATE, LIMIT_GOOD, $aqi);
68+
case $aqi <= LIMIT_UNHEALTHY_SENSITIVE: return percentageFormula(LIMIT_UNHEALTHY_SENSITIVE, LIMIT_MODERATE, $aqi);
69+
case $aqi <= LIMIT_UNHEALTHY: return percentageFormula(LIMIT_UNHEALTHY, LIMIT_UNHEALTHY_SENSITIVE, $aqi);
70+
case $aqi <= LIMIT_VERY_UNHEALTY: return percentageFormula(LIMIT_VERY_UNHEALTY, LIMIT_UNHEALTHY, $aqi);
71+
case $aqi > LIMIT_VERY_UNHEALTY: return percentageFormula(1400, LIMIT_VERY_UNHEALTY, $aqi);
72+
}
73+
}
74+
75+
function percentageFormula($nextLimit, $currentLimit, $value) {
76+
return min(100, 100 * ($value - $currentLimit) / ($nextLimit - $currentLimit));
77+
}
78+
79+
function currentAQI() {
80+
$newAQIarray = array_values(getNewAQIForMap());
81+
$filteredArray = [];
82+
foreach ($newAQIarray as $key => $value) {
83+
if (is_numeric($value)) {
84+
array_push($filteredArray, $value);
85+
}
86+
}
87+
return getAverage($filteredArray);
88+
}
89+
90+
function getAverage($array) {
91+
return round(array_sum($array) / count($array));
92+
}
93+
94+
function getCurrentAQIFor($name) {
95+
$url = "https://api.waqi.info/feed/".$name."/?token=" . AQICN_TOKEN;
96+
$json = json_decode(file_get_contents($url), true);
97+
return $json["data"]["aqi"];
98+
}
99+
100+
function getMapAQI() {
101+
$latestAQI = getNewAQIForMap();
102+
$legacyAQI = fetchOldAQIForMap();
103+
return validateAndFilterAQI($latestAQI, $legacyAQI);
104+
}
105+
106+
function storeAQI($data) {
107+
$fp = fopen(__DIR__.'/assets/data.json', 'w');
108+
fwrite($fp, json_encode($data));
109+
fclose($fp);
110+
}
111+
112+
// important note:
113+
// internetAQI is a dictionary like { "x": "value", ... }
114+
// oldAQI is a dictionary like { "x": ["value", "value", "value"], ... }
115+
// returns a dictionary like { "x": "value", ... }
116+
function validateAndFilterAQI($internetAQI, $oldAQI) {
117+
// cleanup from old code
118+
foreach ($oldAQI as $key => $value) {
119+
if (!is_array($value)) {
120+
$oldAQI[$key] = [];
121+
}
122+
}
123+
124+
// the following mess is because PHP can't distinguish between string and integers keys...
125+
$ALLoldAQIValues = array_values($oldAQI);
126+
$ALLoldAQIKeys = array_keys($oldAQI);
127+
128+
$filterAQI = [];
129+
// checking data integrity of the latest fetched data
130+
foreach ($internetAQI as $key => $value) {
131+
$oldAQIValues = $ALLoldAQIValues[array_search($key, $ALLoldAQIKeys)];
132+
133+
if (is_numeric($value) && valueIsValid($value, $oldAQIValues)) {
134+
$filterAQI[$key] = $value;
135+
if (is_array($oldAQIValues)) {
136+
if (count($oldAQIValues) > 2) {
137+
array_pop($oldAQIValues);
138+
}
139+
array_unshift($oldAQIValues, $value);
140+
$oldAQI[$key] = $oldAQIValues;
141+
} else {
142+
$oldAQI[$key] = [$value];
143+
}
144+
}
145+
}
146+
storeAQI($oldAQI);
147+
148+
if (count($filterAQI) == 0) {
149+
$filterAQI[5773] = currentAQI();
150+
}
151+
return $filterAQI;
152+
}
153+
154+
// makes sure that the value is not the same as previous values
155+
function valueIsValid($value, $oldValues) {
156+
// return true if new value is different than the previous one
157+
// or if the previous 3 values are different
158+
159+
return !is_array($oldValues) || count($oldValues) < 3 || $value != $oldValues[0] || ( $oldValues[0] != $oldValues[1] && $oldValues[1] != $oldValues[2] );
160+
}
161+
162+
// Downloads and parses the map AQI json.
163+
// It returns a dictionary with the AQI station idx as keys and the aqi value as the value
164+
// ⚠️ important:
165+
// - both keys and the values at this point are strings
166+
function getNewAQIForMap() {
167+
$url = "https://api.waqi.info/mapq/bounds/?bounds=13,100,14,101&token=" . AQICN_TOKEN;
168+
$json = json_decode(curl_get_contents($url), true);
169+
$array = [];
170+
for ($i = 0; $i < count($json); $i++) {
171+
$obj = $json[$i];
172+
$array[$obj["x"]] = $obj["aqi"];
173+
}
174+
175+
return $array;
176+
}
177+
178+
function curl_get_contents($url) {
179+
$ch = curl_init($url);
180+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
181+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
182+
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
183+
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
184+
$data = curl_exec($ch);
185+
curl_close($ch);
186+
return $data;
187+
}
188+
189+
// just parses the stored json and return the dictionary in it.
190+
function fetchOldAQIForMap() {
191+
$url = __DIR__.'/assets/data.json';
192+
return (array) json_decode(file_get_contents($url));
193+
}
194+
?>

assets/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
map.png
2+
rainbow-*

assets/avatar/good.gif

37.3 KB
Loading

assets/avatar/hazardous.gif

38.2 KB
Loading

assets/avatar/moderate.gif

37.5 KB
Loading

assets/avatar/unhealthy.gif

36.6 KB
Loading

assets/avatar/unhealthySensitive.gif

36 KB
Loading

assets/avatar/veryUnhealthy.gif

37.7 KB
Loading

assets/background.png

665 KB
Loading

assets/data.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"1837":["132","61","61"],"1861":["127","70","70"],"3684":["132","59","59"],"1862":["122","80","80"],"1814":["151","61","61"],"5773":["155","85","85"],"1843":["139","91","91"],"1849":["137","42","42"],"12759":["144","59","59"],"1857":["151","59","70"],"1816":["61","61","74"],"1834":["154","65","65"],"1840":["155","78","78"],"1836":["154","63","63"],"1841":["132","38","53"],"6814":["46","46","34"],"1859":["57","57","74"],"1838":["134","65","65"],"1847":["132","61","61"],"1864":["102","63","63"],"1842":["158","76","76"],"1860":["152","68","68"],"1833":["85","85","63"],"8322":["93","57","78"],"1813":["151","85","85"],"1812":["151","70","70"],"11556":["109","127","164"],"11207":["50","50","46"],"11381":["999","999","53"],"11555":["91","91","93"],"11380":["34","34","30"],"12796":["164","53","53"],"1835":["42","34","34"],"1856":["30","34","34"],"13619":["130","71","71"]}
454 KB
Binary file not shown.

0 commit comments

Comments
 (0)