Module: PM25

Defined in:
lib/pm_25.rb,
lib/pm_25/version.rb

Constant Summary collapse

API_base =

Yes, pm25.in requires a token but uses http!

'http://www.pm25.in/api/querys/'
VERSION =
"0.0.2"

Class Method Summary collapse

Class Method Details

.access_api(interface, params = {}) ⇒ Hash, Fixnum

TODO pm25.in tends to 502, need to email complaints and handle this.

Parameters:

  • interface (String)

    of api

  • params (Hash) (defaults to: {})

    (additional) options

Returns:

  • (Hash)

    result

  • (Fixnum)

    error code



59
60
61
62
63
64
65
66
67
# File 'lib/pm_25.rb', line 59

def access_api(interface, params={})
  params[:token] ||= get_token
  res = RestClient.get(API_base + interface, {params: params})
  if res.code == 200
    JSON.parse res.body
  else
    res.code
  end
end

.all_cities(token = nil) ⇒ Hash, Fixnum

Get PM 2.5 data for all cities. API frequency limit: 5 per hour.

Parameters:

  • token (String) (defaults to: nil)

Returns:

  • (Hash)
  • (Fixnum)

    error code



157
158
159
# File 'lib/pm_25.rb', line 157

def all_cities(token=nil)
  access_api('all_cities.json', token: token)
end

.aqi_ranking(token = nil) ⇒ Hash, Fixnum

Get average data for all cities. (Cities are sorted by AQI.) API frequency limit: 15 per hour.

Parameters:

  • token (String) (defaults to: nil)

Returns:

  • (Hash)
  • (Fixnum)

    error code



166
167
168
# File 'lib/pm_25.rb', line 166

def aqi_ranking(token=nil)
  access_api('aqi_ranking.json', token: token)
end

.available_cities(token = get_token) ⇒ Array, Fixnum

Get a list of cites providing PM 2.5 data. API frequency limit: 10 per hour.

Parameters:

  • token (String) (defaults to: get_token)

Returns:

  • (Array)

    city list

  • (Fixnum)

    error code



143
144
145
146
147
148
149
150
# File 'lib/pm_25.rb', line 143

def available_cities(token=get_token)
  res = RestClient.get 'http://www.pm25.in/api/querys.json', {params: {token: token}}
  if res.code == 200
    JSON.parse(res.body)['cities']
  else
    res.code
  end
end

.bing_pm25(city) ⇒ Fixnum

Get PM 2.5 from bing.com

Parameters:

  • city (String)

    only accept Chinese

Returns:

  • (Fixnum)

    PM 25 value



174
175
176
177
178
179
# File 'lib/pm_25.rb', line 174

def bing_pm25(city)
  city = URI.encode(city)
  bing_url = "http://cn.bing.com/search?q=#{city}+pm2.5"
  html = Nokogiri.parse(open(bing_url).read)
  html.at('#msn_pm25rt .b_xlText').content.to_i
end

.get_config(constant_name, default_value = nil) ⇒ String

Use environment variable or the value from config at the current directory. If all failed, use the default value.

Parameters:

  • constant_name (String)
  • default_value (String) (defaults to: nil)

Returns:

  • (String)


22
23
24
25
26
27
28
29
30
31
32
# File 'lib/pm_25.rb', line 22

def get_config(constant_name, default_value=nil)
  if ENV[constant_name]
    ENV[constant_name]
  elsif File.exist?('config.json')
    open('config.json') do |f|
      JSON.parse(f.read)[constant_name]
    end
  else
    default_value
  end
end

.get_default_cityString

Use environment variable PM25_IN_CITY or the value from config at the current directory.

Returns:

  • (String)

    city



50
51
52
# File 'lib/pm_25.rb', line 50

def get_default_city
  get_config('PM25_IN_CITY')
end

.get_tokenString

Use environment variable PM25_IN_TOKEN or the value from config at the current directory. If all failed, use the test token. You can apply a token at pm25.in: www.pm25.in/api_doc

Returns:

  • (String)

    token



41
42
43
44
# File 'lib/pm_25.rb', line 41

def get_token
  test_token= '5j1znBVAsnSf5xQyNQyq'
  get_config('PM25_IN_TOKEN', test_token)
end

.just_pm25(city = get_default_city, token = nil) ⇒ Fixnum

Get PM 2.5 value for city. Fallback to bing.com. Return PM 2.5 value only.

Parameters:

  • city (String) (defaults to: get_default_city)

    only accept Chinese

  • token (String) (defaults to: nil)

Returns:

  • (Fixnum)

    average PM 2.5 value for stations



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/pm_25.rb', line 187

