#!/usr/local/bin/php * Copyright (c) 2005-2011, Alexander Wirtz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * o Neither the name of the software nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * @category Web Services * @package Services_Weather * @subpackage buildMetarDB * @author Alexander Wirtz * @copyright 2005-2011 Alexander Wirtz * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version SVN: $Id$ * @link http://pear.php.net/package/Services_Weather * @link http://weather.noaa.gov/tg/site.shtml * @filesource */ require_once "DB.php"; // {{{ constants // {{{ natural constants and measures define("SERVICES_WEATHER_RADIUS_EARTH", 6378.15); // }}} // }}} // {{{ Services_Weather_checkData() /** * Services_Weather_checkData * * Checks the data for a certain string-length and if it either consists of * a certain char-type or a string of "-" as replacement. * * @param array $data The data to be checked * @param array $dataOrder Because the data is in different locations, we provide this * @return bool */ function Services_Weather_checkData($data, $dataOrder) { $return = true; foreach ($dataOrder as $type => $idx) { switch (strtolower($type)) { case "b": $len = 2; $func = "ctype_digit"; break; case "s": $len = 3; $func = "ctype_digit"; break; case "i": $len = 4; $func = "ctype_alnum"; break; default: break; } if ((strlen($data[$idx]) != $len) || (!$func($data[$idx]) && ($data[$idx] != str_repeat("-", $len)))) { $return = false; break; } } return $return; } // }}} // {{{ Services_Weather_getNextArg() /** * Services_Weather_getNextArg * * Checks, if the next argument is a parameter to a predecessing option. * Returns either that parameter or false, if the next argument is an option * * @param int $c Internal argument counter * @return string|bool */ function Services_Weather_getNextArg(&$c) { if ((($c + 1) < $_SERVER["argc"]) && ($_SERVER["argv"][$c + 1]{0} != "-")) { $c++; return $_SERVER["argv"][$c]; } else { return false; } } // }}} // First set a few variables for processing the options $modeSet = false; $saveFile = false; $printHelp = false; $invOpt = false; $verbose = 0; $dbType = "mysql"; $dbProt = "unix"; $dbName = "servicesWeatherDB"; $dbUser = "root"; $dbPass = ""; $dbHost = "localhost"; $dbOptions = array(); $userFile = ""; // Iterate through the arguments and check their validity for ($c = 1; $c < $_SERVER["argc"]; $c++) { switch ($_SERVER["argv"][$c]{1}) { case "l": // location-mode, if another mode is set, bail out if ($modeSet) { $printHelp = true; } else { $modeSet = true; $filePart = "bbsss"; $tableName = "metarLocations"; $dataOrder = array("b" => 0, "s" => 1, "i" => 2); } break; case "a": // dito for airport-mode if ($modeSet) { $printHelp = true; } else { $modeSet = true; $filePart = "cccc"; $tableName = "metarAirports"; $dataOrder = array("b" => 1, "s" => 2, "i" => 0); } break; case "f": // file-flag was provided, check if next argument is a string if (($userFile = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "s": // If you download the file, it will be saved to disk $saveFile = true; break; case "t": // The type of the DB to be used if (($dbType = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "r": // The protocol of the DB to be used if (($dbProt = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "d": // The name of the DB to be used if (($dbName = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "u": // The user of the DB to be used if (($dbUser = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "p": // The password of the DB to be used if (($dbPass = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "h": // The host of the DB to be used if (($dbHost = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } break; case "o": // Options for the DB if (($options = Services_Weather_getNextArg($c)) === false) { $printHelp = true; } else { $options = explode(",", $options); foreach ($options as $option) { $optPair = explode("=", $option); $dbOptions[$optPair[0]] = $optPair[1]; } } break; case "v": // increase verbosity for ($i = 1; $i < strlen($_SERVER["argv"][$c]); $i++) { if ($_SERVER["argv"][$c]{$i} == "v") { $verbose++; } else { $invOpt = true; break; } } break; default: // argument not valid, bail out $invOpt = true; break; } if ($invOpt) { // see above $printHelp = true; echo "Invalid option: '".$_SERVER["argv"][$c]."'\n"; break; } } // help-message if (!$modeSet || $printHelp) { echo "Usage: ".basename($_SERVER["argv"][0], ".php")." -l|-a [options]\n"; echo "Options:\n"; echo " -l build locationsDB\n"; echo " -a build airportsDB\n"; echo " -f use as input\n"; echo " -s save downloaded file to disk\n"; echo " -t type of the DB to be used\n"; echo " -r protocol -----\"----------\n"; echo " -d name ---------\"----------\n"; echo " -u user ---------\"----------\n"; echo " -p pass ---------\"----------\n"; echo " -h host ---------\"----------\n"; echo " -o options ------\"----------\n"; echo " in the notation option=value,...\n"; echo " -v display verbose debugging messages\n"; echo " multiple -v increases verbosity\n"; exit(255); } // check, if zlib is available if (extension_loaded("zlib")) { $open = "gzopen"; $close = "gzclose"; $files = array( $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt", "nsd_".$filePart.".gz", "http://weather.noaa.gov/data/nsd_".$filePart.".gz" ); } else { $open = "fopen"; $close = "fclose"; $files = array( $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt", "http://weather.noaa.gov/data/nsd_".$filePart.".txt" ); } // then try to open a source in the given order foreach ($files as $file) { $fp = @$open($file, "rb"); if ($fp) { // found a valid source if ($verbose > 0) { echo "Services_Weather: Using '".$file."' as source.\n"; } if ($saveFile && !file_exists($file)) { // apparently we want to save the file, and it's a remote file $file = basename($file); $fps = @$open($file, "wb"); if (!$fps) { echo "Services_Weather: Couldn't save to '".$file."'!\n"; } else { if ($verbose > 0) { echo "Services_Weather: Saving source to '".$file."'.\n"; } // read from filepointer and save to disk while ($line = fread($fp, 1024)) { fwrite($fps, $line, strlen($line)); } // unfortunately zlib does not support r/w on a resource, // so no rewind -> move $fp to new file on disk $close($fp); $close($fps); $fp = @$open($file, "rb"); } } break; } } if (!$fp) { // no files found, or connection not available... bail out die("Services_Weather: Sourcefile nsd_".$filePart." not found!\n"); } $dsn = $dbType."://".$dbUser.":".$dbPass."@".$dbProt."+".$dbHost."/".$dbName; $dsninfo = array( "phptype" => $dbType, "protocol" => $dbProt, "username" => $dbUser, "password" => $dbPass, "hostspec" => $dbHost, "database" => $dbName, "mode" => "0644" ); $db = DB::connect($dsninfo, $dbOptions); if (DB::isError($db)) { echo "Services_Weather: Connection to DB with '".$dbType."://".$dbUser.":PASS@".$dbHost."/".$dbName."' failed!\n"; die($db->getMessage()."\n"); } else { // Test, if we have to swipe or create the table first $select = "SELECT * FROM ".$tableName; $result = $db->query($select); if (DB::isError($result)) { // Create new table $create = "CREATE TABLE ".$tableName."(id int,block int,station int,icao varchar(4),name varchar(80),state varchar(2),country varchar(50),wmo int,latitude float,longitude float,elevation float,x float,y float,z float)"; if ($verbose > 0) { echo "Services_Weather: Creating table '".$tableName."'.\n"; } $result = $db->query($create); if (DB::isError($result)) { die($result->getMessage()."\n"); } } else { // Delete the old stuff $delete = "DELETE FROM ".$tableName; if ($verbose > 0) { echo "Services_Weather: Deleting from table '".$tableName."'.\n"; } $result = $db->query($delete); if (DB::isError($result)) { die($result->getMessage()."\n"); } } // Ok, DB should be up and running now, let's shove in the data $line = 0; $error = 0; // read data from file while ($data = fgetcsv($fp, 1000, ";")) { // Check for valid data if ((sizeof($data) < 9) || !Services_Weather_checkData($data, $dataOrder)) { echo "Services_Weather: Invalid data in file!\n"; echo "\tLine ".($line + 1).": ".implode(";", $data)."\n"; $error++; } else { // calculate latitude and longitude // it comes in a ddd-mm[-ss]N|S|E|W format $coord = array( "latitude" => 7, "longitude" => 8); foreach ($coord as $latlon => $aId) { preg_match("/^(\d{1,3})-(\d{1,2})(-(\d{1,2}))?([NSEW])$/", $data[$aId], $result); ${$latlon} = 0; $factor = 1; foreach ($result as $var) { if ((strlen($var) > 0) && ctype_digit($var)) { ${$latlon} += $var / $factor; $factor *= 60; } elseif (ctype_alpha($var) && in_array($var, array("S", "W"))) { ${$latlon} *= (-1); } } } // Calculate the cartesian coordinates for latitude and longitude $theta = deg2rad($latitude); $phi = deg2rad($longitude); $x = SERVICES_WEATHER_RADIUS_EARTH * cos($phi) * cos($theta); $y = SERVICES_WEATHER_RADIUS_EARTH * sin($phi) * cos($theta); $z = SERVICES_WEATHER_RADIUS_EARTH * sin($theta); // Check for elevation in data $elevation = is_numeric($data[11]) ? $data[11] : 0; // integers: convert "--" fields to null, empty fields to 0 foreach (array($dataOrder["b"], $dataOrder["s"], 6) as $i) { if (strpos($data[$i], "--") !== false) { $data[$i] = "null"; } elseif ($data[$i] == "") { $data[$i] = 0; } } // strings: quote foreach (array($dataOrder["i"], 3, 4, 5) as $i) { $data[$i] = $db->quote($data[$i]); } // insert data $insert = "INSERT INTO ".$tableName." VALUES(".($line - $error).","; $insert .= $data[$dataOrder["b"]].",".$data[$dataOrder["s"]].","; $insert .= $data[$dataOrder["i"]].",".$data[3].",".$data[4].","; $insert .= $data[5].",".$data[6].",".round($latitude, 4).","; $insert .= round($longitude, 4).",".$elevation.",".round($x, 4).","; $insert .= round($y, 4).",".round($z, 4).")"; $result = $db->query($insert); if (DB::isError($result)) { echo "\tLine ".($line + 1).": ".$insert."\n"; echo $result->getMessage()."\n"; $error++; } elseif($verbose > 2) { echo $insert."\n"; } } $line++; } // commit and close $db->disconnect(); if ($verbose > 0 || $error > 0) { echo "Services_Weather: ".($line - $error)." ".$tableName." added "; echo "to database '".$dbName."' (".$error." error(s)).\n"; } } $close($fp); ?>