package ru.gelin.android.weather.openweathermap;
import android.content.Context;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.gelin.android.weather.*;
import ru.gelin.android.weather.notification.skin.impl.WeatherConditionFormat;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Weather implementation which constructs from the JSON received from openweathermap.org
*/
public class OpenWeatherMapWeather implements Weather {
String FORECAST_URL_TEMPLATE="http://openweathermap.org/city/%d";
/** City ID */
int cityId = 0;
/** Forecast URL */
URL forecastURL;
/** Weather location */
SimpleLocation location = new SimpleLocation("");
/** Weather time */
Date time = new Date();
/** Query time */
Date queryTime = new Date();
/** Weather conditions */
List<SimpleWeatherCondition> conditions = new ArrayList<SimpleWeatherCondition>();
/** Emptyness flag */
boolean empty = true;
/** Condition text format */
WeatherConditionFormat conditionFormat;
public OpenWeatherMapWeather(Context context) {
this.conditionFormat = new WeatherConditionFormat(context);
}
public OpenWeatherMapWeather(Context context, JSONObject jsonObject) throws WeatherException {
this(context);
parseCurrentWeather(jsonObject);
}
@Override
public Location getLocation() {
return this.location;
}
@Override
public Date getTime() {
return new Date(this.time.getTime());
}
@Override
public Date getQueryTime() {
return new Date(this.queryTime.getTime());
}
@Override
public UnitSystem getUnitSystem() {
return UnitSystem.SI;
}
@Override
public List<WeatherCondition> getConditions() {
return Collections.unmodifiableList(new ArrayList<WeatherCondition>(this.conditions));
}
List<SimpleWeatherCondition> getOpenWeatherMapConditions() {
return this.conditions;
}
@Override
public boolean isEmpty() {
return this.empty;
}
public int getCityId() {
return this.cityId;
}
public URL getForecastURL() {
return this.forecastURL;
}
void parseCurrentWeather(JSONObject json) throws WeatherException {
try {
int code = json.getInt("cod");
if (code != 200) {
this.empty = true;
return;
}
parseCityId(json);
parseLocation(json);
parseTime(json);
WeatherParser parser = new CurrentWeatherParser(json);
parser.parseCondition();
} catch (JSONException e) {
throw new WeatherException("cannot parse the weather", e);
}
this.empty = false;
}
void parseDailyForecast(JSONObject json) throws WeatherException {
try {
JSONArray list = json.getJSONArray("list");
SimpleWeatherCondition condition = getCondition(0);
if (list.length() > 0) {
appendForecastTemperature(condition, list.getJSONObject(0));
condition.setConditionText(this.conditionFormat.getText(condition));
}
for (int i = 1; i < 4 && i < list.length(); i++) {
condition = getCondition(i);
parseForecast(condition, list.getJSONObject(i));
condition.setConditionText(this.conditionFormat.getText(condition));
}
} catch (JSONException e) {
throw new WeatherException("cannot parse forecasts", e);
}
}
private void parseCityId(JSONObject json) throws JSONException {
this.cityId = json.getInt("id");
try {
this.forecastURL = new URL(String.format(FORECAST_URL_TEMPLATE, this.cityId));
} catch (MalformedURLException e) {
this.forecastURL = null;
}
}
private void parseLocation(JSONObject json) {
try {
this.location = new SimpleLocation(json.getString("name"), false);
} catch (JSONException e) {
this.location = new SimpleLocation("", false);
}
}
private void parseTime(JSONObject json) {
try {
long timestamp = json.getLong("dt");
this.time = new Date(timestamp * 1000);
} catch (JSONException e) {
this.time = new Date();
}
}
private abstract class WeatherParser {
JSONObject json;
WeatherParser(JSONObject json) {
this.json = json;
}
public void parseCondition() {
SimpleWeatherCondition condition = new SimpleWeatherCondition();
condition.setTemperature(parseTemperature());
condition.setWind(parseWind());
condition.setHumidity(parseHumidity());
condition.setPrecipitation(parsePrecipitation());
condition.setCloudiness(parseCloudiness());
parseWeatherType(condition);
condition.setConditionText(OpenWeatherMapWeather.this.conditionFormat.getText(condition));
OpenWeatherMapWeather.this.conditions.add(condition);
}
private SimpleTemperature parseTemperature() {
AppendableTemperature temperature = new AppendableTemperature(TemperatureUnit.K);
JSONObject main;
if (!hasTemp()) {
//temp is optional
return temperature;
}
try {
double currentTemp = getCurrentTemp();
temperature.setCurrent((int)currentTemp, TemperatureUnit.K);
} catch (JSONException e) {
//temp is optional
}
try {
double minTemp = getMinTemp();
temperature.setLow((int)minTemp, TemperatureUnit.K);
} catch (JSONException e) {
//min temp is optional
}
try {
double maxTemp = getMaxTemp();
temperature.setHigh((int)maxTemp, TemperatureUnit.K);
} catch (JSONException e) {
//max temp is optional
}
return temperature;
}
protected abstract boolean hasTemp();
protected abstract double getCurrentTemp() throws JSONException;
protected abstract double getMinTemp() throws JSONException;
protected abstract double getMaxTemp() throws JSONException;
private SimpleWind parseWind() {
SimpleWind wind = new SimpleWind(WindSpeedUnit.MPS);
if (!hasWind()) {
return wind;
}
try {
double speed = getWindSpeed();
wind.setSpeed((int)Math.round(speed), WindSpeedUnit.MPS);
} catch (JSONException e) {
//wind speed is optional
}
try {
double deg = getWindDeg();
wind.setDirection(WindDirection.valueOf((int) deg));
} catch (JSONException e) {
//wind direction is optional
}
wind.setText(String.format("Wind: %s, %d m/s", String.valueOf(wind.getDirection()), wind.getSpeed()));
return wind;
}
protected abstract boolean hasWind();
protected abstract double getWindSpeed() throws JSONException;
protected abstract double getWindDeg() throws JSONException;
private SimpleHumidity parseHumidity() {
SimpleHumidity humidity = new SimpleHumidity();
try {
double humidityValue = getHumidity();
humidity.setValue((int)humidityValue);
humidity.setText(String.format("Humidity: %d%%", humidity.getValue()));
} catch (JSONException e) {
//humidity is optional
}
return humidity;
}
protected abstract double getHumidity() throws JSONException;
private SimplePrecipitation parsePrecipitation() {
SimplePrecipitation precipitation = new SimplePrecipitation(PrecipitationUnit.MM);
try {
precipitation.setValue(getPrecipitation(), PrecipitationPeriod.PERIOD_3H);
} catch (JSONException e) {
//no rain
}
return precipitation;
}
protected abstract float getPrecipitation() throws JSONException;
public SimpleCloudiness parseCloudiness() {
SimpleCloudiness cloudiness = new SimpleCloudiness(CloudinessUnit.PERCENT);
try {
cloudiness.setValue(getCloudiness(), CloudinessUnit.PERCENT);
} catch (JSONException e) {
//no clouds
}
return cloudiness;
}
protected abstract int getCloudiness() throws JSONException;
public void parseWeatherType(SimpleWeatherCondition condition) {
try {
JSONArray weathers = this.json.getJSONArray("weather");
for (int i = 0; i < weathers.length(); i++) {
JSONObject weather = weathers.getJSONObject(i);
WeatherConditionType type = WeatherConditionTypeFactory.fromId(weather.getInt("id"));
condition.addConditionType(type);
}
} catch (JSONException e) {
//no weather type
}
}
}
private class CurrentWeatherParser extends WeatherParser {
CurrentWeatherParser(JSONObject json) {
super(json);
}
@Override
protected boolean hasTemp() {
try {
this.json.getJSONObject("main");
return true;
} catch (JSONException e) {
return false;
}
}
@Override
protected double getCurrentTemp() throws JSONException {
return this.json.getJSONObject("main").getDouble("temp");
}
@Override
protected double getMinTemp() throws JSONException {
return this.json.getJSONObject("main").getDouble("temp_min");
}
@Override
protected double getMaxTemp() throws JSONException {
return this.json.getJSONObject("main").getDouble("temp_max");
}
@Override
protected boolean hasWind() {
try {
this.json.getJSONObject("wind");
return true;
} catch (JSONException e) {
return false;
}
}
@Override
protected double getWindSpeed() throws JSONException {
return this.json.getJSONObject("wind").getDouble("speed");
}
@Override
protected double getWindDeg() throws JSONException {
return this.json.getJSONObject("wind").getDouble("deg");
}
@Override
protected double getHumidity() throws JSONException {
return this.json.getJSONObject("main").getDouble("humidity");
}
@Override
protected float getPrecipitation() throws JSONException {
return (float)this.json.getJSONObject("rain").getDouble("3h");
}
@Override
protected int getCloudiness() throws JSONException {
return (int)this.json.getJSONObject("clouds").getDouble("all");
}
}
private class ForecastWeatherParser extends WeatherParser {
ForecastWeatherParser(JSONObject json) {
super(json);
}
@Override
protected boolean hasTemp() {
try {
this.json.getJSONObject("temp");
return true;
} catch (JSONException e) {
return false;
}
}
@Override
protected double getCurrentTemp() throws JSONException {
throw new JSONException("no current temp in forecast");
}
@Override
protected double getMinTemp() throws JSONException {
return this.json.getJSONObject("temp").getDouble("min");
}
@Override
protected double getMaxTemp() throws JSONException {
return this.json.getJSONObject("temp").getDouble("max");
}
@Override
protected boolean hasWind() {
return true; //considering forecasts always have wind
}
@Override
protected double getWindSpeed() throws JSONException {
return this.json.getDouble("speed");
}
@Override
protected double getWindDeg() throws JSONException {
return this.json.getDouble("deg");
}
@Override
protected double getHumidity() throws JSONException {
return this.json.getDouble("humidity");
}
@Override
protected float getPrecipitation() throws JSONException {
return (float)this.json.getDouble("rain");
}
@Override
protected int getCloudiness() throws JSONException {
return (int)this.json.getDouble("clouds");
}
}
private SimpleWeatherCondition getCondition(int i) {
while (i >= this.conditions.size()) {
SimpleWeatherCondition condition = new SimpleWeatherCondition();
condition.setConditionText("");
condition.setTemperature(new AppendableTemperature(TemperatureUnit.K));
condition.setHumidity(new SimpleHumidity());
condition.setWind(new SimpleWind(WindSpeedUnit.MPS));
condition.setPrecipitation(new SimplePrecipitation(PrecipitationUnit.MM));
condition.setCloudiness(new SimpleCloudiness(CloudinessUnit.PERCENT));
this.conditions.add(condition);
}
return this.conditions.get(i);
}
private void appendForecastTemperature(SimpleWeatherCondition condition, JSONObject json)
throws JSONException {
WeatherParser parser = new ForecastWeatherParser(json);
AppendableTemperature existedTemp = (AppendableTemperature)condition.getTemperature();
SimpleTemperature newTemp = parser.parseTemperature();
existedTemp.append(newTemp);
}
private void parseForecast(SimpleWeatherCondition condition, JSONObject json)
throws JSONException {
appendForecastTemperature(condition, json);
WeatherParser parser = new ForecastWeatherParser(json);
condition.setHumidity(parser.parseHumidity());
condition.setWind(parser.parseWind());
condition.setPrecipitation(parser.parsePrecipitation());
condition.setCloudiness(parser.parseCloudiness());
parser.parseWeatherType(condition);
}
}