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
+ ?>
0 commit comments