def just_pm25(city=get_default_city, token=nil)
  result = pm25(city, token)
  # error on pm25.in api
  if result.is_a? Fixnum
    bing_pm25(city)
  elsif result.include? 'error'
    bing_pm25(city)
  else
    publish_time = Time.parse(result[-1]['time_point'])
    # Data on pm25.in is too old.
    # According to GB 3095-2013, PM 2.5 should have hourly
    # average values for at least 20 hours a day.
    # http://hbj.shanghang.gov.cn/hjjc/gldt/201411/P020141110361645902554.pdf
    if Time.now - publish_time > 2.hours
      bing_pm25(city)
    else
      result[-1]['pm2_5']
    end
  end
end

.pm25(city = get_default_city, token = nil) ⇒ Array<Hash>

Get PM 2.5 info of all stations in the specified city. API frequency limit: 500 per hour.

  • Chinese (e.g. 广州)

  • area code (e.g. 020)

  • Pinyin (e.g. guangzhou)

If there is ambiguity in Pinyin, use ‘shi` as postfix, for example: 泰州 is taizhoushi, and 台州 is taizhou. For more information, see www.pm25.in/ Every station includes:

  • aqi (according to CN standard)

  • area

  • pm2_5

  • pm2_5_24h (moving average)

  • position_name (station name)

  • primary_pollutant

  • quality (优、良、轻度污染、中度污染、重度污染、严重污染 according to CN standard)

  • station_code

  • time_point (publish time of air condition)

Example:

pm25('zhuhai')

[

{
    "aqi"=> 82,
    "area"=> "珠海",
    "pm2_5"=> 31,
    "pm2_5_24h"=> 60,
    "position_name"=> "吉大",
    "primary_pollutant"=> "颗粒物(PM2.5)",
    "quality"=> "良",
    "station_code"=> "1367A",
    "time_point"=> "2013-03-07T19:00:00Z"
},
...
...
...
{
    "aqi"=> 108,
    "area"=> "珠海",
    "pm2_5"=> 0,
    "pm2_5_24h"=> 53,
    "position_name"=> "斗门",
    "primary_pollutant"=> "臭氧8小时",
    "quality"=> "轻度污染",
    "station_code"=> "1370A",
    "time_point"=> "2013-03-07T19:00:00Z"
},
{
    "aqi"=> 99,
    "area"=> "珠海",
    "pm2_5"=> 39,
    "pm2_5_24h"=> 67,
    "position_name"=> null,
    "primary_pollutant"=> null,
    "quality"=> "良",
    "station_code"=> null,
    "time_point"=> "2013-03-07T19:00:00Z"
}

]

Parameters:

  • city (String) (defaults to: get_default_city)

    You can use

Returns:

  • (Array<Hash>)

    an array of all stations



134
135
136
# File 'lib/pm_25.rb', line 134

def pm25(city=get_default_city, token=nil)
  access_api('pm2_5.json', city: city, token: token)
end

.pm25_level(pm25) ⇒ Hash{Symbol: Fixnum, Symbol: String, Symbol: String, Symbol: String}

Get AQI category, meaning and action according to US standard.

Parameters:

  • (Fixnum)

Returns:

  • (Hash{Symbol: Fixnum, Symbol: String, Symbol: String, Symbol: String})


212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/pm_25.rb', line 212

def pm25_level(pm25)
  if pm25 <= 12
    aqi_category = 'Good'
    aqi_meaning = 'Air quality is considered satisfactory, and air pollution poses little or no risk.'
    aqi_action = 'None'
  elsif pm25 <= 35.4
    aqi_category = 'Moderate'
    aqi_meaning = 'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.'
    aqi_action = 'Unusually sensitive people should consider reducing prolonged or heavy exertion.'
  elsif pm25 <= 55.4
    aqi_category = 'Unhealthy for sensitive Groups'
    aqi_meaning = 'Members of sensitive groups may experience health effects. The general public is not likely to be affected.'
    aqi_action = 'People with heart or lung disease, children and older adults should reduc e prolonged or heavy exertion'
  elsif pm25 <= 150.4
    aqi_category = 'Unhealthy'
    aqi_meaning = 'Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects.'
    aqi_action = 'People with heart or lung disease, children and older adults should avoid prolonged or heavy exertion. Everyone else should reduce prolonged or heavy exertion.'
  elsif pm25 <= 250.4
    aqi_category = 'Very Unhealthy'
    aqi_meaning = 'Health warnings of emergency conditions. The entire population is more likely to be affected.'
    aqi_action = 'People with heart or lung disease, children and older adults should avoid all physical activity outdoors. Everyone else should avoid prolonged or heavy exertion.'
  else
    aqi_category = 'Hazardous'
    aqi_meaning = 'Health alert: everyone may experience more serious health effects'
    aqi_action = 'Avoid all physical activity outdoors.'
  end
{pm25: pm25,
 category: aqi_category,
 meaning: aqi_meaning,
 action: aqi_action}
end