Services_Weather
[ class tree: Services_Weather ] [ index: Services_Weather ] [ all elements ]

Source for file buildMetarDB.php

Documentation is available at buildMetarDB.php

  1. #!/usr/local/bin/php
  2. <?php
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  4.  
  5. /**
  6.  * This script downloads, saves and processes the textfiles needed for
  7.  * the building the databases to enable searching for METAR stations.
  8.  *
  9.  * You can download the locations, which is a database of about 12000 world-
  10.  * wide locations, which can be used to determine the coordinates of your
  11.  * city or you can download a file with 6500 airports providing the metar
  12.  * data. This database is used for the next-METAR-station search. Please see
  13.  * the apropriate documentation in the Services_Weather_Metar class.
  14.  *
  15.  * For usage of this script, invoke with '-h'.
  16.  *
  17.  * PHP versions 4 and 5
  18.  *
  19.  * <LICENSE>
  20.  * Copyright (c) 2005-2011, Alexander Wirtz
  21.  * All rights reserved.
  22.  *
  23.  * Redistribution and use in source and binary forms, with or without
  24.  * modification, are permitted provided that the following conditions
  25.  * are met:
  26.  * o Redistributions of source code must retain the above copyright notice,
  27.  *   this list of conditions and the following disclaimer.
  28.  * o Redistributions in binary form must reproduce the above copyright notice,
  29.  *   this list of conditions and the following disclaimer in the documentation
  30.  *   and/or other materials provided with the distribution.
  31.  * o Neither the name of the software nor the names of its contributors
  32.  *   may be used to endorse or promote products derived from this software
  33.  *   without specific prior written permission.
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  36.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  38.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  39.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  40.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  41.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  42.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  43.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  44.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  45.  * POSSIBILITY OF SUCH DAMAGE.
  46.  * </LICENSE>
  47.  *
  48.  * @category    Web Services
  49.  * @package     Services_Weather
  50.  * @subpackage  buildMetarDB
  51.  * @author      Alexander Wirtz <eru@php.net>
  52.  * @copyright   2005-2011 Alexander Wirtz
  53.  * @license     http://www.opensource.org/licenses/bsd-license.php  BSD License
  54.  * @version     SVN: $Id$
  55.  * @link        http://pear.php.net/package/Services_Weather
  56.  * @link        http://weather.noaa.gov/tg/site.shtml
  57.  * @filesource
  58.  */
  59.  
  60. require_once "DB.php";
  61.  
  62. // {{{ constants
  63. // {{{ natural constants and measures
  64. define("SERVICES_WEATHER_RADIUS_EARTH"6378.15);
  65. // }}}
  66. // }}}
  67.  
  68. // {{{ Services_Weather_checkData()
  69. /**
  70.  * Services_Weather_checkData
  71.  *
  72.  * Checks the data for a certain string-length and if it either consists of
  73.  * a certain char-type or a string of "-" as replacement.
  74.  *
  75.  * @param   array                           $data           The data to be checked
  76.  * @param   array                           $dataOrder      Because the data is in different locations, we provide this
  77.  * @return  bool 
  78.  */
  79. function Services_Weather_checkData($data$dataOrder)
  80. {
  81.     $return = true;
  82.     foreach ($dataOrder as $type => $idx{
  83.         switch (strtolower($type)) {
  84.             case "b":
  85.                 $len  = 2;
  86.                 $func "ctype_digit";
  87.                 break;
  88.             case "s":
  89.                 $len  = 3;
  90.                 $func "ctype_digit";
  91.                 break;
  92.             case "i":
  93.                 $len  = 4;
  94.                 $func "ctype_alnum";
  95.                 break;
  96.             default:
  97.                 break;
  98.         }
  99.         if ((strlen($data[$idx]!= $len|| (!$func($data[$idx]&& ($data[$idx!= str_repeat("-"$len)))) {
  100.             $return = false;
  101.             break;
  102.         }
  103.     }
  104.     return $return;
  105. }
  106. // }}}
  107.  
  108. // {{{ Services_Weather_getNextArg()
  109. /**
  110.  * Services_Weather_getNextArg
  111.  *
  112.  * Checks, if the next argument is a parameter to a predecessing option.
  113.  * Returns either that parameter or false, if the next argument is an option
  114.  *
  115.  * @param   int                             $c              Internal argument counter
  116.  * @return  string|bool
  117.  */
  118. {
  119.     if ((($c + 1$_SERVER["argc"]&& ($_SERVER["argv"][$c + 1]{0!= "-")) {
  120.         $c++;
  121.         return $_SERVER["argv"][$c];
  122.     else {
  123.         return false;
  124.     }
  125. }
  126. // }}}
  127.  
  128. // First set a few variables for processing the options
  129. $modeSet   = false;
  130. $saveFile  = false;
  131. $printHelp = false;
  132. $invOpt    = false;
  133. $verbose   = 0;
  134. $dbType    "mysql";
  135. $dbProt    "unix";
  136. $dbName    "servicesWeatherDB";
  137. $dbUser    "root";
  138. $dbPass    "";
  139. $dbHost    "localhost";
  140. $dbOptions = array();
  141. $userFile  "";
  142.  
  143. // Iterate through the arguments and check their validity
  144. for ($c = 1; $c $_SERVER["argc"]$c++{
  145.     switch ($_SERVER["argv"][$c]{1}{
  146.         case "l":
  147.             // location-mode, if another mode is set, bail out
  148.             if ($modeSet{
  149.                 $printHelp = true;
  150.             else {
  151.                 $modeSet   = true;
  152.                 $filePart  "bbsss";
  153.                 $tableName "metarLocations";
  154.                 $dataOrder = array("b" => 0"s" => 1"i" => 2);
  155.             }
  156.             break;
  157.         case "a":
  158.             // dito for airport-mode
  159.             if ($modeSet{
  160.                 $printHelp = true;
  161.             else {
  162.                 $modeSet   = true;
  163.                 $filePart  "cccc";
  164.                 $tableName "metarAirports";
  165.                 $dataOrder = array("b" => 1"s" => 2"i" => 0);
  166.             }
  167.             break;
  168.         case "f":
  169.             // file-flag was provided, check if next argument is a string
  170.             if (($userFile Services_Weather_getNextArg($c)) === false{
  171.                 $printHelp = true;
  172.             }
  173.             break;
  174.         case "s":
  175.             // If you download the file, it will be saved to disk
  176.             $saveFile      = true;
  177.             break;
  178.         case "t":
  179.             // The type of the DB to be used
  180.             if (($dbType Services_Weather_getNextArg($c)) === false{
  181.                 $printHelp = true;
  182.             }
  183.             break;
  184.         case "r":
  185.             // The protocol of the DB to be used
  186.             if (($dbProt Services_Weather_getNextArg($c)) === false{
  187.                 $printHelp = true;
  188.             }
  189.             break;
  190.         case "d":
  191.             // The name of the DB to be used
  192.             if (($dbName Services_Weather_getNextArg($c)) === false{
  193.                 $printHelp = true;
  194.             }
  195.             break;
  196.         case "u":
  197.             // The user of the DB to be used
  198.             if (($dbUser Services_Weather_getNextArg($c)) === false{
  199.                 $printHelp = true;
  200.             }
  201.             break;
  202.         case "p":
  203.             // The password of the DB to be used
  204.             if (($dbPass Services_Weather_getNextArg($c)) === false{
  205.                 $printHelp = true;
  206.             }
  207.             break;
  208.         case "h":
  209.             // The host of the DB to be used
  210.             if (($dbHost Services_Weather_getNextArg($c)) === false{
  211.                 $printHelp = true;
  212.             }
  213.             break;
  214.         case "o":
  215.             // Options for the DB
  216.             if (($options Services_Weather_getNextArg($c)) === false{
  217.                 $printHelp = true;
  218.             else {
  219.                 $options   explode(","$options);
  220.                 foreach ($options as $option{
  221.                     $optPair explode("="$option);
  222.                     $dbOptions[$optPair[0]] $optPair[1];
  223.                 }
  224.             }
  225.             break;
  226.         case "v":
  227.             // increase verbosity
  228.             for ($i = 1; $i strlen($_SERVER["argv"][$c])$i++{
  229.                 if ($_SERVER["argv"][$c]{$i== "v"{
  230.                     $verbose++;
  231.                 else {
  232.                     $invOpt    = true;
  233.                     break;
  234.                 }
  235.             }
  236.             break;
  237.         default:
  238.             // argument not valid, bail out
  239.             $invOpt    = true;
  240.             break;
  241.     }
  242.     if ($invOpt{
  243.         // see above
  244.         $printHelp = true;
  245.         echo "Invalid option: '".$_SERVER["argv"][$c]."'\n";
  246.         break;
  247.     }
  248. }
  249.  
  250. // help-message
  251. if (!$modeSet || $printHelp{
  252.     echo "Usage: ".basename($_SERVER["argv"][0]".php")." -l|-a [options]\n";
  253.     echo "Options:\n";
  254.     echo "  -l              build locationsDB\n";
  255.     echo "  -a              build airportsDB\n";
  256.     echo "  -f <file>       use <file> as input\n";
  257.     echo "  -s              save downloaded file to disk\n";
  258.     echo "  -t <dbtype>     type of the DB to be used\n";
  259.     echo "  -r <dbprotocol> protocol -----\"----------\n";
  260.     echo "  -d <dbname>     name ---------\"----------\n";
  261.     echo "  -u <dbuser>     user ---------\"----------\n";
  262.     echo "  -p <dbpass>     pass ---------\"----------\n";
  263.     echo "  -h <dbhost>     host ---------\"----------\n";
  264.     echo "  -o <dboptions>  options ------\"----------\n";
  265.     echo "                  in the notation option=value,...\n";
  266.     echo "  -v              display verbose debugging messages\n";
  267.     echo "                  multiple -v increases verbosity\n";
  268.     exit(255);
  269. }
  270.  
  271. // check, if zlib is available
  272. if (extension_loaded("zlib")) {
  273.     $open  "gzopen";
  274.     $close "gzclose";
  275.     $files = array(
  276.         $userFile"nsd_".$filePart"nsd_".$filePart.".txt",
  277.         "nsd_".$filePart.".gz""http://weather.noaa.gov/data/nsd_".$filePart.".gz"
  278.     );
  279. else {
  280.     $open  "fopen";
  281.     $close "fclose";
  282.     $files = array(
  283.         $userFile"nsd_".$filePart"nsd_".$filePart.".txt",
  284.         "http://weather.noaa.gov/data/nsd_".$filePart.".txt"
  285.     );
  286. }
  287. // then try to open a source in the given order
  288. foreach ($files as $file{
  289.     $fp @$open($file"rb");
  290.     if ($fp{
  291.         // found a valid source
  292.         if ($verbose > 0{
  293.             echo "Services_Weather: Using '".$file."' as source.\n";
  294.         }
  295.         if ($saveFile && !file_exists($file)) {
  296.             // apparently we want to save the file, and it's a remote file
  297.             $file basename($file);
  298.             $fps @$open($file"wb");
  299.             if (!$fps{
  300.                 echo "Services_Weather: Couldn't save to '".$file."'!\n";
  301.             else {
  302.                 if ($verbose > 0{
  303.                     echo "Services_Weather: Saving source to '".$file."'.\n";
  304.                 }
  305.                 // read from filepointer and save to disk
  306.                 while ($line fread($fp1024)) {
  307.                     fwrite($fps$linestrlen($line));
  308.                 }
  309.                 // unfortunately zlib does not support r/w on a resource,
  310.                 // so no rewind -> move $fp to new file on disk
  311.                 $close($fp);
  312.                 $close($fps);
  313.                 $fp @$open($file"rb");
  314.             }
  315.         }
  316.         break;
  317.     }
  318. }
  319. if (!$fp{
  320.     // no files found, or connection not available... bail out
  321.     die("Services_Weather: Sourcefile nsd_".$filePart." not found!\n");
  322. }
  323.  
  324. $dsn     $dbType."://".$dbUser.":".$dbPass."@".$dbProt."+".$dbHost."/".$dbName;
  325. $dsninfo = array(
  326.     "phptype"  => $dbType,
  327.     "protocol" => $dbProt,
  328.     "username" => $dbUser,
  329.     "password" => $dbPass,
  330.     "hostspec" => $dbHost,
  331.     "database" => $dbName,
  332.     "mode"     => "0644"
  333. );
  334.  
  335. $db  = DB::connect($dsninfo$dbOptions);
  336. if (DB::isError($db)) {
  337.     echo "Services_Weather: Connection to DB with '".$dbType."://".$dbUser.":PASS@".$dbHost."/".$dbName."' failed!\n";
  338.     die($db->getMessage()."\n");
  339. else {
  340.     // Test, if we have to swipe or create the table first
  341.     $select "SELECT * FROM ".$tableName;
  342.     $result $db->query($select);
  343.  
  344.     if (DB::isError($result)) {
  345.         // Create new table
  346.         $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)";
  347.         if ($verbose > 0{
  348.             echo "Services_Weather: Creating table '".$tableName."'.\n";
  349.         }
  350.         $result  $db->query($create);
  351.         if (DB::isError($result)) {
  352.             die($result->getMessage()."\n");
  353.         }
  354.     else {
  355.         // Delete the old stuff
  356.         $delete "DELETE FROM ".$tableName;
  357.         if ($verbose > 0{
  358.             echo "Services_Weather: Deleting from table '".$tableName."'.\n";
  359.         }
  360.         $result $db->query($delete);
  361.         if (DB::isError($result)) {
  362.             die($result->getMessage()."\n");
  363.         }
  364.     }
  365.  
  366.     // Ok, DB should be up and running now, let's shove in the data
  367.     $line   = 0;
  368.     $error  = 0;
  369.     // read data from file
  370.     while ($data fgetcsv($fp1000";")) {
  371.         // Check for valid data
  372.         if ((sizeof($data< 9|| !Services_Weather_checkData($data$dataOrder)) {
  373.                 echo "Services_Weather: Invalid data in file!\n";
  374.                 echo "\tLine ".($line + 1).": ".implode(";"$data)."\n";
  375.                 $error++;
  376.         else {
  377.             // calculate latitude and longitude
  378.             // it comes in a ddd-mm[-ss]N|S|E|W format
  379.             $coord = array"latitude" => 7"longitude" => 8);
  380.             foreach ($coord as $latlon => $aId{
  381.                 preg_match("/^(\d{1,3})-(\d{1,2})(-(\d{1,2}))?([NSEW])$/"$data[$aId]$result);
  382.                 ${$latlon= 0; $factor = 1;
  383.                 foreach ($result as $var{
  384.                     if ((strlen($var> 0&& ctype_digit($var)) {
  385.                         ${$latlon+= $var $factor;
  386.                         $factor *= 60;
  387.                     elseif (ctype_alpha($var&& in_array($vararray("S""W"))) {
  388.                         ${$latlon*= (-1);
  389.                     }
  390.                 }
  391.             }
  392.  
  393.             // Calculate the cartesian coordinates for latitude and longitude
  394.             $theta deg2rad($latitude);
  395.             $phi   deg2rad($longitude);
  396.  
  397.             $x SERVICES_WEATHER_RADIUS_EARTH * cos($phicos($theta);
  398.             $y SERVICES_WEATHER_RADIUS_EARTH * sin($phicos($theta);
  399.             $z SERVICES_WEATHER_RADIUS_EARTH             * sin($theta);
  400.  
  401.             // Check for elevation in data
  402.             $elevation is_numeric($data[11]$data[11: 0;
  403.  
  404.             // integers: convert "--" fields to null, empty fields to 0
  405.             foreach (array($dataOrder["b"]$dataOrder["s"]6as $i{
  406.                 if (strpos($data[$i]"--"!== false{
  407.                     $data[$i"null";
  408.                 elseif ($data[$i== ""{
  409.                     $data[$i= 0;
  410.                 }
  411.             }
  412.  
  413.             // strings: quote
  414.             foreach (array($dataOrder["i"]345as $i{
  415.                 $data[$i$db->quote($data[$i]);
  416.             }
  417.  
  418.             // insert data
  419.             $insert  "INSERT INTO ".$tableName." VALUES(".($line $error).",";
  420.             $insert .= $data[$dataOrder["b"]].",".$data[$dataOrder["s"]].",";
  421.             $insert .= $data[$dataOrder["i"]].",".$data[3].",".$data[4].",";
  422.             $insert .= $data[5].",".$data[6].",".round($latitude4).",";
  423.             $insert .= round($longitude4).",".$elevation.",".round($x4).",";
  424.             $insert .= round($y4).",".round($z4).")";
  425.  
  426.             $result $db->query($insert);
  427.             if (DB::isError($result)) {
  428.                 echo "\tLine ".($line + 1).": ".$insert."\n";
  429.                 echo $result->getMessage()."\n";
  430.                 $error++;
  431.             elseif($verbose > 2{
  432.                 echo $insert."\n";
  433.             }
  434.         }
  435.         $line++;
  436.     }
  437.     // commit and close
  438.     $db->disconnect();
  439.     if ($verbose > 0 || $error > 0{
  440.         echo "Services_Weather: ".($line $error)." ".$tableName." added ";
  441.         echo "to database '".$dbName."' (".$error." error(s)).\n";
  442.     }
  443. }
  444. $close($fp);
  445. ?>

Documentation generated on Mon, 11 Mar 2019 15:50:57 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.