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