/* * Copyright (C) 2014 Francesco Azzola * Surviving with Android (http://www.survivingwithandroid.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.survivingwithandroid.weather.lib.provider.openweathermap; import android.util.Log; import com.survivingwithandroid.weather.lib.WeatherCode; import com.survivingwithandroid.weather.lib.WeatherConfig; import com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException; import com.survivingwithandroid.weather.lib.exception.WeatherLibException; import com.survivingwithandroid.weather.lib.model.BaseWeather; import com.survivingwithandroid.weather.lib.model.City; import com.survivingwithandroid.weather.lib.model.CurrentWeather; import com.survivingwithandroid.weather.lib.model.DayForecast; import com.survivingwithandroid.weather.lib.model.HistoricalHourWeather; import com.survivingwithandroid.weather.lib.model.HistoricalWeather; import com.survivingwithandroid.weather.lib.model.HourForecast; import com.survivingwithandroid.weather.lib.model.Location; import com.survivingwithandroid.weather.lib.model.Weather; import com.survivingwithandroid.weather.lib.model.WeatherForecast; import com.survivingwithandroid.weather.lib.model.WeatherHourForecast; import com.survivingwithandroid.weather.lib.provider.IWeatherCodeProvider; import com.survivingwithandroid.weather.lib.provider.IWeatherProvider; import com.survivingwithandroid.weather.lib.request.Params; import com.survivingwithandroid.weather.lib.request.WeatherRequest; import com.survivingwithandroid.weather.lib.util.LogUtils; import com.survivingwithandroid.weather.lib.util.WeatherUtility; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Concrete implementation of IWeatherProvider. This class handles the protocol details for Openweathermap * weather provider. It extracts the weather information building the weather model. * * @author Francesco Azzola * */ public class OpenweathermapProvider implements IWeatherProvider { private static String BASE_URL = "http://api.openweathermap.org/data/2.5/weather?mode=json&q="; private static String BASE_URL_ID = "http://api.openweathermap.org/data/2.5/weather?mode=json&id="; private static String GEO_BASE_URL_ID = "http://api.openweathermap.org/data/2.5/weather?mode=json"; private static String IMG_URL = "http://openweathermap.org/img/w/"; private static String SEARCH_URL = "http://api.openweathermap.org/data/2.5/find?mode=json&type=like&q="; private static String SEARCH_URL_GEO = "http://api.openweathermap.org/data/2.5/find?mode=json&type=accurate"; private static String BASE_FORECAST_URL_ID = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&id="; private static String GEO_BASE_FORECAST_URL_ID = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json"; private static String BASE_HOUR_FORECAST_URL = "http://api.openweathermap.org/data/2.5/forecast?mode=json&id="; private static String GEO_BASE_HOUR_FORECAST_URL = "http://api.openweathermap.org/data/2.5/forecast?mode=json"; private static String BASE_HISTORICAL_URL = "http://api.openweathermap.org/data/2.5/history/city?mode=json&id="; private static String GEO_BASE_HISTORICAL_URL = "http://api.openweathermap.org/data/2.5/history/city?mode=json"; private WeatherConfig config; private BaseWeather.WeatherUnit units = new BaseWeather.WeatherUnit(); private IWeatherCodeProvider codeProvider; public CurrentWeather getCurrentCondition(String data) throws WeatherLibException { //LogUtils.LOGD("JSON CurrentWeather ["+data+"]"); CurrentWeather cWeather = new CurrentWeather(); Weather weather = new Weather(); try { // We create out JSONObject from the data JSONObject jObj = new JSONObject(data); // We start extracting the info Location loc = new Location(); JSONObject coordObj = getObject("coord", jObj); loc.setLatitude(getFloat("lat", coordObj)); loc.setLongitude(getFloat("lon", coordObj)); JSONObject sysObj = getObject("sys", jObj); loc.setCountry(getString("country", sysObj)); loc.setSunrise(getInt("sunrise", sysObj)); loc.setSunset(getInt("sunset", sysObj)); loc.setCity(getString("name", jObj)); weather.location = loc; // We get weather info (This is an array) JSONArray jArr = jObj.getJSONArray("weather"); // We use only the first value JSONObject JSONWeather = jArr.getJSONObject(0); weather.currentCondition.setWeatherId(getInt("id", JSONWeather)); // Convert internal code if (codeProvider != null) { try { weather.currentCondition.setWeatherCode(codeProvider.getWeatherCode(String.valueOf(weather.currentCondition.getWeatherId()))); } catch(Throwable t) { weather.currentCondition.setWeatherCode(WeatherCode.NOT_AVAILABLE); } } weather.currentCondition.setDescr(getString("description", JSONWeather)); weather.currentCondition.setCondition(getString("main", JSONWeather)); weather.currentCondition.setIcon(getString("icon", JSONWeather)); JSONObject mainObj = getObject("main", jObj); weather.currentCondition.setHumidity(getInt("humidity", mainObj)); weather.currentCondition.setPressure(getFloat("pressure", mainObj)); //#18 weather.currentCondition.setPressureGroundLevel((float) mainObj.optDouble("grnd_level")); weather.currentCondition.setPressureSeaLevel((float) mainObj.optDouble("sea_level")); weather.temperature.setMaxTemp(getFloat("temp_max", mainObj)); weather.temperature.setMinTemp(getFloat("temp_min", mainObj)); weather.temperature.setTemp(getFloat("temp", mainObj)); // Wind JSONObject wObj = getObject("wind", jObj); weather.wind.setSpeed(getFloat("speed", wObj)); weather.wind.setDeg(getFloat("deg", wObj)); /* try { weather.wind.setGust(getFloat("gust", wObj)); } catch (Throwable t) { } */ weather.wind.setGust((float) wObj.optDouble("gust")); // Clouds JSONObject cObj = getObject("clouds", jObj); weather.clouds.setPerc(getInt("all", cObj)); // Rain (use opt to handle option parameters JSONObject rObj = jObj.optJSONObject("rain"); if (rObj != null) { float amm1 = (float) rObj.optDouble("1h"); if (amm1 > 0) { weather.rain[0].setAmmount(amm1); weather.rain[0].setTime("1h"); } float amm3 = (float) rObj.optDouble("3h"); if (amm3 > 0) { weather.rain[1].setAmmount(amm3); weather.rain[1].setTime("3h"); } } // Snow JSONObject sObj = jObj.optJSONObject("snow"); if (sObj != null) { weather.snow.setAmmount((float) sObj.optDouble("3h")); weather.snow.setTime("3h"); } } catch (JSONException json) { json.printStackTrace(); throw new WeatherLibException(json); } cWeather.setUnit(units); cWeather.weather = weather; return cWeather; } public WeatherForecast getForecastWeather(String data) throws WeatherLibException { WeatherForecast forecast = new WeatherForecast(); try { // We create out JSONObject from the data JSONObject jObj = new JSONObject(data); // Parse city Location loc = new Location(); JSONObject cObj = jObj.getJSONObject("city"); loc.setCity(cObj.getString("name")); JSONObject cooObj = cObj.getJSONObject("coord"); loc.setLatitude((float) cooObj.getDouble("lat")); loc.setLongitude((float) cooObj.getDouble("lon")); loc.setCountry(cObj.getString("country")); loc.setPopulation(cObj.getLong("population")); JSONArray jArr = jObj.getJSONArray("list"); // Here we have the forecast for every day // We traverse all the array and parse the data for (int i = 0; i < jArr.length(); i++) { JSONObject jDayForecast = jArr.getJSONObject(i); // Now we have the json object so we can extract the data DayForecast df = new DayForecast(); // We retrieve the timestamp (dt) df.timestamp = jDayForecast.getLong("dt"); // Set location df.weather.location = loc; // Clouds df.weather.clouds.setPerc(jDayForecast.getInt("clouds")); df.weather.wind.setSpeed(getFloat("speed", jDayForecast)); df.weather.wind.setDeg(getFloat("deg", jDayForecast)); // Rain (use opt to handle option parameters JSONObject rObj = jObj.optJSONObject("rain"); if (rObj != null) { float amm1 = (float) rObj.optDouble("1h"); if (amm1 > 0) { df.weather.rain[0].setAmmount(amm1); df.weather.rain[0].setTime("1h"); } float amm3 = (float) rObj.optDouble("3h"); if (amm3 > 0) { df.weather.rain[1].setAmmount(amm3); df.weather.rain[1].setTime("3h"); } } // Temp is an object JSONObject jTempObj = jDayForecast.getJSONObject("temp"); df.forecastTemp.day = (float) jTempObj.getDouble("day"); df.forecastTemp.min = (float) jTempObj.getDouble("min"); df.forecastTemp.max = (float) jTempObj.getDouble("max"); df.forecastTemp.night = (float) jTempObj.getDouble("night"); df.forecastTemp.eve = (float) jTempObj.getDouble("eve"); df.forecastTemp.morning = (float) jTempObj.getDouble("morn"); // Pressure & Humidity df.weather.currentCondition.setPressure((float) jDayForecast.getDouble("pressure")); df.weather.currentCondition.setHumidity((float) jDayForecast.getDouble("humidity")); // ...and now the weather JSONArray jWeatherArr = jDayForecast.getJSONArray("weather"); JSONObject jWeatherObj = jWeatherArr.getJSONObject(0); df.weather.currentCondition.setWeatherId(getInt("id", jWeatherObj)); // Convert internal code if (codeProvider != null) { try { df.weather.currentCondition.setWeatherCode(codeProvider.getWeatherCode(String.valueOf(df.weather.currentCondition.getWeatherId()))); } catch(Throwable t) { df.weather.currentCondition.setWeatherCode(WeatherCode.NOT_AVAILABLE); } } df.weather.currentCondition.setDescr(getString("description", jWeatherObj)); df.weather.currentCondition.setCondition(getString("main", jWeatherObj)); df.weather.currentCondition.setIcon(getString("icon", jWeatherObj)); forecast.addForecast(df); } } catch (JSONException json) { throw new WeatherLibException(json); } forecast.setUnit(units); return forecast; } public List<City> getCityResultList(String data) throws WeatherLibException { List<City> cityList = new ArrayList<City>(); // Log.d("SwA", "Data ["+data+"]"); try { JSONObject jObj = new JSONObject(data); JSONArray jArr = jObj.getJSONArray("list"); for (int i = 0; i < jArr.length(); i++) { JSONObject obj = jArr.getJSONObject(i); String name = obj.getString("name"); String id = obj.getString("id"); JSONObject sys = obj.getJSONObject("sys"); String country = sys.getString("country"); //Log.d("SwA", "ID [" + id + "]"); City.CityBuilder builder = new City.CityBuilder().id(id).country(country).name(name); //City c = new City(id, name, null, country); City c = builder.build(); cityList.add(c); } } catch (JSONException json) { throw new WeatherLibException(json); } return cityList; } @Override public WeatherHourForecast getHourForecastWeather(String data) throws WeatherLibException { WeatherHourForecast forecast = new WeatherHourForecast(); try { JSONObject jObj = new JSONObject(data); // We move to the list tag JSONArray wList = jObj.getJSONArray("list"); for (int i = 0; i < wList.length(); i++) { JSONObject jHour = wList.getJSONObject(i); HourForecast hourForecast = new HourForecast(); hourForecast.timestamp = getLong("dt", jHour); // Weather info JSONObject jMain = jHour.getJSONObject("main"); hourForecast.weather.temperature.setTemp(getFloat("temp", jMain)); hourForecast.weather.temperature.setMinTemp(getFloat("temp_min", jMain)); hourForecast.weather.temperature.setMaxTemp(getFloat("temp_max", jMain)); hourForecast.weather.currentCondition.setPressure(getFloat("pressure", jMain)); hourForecast.weather.currentCondition.setPressureSeaLevel((float) jMain.optDouble("sea_level")); hourForecast.weather.currentCondition.setPressureGroundLevel((float) jMain.optDouble("grnd_level")); hourForecast.weather.currentCondition.setHumidity(getFloat("humidity", jMain)); // Now the weather codes // We get weather info (This is an array) JSONArray jArr1 = jHour.getJSONArray("weather"); // We use only the first value JSONObject JSONWeather = jArr1.getJSONObject(0); hourForecast.weather.currentCondition.setWeatherId(getInt("id", JSONWeather)); // Convert internal code if (codeProvider != null) { try { hourForecast.weather.currentCondition.setWeatherCode(codeProvider.getWeatherCode(String.valueOf(hourForecast.weather.currentCondition.getWeatherId()))); } catch(Throwable t) { hourForecast.weather.currentCondition.setWeatherCode(WeatherCode.NOT_AVAILABLE); } } hourForecast.weather.currentCondition.setDescr(getString("description", JSONWeather)); hourForecast.weather.currentCondition.setCondition(getString("main", JSONWeather)); hourForecast.weather.currentCondition.setIcon(getString("icon", JSONWeather)); // Clouds JSONObject cObj = getObject("clouds", jHour); hourForecast.weather.clouds.setPerc(getInt("all", cObj)); // Wind JSONObject wObj = getObject("wind", jHour); hourForecast.weather.wind.setSpeed(getFloat("speed", wObj)); hourForecast.weather.wind.setDeg(getFloat("deg", wObj)); hourForecast.weather.wind.setGust((float) wObj.optDouble("gust")); //Log.d("SwA", "Add weather forecast"); forecast.addForecast(hourForecast); } } catch (JSONException json) { throw new WeatherLibException(json); } forecast.setUnit(units); return forecast; } public HistoricalWeather getHistoricalWeather(String data) throws WeatherLibException { HistoricalWeather histWeather = new HistoricalWeather(); try { JSONObject jObj = new JSONObject(data); // We move to the list tag JSONArray wList = jObj.getJSONArray("list"); for (int i=0; i < wList.length(); i++) { JSONObject jHour = wList.getJSONObject(i); HistoricalHourWeather hhWeather = new HistoricalHourWeather(); // Clouds JSONObject cObj = getObject("clouds", jHour); hhWeather.weather.clouds.setPerc(getInt("all", cObj)); hhWeather.timestamp = jHour.getLong("dt") * 1000; JSONObject mainObj = getObject("main", jHour); hhWeather.weather.currentCondition.setPressure((float) mainObj.getDouble("pressure")); hhWeather.weather.temperature.setTemp((float) mainObj.getDouble("temp")); hhWeather.weather.temperature.setMaxTemp((float) mainObj.getDouble("temp_max")); hhWeather.weather.temperature.setMinTemp((float) mainObj.getDouble("temp_min")); JSONObject wObj = jHour.getJSONArray("weather").getJSONObject(0); hhWeather.weather.currentCondition.setDescr(wObj.getString("description")); hhWeather.weather.currentCondition.setIcon(wObj.getString("icon")); hhWeather.weather.currentCondition.setCondition(wObj.getString("main")); hhWeather.weather.currentCondition.setWeatherId(getInt("id", wObj)); String tmp = String.valueOf(hhWeather.weather.currentCondition.getWeatherId())+"]"; // Convert internal code if (codeProvider != null) { try { hhWeather.weather.currentCondition.setWeatherCode(codeProvider.getWeatherCode(String.valueOf(hhWeather.weather.currentCondition.getWeatherId()))); } catch (Throwable t) { hhWeather.weather.currentCondition.setWeatherCode(WeatherCode.NOT_AVAILABLE); } } JSONObject windObj = getObject("wind", jHour); hhWeather.weather.wind.setSpeed((float) windObj.getDouble("speed")); hhWeather.weather.wind.setDeg((float) windObj.getDouble("deg")); histWeather.addHistoricalHourWeather(hhWeather); } } catch(JSONException json) { throw new WeatherLibException(json); } histWeather.setUnit(units); return histWeather; } @Override public void setConfig(WeatherConfig config) { this.config = config; units = WeatherUtility.createWeatherUnit(config.unitSystem); } @Override public String getQueryCityURL(String cityNamePattern) { return SEARCH_URL + cityNamePattern + createAPPID(); } /* @Override public String getQueryCurrentWeatherURL(String cityId) { return BASE_URL_ID + cityId + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + createAPPID(); } @Override public String getQueryForecastWeatherURL(String cityId) { return BASE_FORECAST_URL_ID + cityId + "&lang=" + config.lang + "&cnt=" + config.numDays + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + createAPPID(); } */ @Override public String getQueryImageURL(String icon) throws ApiKeyRequiredException { return IMG_URL + icon + ".png"; } @Override public void setWeatherCodeProvider(IWeatherCodeProvider codeProvider) { this.codeProvider = codeProvider; } @Override public String getQueryCityURLByLocation(android.location.Location location) throws ApiKeyRequiredException { return SEARCH_URL_GEO + "&lat=" + location.getLatitude() + "&lon=" + location.getLongitude() + "&cnt=3" + createAPPID(); } @Override public String getQueryCityURLByCoord(double lon, double lat) throws ApiKeyRequiredException { return SEARCH_URL_GEO + "&lat=" + lat + "&lon=" + lon + "&cnt=3" + createAPPID() ; } /* @Override public String getQueryHourForecastWeatherURL(String cityId) throws ApiKeyRequiredException { return BASE_HOUR_FORECAST_URL + cityId + "&lang=" + config.lang + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + createAPPID(); } @Override public String getQueryHistoricalWeatherURL(String cityId, Date d1, Date d2) throws ApiKeyRequiredException { long timestamp1 = d1.getTime(); long timestamp2 = d2.getTime(); return BASE_HISTORICAL_URL + cityId + "&lang=" + config.lang + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&type=hour&start=" + timestamp1 + "&end=" + timestamp2 + createAPPID(); } */ @Override public String getQueryLayerURL(String cityId, Params params) throws ApiKeyRequiredException { return null; } private String createAPPID() { if (config.ApiKey == null || config.ApiKey.equals("")) return ""; return "&APPID=" + config.ApiKey; } private static JSONObject getObject(String tagName, JSONObject jObj) throws JSONException { JSONObject subObj = jObj.optJSONObject(tagName); return subObj; } private static String getString(String tagName, JSONObject jObj) throws JSONException { return jObj.optString(tagName); } private static float getFloat(String tagName, JSONObject jObj) throws JSONException { return (float) jObj.optDouble(tagName); } private static int getInt(String tagName, JSONObject jObj) throws JSONException { return jObj.optInt(tagName); } private static long getLong(String tagName, JSONObject jObj) throws JSONException { return jObj.optLong(tagName); } // New methods @Override public String getQueryCurrentWeatherURL(WeatherRequest request) throws ApiKeyRequiredException { if (request.getCityId() != null) return BASE_URL_ID + request.getCityId() + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + createAPPID(); else return GEO_BASE_URL_ID + "&lat=" + request.getLat() + "&lon=" + request.getLon() + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + createAPPID(); } @Override public String getQueryForecastWeatherURL(WeatherRequest request) throws ApiKeyRequiredException { if (request.getCityId() != null) return BASE_FORECAST_URL_ID + request.getCityId() + "&lang=" + config.lang + "&cnt=" + config.numDays + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + createAPPID(); else return GEO_BASE_FORECAST_URL_ID + "&lat=" + request.getLat() + "&lon=" + request.getLon() + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + createAPPID(); } @Override public String getQueryHourForecastWeatherURL(WeatherRequest request) throws ApiKeyRequiredException { if (request.getCityId() != null) return BASE_HOUR_FORECAST_URL + request.getCityId() + "&lang=" + config.lang + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + createAPPID(); else return GEO_BASE_HOUR_FORECAST_URL + "&lat=" + request.getLat() + "&lon=" + request.getLon() + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + createAPPID(); } @Override public String getQueryHistoricalWeatherURL(WeatherRequest request, Date d1, Date d2) throws ApiKeyRequiredException { long timestamp1 = d1.getTime() / 1000; // Unix time stamp long timestamp2 = d2.getTime() / 1000; // unix time stamp if (request.getCityId() != null) return BASE_HISTORICAL_URL + request.getCityId() + "&lang=" + config.lang + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&type=hour&start=" + timestamp1 + "&end=" + timestamp2 + createAPPID(); else return GEO_BASE_HISTORICAL_URL + "&lat=" + request.getLat() + "&lon=" + request.getLon() + "&units=" + (WeatherUtility.isMetric(config.unitSystem) ? "metric" : "imperial") + "&lang=" + config.lang + "&type=hour&start=" + timestamp1 + "&end=" + timestamp2 + createAPPID(); } }