00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "ion_noaa.h"
00023
00024 class NOAAIon::Private : public QObject
00025 {
00026 public:
00027 Private() {
00028 m_url = 0;
00029 }
00030 ~Private() {
00031 delete m_url;
00032 }
00033
00034 private:
00035 struct XMLMapInfo {
00036 QString stateName;
00037 QString stationName;
00038 QString XMLurl;
00039 QString sourceOptions;
00040 };
00041
00042 public:
00043
00044 QHash<QString, NOAAIon::Private::XMLMapInfo> m_place;
00045 QHash<QString, QString> m_locations;
00046 QString m_state;
00047 QString m_station_name;
00048 QString m_xmlurl;
00049
00050
00051 QHash<QString, WeatherData> m_weatherData;
00052
00053
00054 QMap<KJob *, QXmlStreamReader*> m_jobXml;
00055 QMap<KJob *, QString> m_jobList;
00056 QXmlStreamReader m_xmlSetup;
00057 KUrl *m_url;
00058 KIO::TransferJob *m_job;
00059
00060 QDateTime m_dateFormat;
00061 };
00062
00063 QMap<QString, IonInterface::WindDirections> NOAAIon::setupWindIconMappings(void)
00064 {
00065 QMap<QString, WindDirections> windDir;
00066 windDir["north"] = N;
00067 windDir["northeast"] = NE;
00068 windDir["south"] = S;
00069 windDir["southwest"] = SW;
00070 windDir["east"] = E;
00071 windDir["southeast"] = SE;
00072 windDir["west"] = W;
00073 windDir["northwest"] = NW;
00074 windDir["calm"] = VR;
00075 return windDir;
00076 }
00077
00078 QMap<QString, IonInterface::ConditionIcons> NOAAIon::setupConditionIconMappings(void)
00079 {
00080
00081 QMap<QString, ConditionIcons> conditionList;
00082 return conditionList;
00083 }
00084
00085 QMap<QString, IonInterface::ConditionIcons> const& NOAAIon::conditionIcons(void)
00086 {
00087 static QMap<QString, ConditionIcons> const condval = setupConditionIconMappings();
00088 return condval;
00089 }
00090
00091 QMap<QString, IonInterface::WindDirections> const& NOAAIon::windIcons(void)
00092 {
00093 static QMap<QString, WindDirections> const wval = setupWindIconMappings();
00094 return wval;
00095 }
00096
00097
00098 NOAAIon::NOAAIon(QObject *parent, const QVariantList &args)
00099 : IonInterface(parent, args), d(new Private())
00100 {
00101 Q_UNUSED(args)
00102 }
00103
00104 NOAAIon::~NOAAIon()
00105 {
00106
00107 delete d;
00108 }
00109
00110
00111 void NOAAIon::init()
00112 {
00113
00114 getXMLSetup();
00115 }
00116
00117 QStringList NOAAIon::validate(const QString& source) const
00118 {
00119 QStringList placeList;
00120 QHash<QString, QString>::const_iterator it = d->m_locations.constBegin();
00121 while (it != d->m_locations.constEnd()) {
00122 if (it.value().toLower().contains(source.toLower())) {
00123 placeList.append(QString("place|%1").arg(it.value().split('|')[1]));
00124 }
00125 ++it;
00126 }
00127
00128
00129 if (placeList.isEmpty()) {
00130 return QStringList();
00131 }
00132
00133 placeList.sort();
00134 return placeList;
00135 }
00136
00137 bool NOAAIon::updateIonSource(const QString& source)
00138 {
00139
00140
00141
00142
00143 QStringList sourceAction = source.split('|');
00144
00145
00146 if (sourceAction.size() < 2) {
00147 setData(source, "validate", QString("noaa|timeout"));
00148 return true;
00149 }
00150
00151 if (sourceAction[1] == QString("validate")) {
00152 kDebug() << "Initiate Validating of place: " << sourceAction[2];
00153 QStringList result = validate(QString("%1|%2").arg(sourceAction[0]).arg(sourceAction[2]));
00154
00155 if (result.size() == 1) {
00156 setData(source, "validate", QString("noaa|valid|single|%1").arg(result.join("|")));
00157 return true;
00158 } else if (result.size() > 1) {
00159 setData(source, "validate", QString("noaa|valid|multiple|%1").arg(result.join("|")));
00160 return true;
00161 } else if (result.size() == 0) {
00162 setData(source, "validate", QString("noaa|invalid|single|%1").arg(sourceAction[2]));
00163 return true;
00164 }
00165
00166 } else if (sourceAction[1] == QString("weather")) {
00167 getXMLData(source);
00168 return true;
00169 }
00170 return false;
00171 }
00172
00173
00174 void NOAAIon::getXMLSetup()
00175 {
00176 d->m_url = new KUrl("http://www.weather.gov/data/current_obs/index.xml");
00177
00178 KIO::TransferJob *job = KIO::get(d->m_url->url(), KIO::NoReload, KIO::HideProgressInfo);
00179
00180 if (job) {
00181 connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)), this,
00182 SLOT(setup_slotDataArrived(KIO::Job *, const QByteArray &)));
00183 connect(job, SIGNAL(result(KJob *)), this, SLOT(setup_slotJobFinished(KJob *)));
00184 }
00185 }
00186
00187
00188 void NOAAIon::getXMLData(const QString& source)
00189 {
00190 KUrl url;
00191
00192 QString dataKey = source;
00193 dataKey.remove("|weather");
00194 url = d->m_place[dataKey].XMLurl;
00195
00196 kDebug() << "URL Location: " << url.url();
00197
00198
00199
00200 if (url.url().isEmpty()) {
00201 setData(source, "validate", QString("noaa|timeout"));
00202 return;
00203 }
00204
00205 d->m_job = KIO::get(url.url(), KIO::Reload, KIO::HideProgressInfo);
00206 d->m_jobXml.insert(d->m_job, new QXmlStreamReader);
00207 d->m_jobList.insert(d->m_job, source);
00208
00209 if (d->m_job) {
00210 connect(d->m_job, SIGNAL(data(KIO::Job *, const QByteArray &)), this,
00211 SLOT(slotDataArrived(KIO::Job *, const QByteArray &)));
00212 connect(d->m_job, SIGNAL(result(KJob *)), this, SLOT(slotJobFinished(KJob *)));
00213 }
00214 }
00215
00216 void NOAAIon::setup_slotDataArrived(KIO::Job *job, const QByteArray &data)
00217 {
00218 Q_UNUSED(job)
00219
00220 if (data.isEmpty()) {
00221 return;
00222 }
00223
00224
00225 d->m_xmlSetup.addData(data);
00226 }
00227
00228 void NOAAIon::slotDataArrived(KIO::Job *job, const QByteArray &data)
00229 {
00230 if (data.isEmpty() || !d->m_jobXml.contains(job)) {
00231 return;
00232 }
00233
00234
00235 d->m_jobXml[job]->addData(data);
00236 }
00237
00238 void NOAAIon::slotJobFinished(KJob *job)
00239 {
00240
00241 setData(d->m_jobList[job], Data());
00242 readXMLData(d->m_jobList[job], *d->m_jobXml[job]);
00243 d->m_jobList.remove(job);
00244 delete d->m_jobXml[job];
00245 d->m_jobXml.remove(job);
00246 }
00247
00248 void NOAAIon::setup_slotJobFinished(KJob *job)
00249 {
00250 Q_UNUSED(job)
00251 readXMLSetup();
00252 setInitialized(true);
00253 }
00254
00255 void NOAAIon::parseStationID()
00256 {
00257 QString tmp;
00258 while (!d->m_xmlSetup.atEnd()) {
00259 d->m_xmlSetup.readNext();
00260
00261 if (d->m_xmlSetup.isEndElement() && d->m_xmlSetup.name() == "station") {
00262 break;
00263 }
00264
00265 if (d->m_xmlSetup.isStartElement()) {
00266 if (d->m_xmlSetup.name() == "state") {
00267 d->m_state = d->m_xmlSetup.readElementText();
00268 } else if (d->m_xmlSetup.name() == "station_name") {
00269 d->m_station_name = d->m_xmlSetup.readElementText();
00270 } else if (d->m_xmlSetup.name() == "xml_url") {
00271 d->m_xmlurl = d->m_xmlSetup.readElementText();
00272
00273 tmp = "noaa|" + d->m_station_name + ", " + d->m_state;
00274 d->m_place[tmp].stateName = d->m_state;
00275 d->m_place[tmp].stationName = d->m_station_name;
00276 d->m_place[tmp].XMLurl = d->m_xmlurl.replace("http://", "http://www.");
00277
00278 d->m_locations[tmp] = tmp;
00279 } else {
00280 parseUnknownElement(d->m_xmlSetup);
00281 }
00282 }
00283 }
00284 }
00285
00286 void NOAAIon::parseStationList()
00287 {
00288 while (!d->m_xmlSetup.atEnd()) {
00289 d->m_xmlSetup.readNext();
00290
00291 if (d->m_xmlSetup.isEndElement()) {
00292 break;
00293 }
00294
00295 if (d->m_xmlSetup.isStartElement()) {
00296 if (d->m_xmlSetup.name() == "station") {
00297 parseStationID();
00298 } else {
00299 parseUnknownElement(d->m_xmlSetup);
00300 }
00301 }
00302 }
00303 }
00304
00305
00306 bool NOAAIon::readXMLSetup()
00307 {
00308 while (!d->m_xmlSetup.atEnd()) {
00309 d->m_xmlSetup.readNext();
00310
00311 if (d->m_xmlSetup.isStartElement()) {
00312 if (d->m_xmlSetup.name() == "wx_station_index") {
00313 parseStationList();
00314 }
00315 }
00316 }
00317 return !d->m_xmlSetup.error();
00318 }
00319
00320 WeatherData NOAAIon::parseWeatherSite(WeatherData& data, QXmlStreamReader& xml)
00321 {
00322 data.temperature_C = "N/A";
00323 data.temperature_F = "N/A";
00324 data.dewpoint_C = "N/A";
00325 data.dewpoint_F = "N/A";
00326 data.weather = "N/A";
00327 data.stationID = "N/A";
00328 data.pressure = "N/A";
00329 data.visibility = "N/A";
00330 data.humidity = "N/A";
00331 data.windSpeed = "N/A";
00332 data.windGust = "N/A";
00333 data.windchill_F = "N/A";
00334 data.windchill_C = "N/A";
00335 data.heatindex_F = "N/A";
00336 data.heatindex_C = "N/A";
00337
00338 while (!xml.atEnd()) {
00339 xml.readNext();
00340
00341 if (xml.isStartElement()) {
00342 if (xml.name() == "location") {
00343 data.locationName = xml.readElementText();
00344 } else if (xml.name() == "station_id") {
00345 data.stationID = xml.readElementText();
00346 } else if (xml.name() == "observation_time") {
00347 data.observationTime = xml.readElementText();
00348 QStringList tmpDateStr = data.observationTime.split(' ');
00349 data.observationTime = QString("%1 %2").arg(tmpDateStr[5]).arg(tmpDateStr[6]);
00350 d->m_dateFormat = QDateTime::fromString(data.observationTime, "h:mm ap");
00351 data.iconPeriodHour = d->m_dateFormat.toString("HH");
00352 data.iconPeriodAP = d->m_dateFormat.toString("ap");
00353
00354 } else if (xml.name() == "weather") {
00355 data.weather = xml.readElementText();
00356
00357 } else if (xml.name() == "temp_f") {
00358 data.temperature_F = xml.readElementText();
00359 } else if (xml.name() == "temp_c") {
00360 data.temperature_C = xml.readElementText();
00361 } else if (xml.name() == "relative_humidity") {
00362 data.humidity = xml.readElementText();
00363 } else if (xml.name() == "wind_dir") {
00364 data.windDirection = xml.readElementText();
00365 } else if (xml.name() == "wind_mph") {
00366 data.windSpeed = xml.readElementText();
00367 } else if (xml.name() == "wind_gust_mph") {
00368 data.windGust = xml.readElementText();
00369 } else if (xml.name() == "pressure_in") {
00370 data.pressure = xml.readElementText();
00371 } else if (xml.name() == "dewpoint_f") {
00372 data.dewpoint_F = xml.readElementText();
00373 } else if (xml.name() == "dewpoint_c") {
00374 data.dewpoint_C = xml.readElementText();
00375 } else if (xml.name() == "heat_index_f") {
00376 data.heatindex_F = xml.readElementText();
00377 } else if (xml.name() == "heat_index_c") {
00378 data.heatindex_C = xml.readElementText();
00379 } else if (xml.name() == "windchill_f") {
00380 data.windchill_F = xml.readElementText();
00381 } else if (xml.name() == "windchill_c") {
00382 data.windchill_C = xml.readElementText();
00383 } else if (xml.name() == "visibility_mi") {
00384 data.visibility = xml.readElementText();
00385 } else {
00386 parseUnknownElement(xml);
00387 }
00388 }
00389 }
00390 return data;
00391 }
00392
00393
00394 bool NOAAIon::readXMLData(const QString& source, QXmlStreamReader& xml)
00395 {
00396 WeatherData data;
00397
00398 while (!xml.atEnd()) {
00399 xml.readNext();
00400
00401 if (xml.isEndElement()) {
00402 break;
00403 }
00404
00405 if (xml.isStartElement()) {
00406 if (xml.name() == "current_observation") {
00407 data = parseWeatherSite(data, xml);
00408 } else {
00409 parseUnknownElement(xml);
00410 }
00411 }
00412 }
00413
00414 d->m_weatherData[source] = data;
00415 updateWeather(source);
00416 return !xml.error();
00417 }
00418
00419
00420 void NOAAIon::parseUnknownElement(QXmlStreamReader& xml)
00421 {
00422
00423 while (!xml.atEnd()) {
00424 xml.readNext();
00425
00426 if (xml.isEndElement()) {
00427 break;
00428 }
00429
00430 if (xml.isStartElement()) {
00431 parseUnknownElement(xml);
00432 }
00433 }
00434 }
00435
00436 void NOAAIon::updateWeather(const QString& source)
00437 {
00438 QMap<QString, QString> dataFields;
00439 QStringList fieldList;
00440
00441 setData(source, "Country", country(source));
00442 setData(source, "Place", place(source));
00443 setData(source, "Station", station(source));
00444
00445
00446 setData(source, "Observation Period", observationTime(source));
00447 setData(source, "Current Conditions", condition(source));
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462 setData(source, "Condition Icon", "weather-none-available");
00463
00464 dataFields = temperature(source);
00465 setData(source, "Temperature", dataFields["temperature"]);
00466
00467 if (dataFields["temperature"] != "N/A") {
00468 setData(source, "Temperature Unit", dataFields["temperatureUnit"]);
00469 }
00470
00471
00472 if (dataFields["comfortTemperature"] != "N/A") {
00473 if (d->m_weatherData[source].windchill_F != "NA") {
00474 setData(source, "Windchill", QString("%1").arg(dataFields["comfortTemperature"]));
00475 setData(source, "Humidex", "N/A");
00476 }
00477 if (d->m_weatherData[source].heatindex_F != "NA" && d->m_weatherData[source].temperature_F.toInt() != d->m_weatherData[source].heatindex_F.toInt()) {
00478 setData(source, "Humidex", QString("%1").arg(dataFields["comfortTemperature"]));
00479 setData(source, "Windchill", "N/A");
00480 }
00481 } else {
00482 setData(source, "Windchill", "N/A");
00483 setData(source, "Humidex", "N/A");
00484 }
00485
00486 setData(source, "Dewpoint", dewpoint(source));
00487 if (dewpoint(source) != "N/A") {
00488 setData(source, "Dewpoint Unit", dataFields["temperatureUnit"]);
00489 }
00490
00491 dataFields = pressure(source);
00492 setData(source, "Pressure", dataFields["pressure"]);
00493
00494 if (dataFields["pressure"] != "N/A") {
00495 setData(source, "Pressure Unit", dataFields["pressureUnit"]);
00496 }
00497
00498 dataFields = visibility(source);
00499 setData(source, "Visibility", dataFields["visibility"]);
00500
00501 if (dataFields["visibility"] != "N/A") {
00502 setData(source, "Visibility Unit", dataFields["visibilityUnit"]);
00503 }
00504
00505 setData(source, "Humidity", humidity(source));
00506
00507
00508 setData(source, QString("Total Weather Days"), 0);
00509
00510 dataFields = wind(source);
00511 setData(source, "Wind Speed", dataFields["windSpeed"]);
00512
00513 if (dataFields["windSpeed"] != "Calm") {
00514 setData(source, "Wind Speed Unit", dataFields["windUnit"]);
00515 }
00516
00517 setData(source, "Wind Gust", dataFields["windGust"]);
00518 setData(source, "Wind Gust Unit", dataFields["windGustUnit"]);
00519 setData(source, "Wind Direction", getWindDirectionIcon(windIcons(), dataFields["windDirection"].toLower()));
00520 setData(source, "Credit", "Data provided by NOAA National Weather Service");
00521 }
00522
00523 QString NOAAIon::country(const QString& source)
00524 {
00525 Q_UNUSED(source);
00526 return QString("USA");
00527 }
00528 QString NOAAIon::place(const QString& source)
00529 {
00530 return d->m_weatherData[source].locationName;
00531 }
00532 QString NOAAIon::station(const QString& source)
00533 {
00534 return d->m_weatherData[source].stationID;
00535 }
00536
00537 QString NOAAIon::observationTime(const QString& source)
00538 {
00539 return d->m_weatherData[source].observationTime;
00540 }
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552 int NOAAIon::periodHour(const QString& source)
00553 {
00554 return d->m_weatherData[source].iconPeriodHour.toInt();
00555 }
00556
00557 QString NOAAIon::condition(const QString& source)
00558 {
00559 if (d->m_weatherData[source].weather.isEmpty() || d->m_weatherData[source].weather == "NA") {
00560 d->m_weatherData[source].weather = "N/A";
00561 }
00562 return d->m_weatherData[source].weather;
00563 }
00564
00565 QString NOAAIon::dewpoint(const QString& source)
00566 {
00567 return d->m_weatherData[source].dewpoint_F;
00568 }
00569
00570 QString NOAAIon::humidity(const QString& source)
00571 {
00572 if (d->m_weatherData[source].humidity == "NA") {
00573 return QString("N/A");
00574 } else {
00575 return QString("%1%").arg(d->m_weatherData[source].humidity);
00576 }
00577 }
00578
00579 QMap<QString, QString> NOAAIon::visibility(const QString& source)
00580 {
00581 QMap<QString, QString> visibilityInfo;
00582 if (d->m_weatherData[source].visibility.isEmpty()) {
00583 visibilityInfo.insert("visibility", QString("N/A"));
00584 return visibilityInfo;
00585 }
00586 visibilityInfo.insert("visibility", d->m_weatherData[source].visibility);
00587 visibilityInfo.insert("visibilityUnit", QString::number(WeatherUtils::Miles));
00588 return visibilityInfo;
00589 }
00590
00591 QMap<QString, QString> NOAAIon::temperature(const QString& source)
00592 {
00593 QMap<QString, QString> temperatureInfo;
00594 temperatureInfo.insert("temperature", d->m_weatherData[source].temperature_F);
00595 temperatureInfo.insert("temperatureUnit", QString::number(WeatherUtils::Fahrenheit));
00596 temperatureInfo.insert("comfortTemperature", "N/A");
00597
00598 if (d->m_weatherData[source].heatindex_F != "NA" && d->m_weatherData[source].windchill_F == "NA") {
00599 temperatureInfo.insert("comfortTemperature", d->m_weatherData[source].heatindex_F);
00600 }
00601
00602 if (d->m_weatherData[source].windchill_F != "NA" && d->m_weatherData[source].heatindex_F == "NA") {
00603 temperatureInfo.insert("comfortTemperature", d->m_weatherData[source].windchill_F);
00604 }
00605
00606 return temperatureInfo;
00607 }
00608
00609 QMap<QString, QString> NOAAIon::pressure(const QString& source)
00610 {
00611 QMap<QString, QString> pressureInfo;
00612 if (d->m_weatherData[source].pressure.isEmpty()) {
00613 pressureInfo.insert("pressure", "N/A");
00614 return pressureInfo;
00615 }
00616
00617 pressureInfo.insert("pressure", d->m_weatherData[source].pressure);
00618 pressureInfo.insert("pressureUnit", QString::number(WeatherUtils::InchesHG));
00619 return pressureInfo;
00620 }
00621
00622 QMap<QString, QString> NOAAIon::wind(const QString& source)
00623 {
00624 QMap<QString, QString> windInfo;
00625
00626
00627 if (d->m_weatherData[source].windSpeed == "NA") {
00628 windInfo.insert("windSpeed", "Calm");
00629 windInfo.insert("windUnit", QString::number(WeatherUtils::NoUnit));
00630 } else {
00631 windInfo.insert("windSpeed", QString::number(d->m_weatherData[source].windSpeed.toFloat(), 'f', 1));
00632 windInfo.insert("windUnit", QString::number(WeatherUtils::MilesAnHour));
00633 }
00634
00635
00636 if (d->m_weatherData[source].windGust == "NA") {
00637 windInfo.insert("windGust", "N/A");
00638 windInfo.insert("windGustUnit", QString::number(WeatherUtils::NoUnit));
00639 } else {
00640 windInfo.insert("windGust", QString::number(d->m_weatherData[source].windGust.toFloat(), 'f', 1));
00641 windInfo.insert("windGustUnit", QString::number(WeatherUtils::MilesAnHour));
00642 }
00643
00644 if (d->m_weatherData[source].windDirection.isEmpty()) {
00645 windInfo.insert("windDirection", "N/A");
00646 } else {
00647 windInfo.insert("windDirection", d->m_weatherData[source].windDirection);
00648 }
00649 return windInfo;
00650 }
00651
00652 #include "ion_noaa.moc"