<?php
/*=======================================================================
 *
 * pnpoly.inc.php
 *
 * Autor: Carsten Annacker
 *
 =======================================================================*/

include_once("../include/dbglobal.inc.php");
include_once("../include/caglobal.inc.php");
include_once("../include/global.inc.php");

/*
 * Test der pnpoly Funktion:
 * Die PLZ-Mittelpunkte werden gegen die PLZ-Polygone geprüft
 *
 */

/*
set_time_limit(300);
$sqlQuery = "SELECT srvp_plz, srvp_longitude, srvp_latitude FROM serviceplz ORDER BY srvp_plz" ;
$res = mysql_query($sqlQuery) or reportDie ($sqlQuery);

while ($row = mysql_fetch_array($res, MYSQL_ASSOC)):
  echo "\n<br>\n<br>" . $row["srvp_plz"] . ": ";
  echo "(" . $row["srvp_longitude"] . ", " . $row["srvp_latitude"] . "): ";
  if (isInZipcodeArea($row["srvp_longitude"], $row["srvp_latitude"], $row["srvp_plz"]))
    echo "IN";
  else
    echo "OUT";
endwhile;
mysql_free_result($res);
*/

define ("MAX_SEARCH_RADIUS",  5);         // in km
define ("MAX_SEARCHES"     ,  10);        // max. Anzahl der Umkreissuchen
define ("GEO_EARTH_RADIUS" ,  6371.0);    // in km
//define ("DATABASE"         , "phoenix_special");
define ("TBL_PLZ"          , "phoenix_special.plz_geodb");
define ("FLD_BREITE"       , "plzg_breite");
define ("FLD_LAENGE"       , "plzg_laenge");
define ("FLD_PLZ_CODE"     , "plzg_code");

/*
 * Test der findZipcodeArea Funktion:
 * Willkürliche Punkte finden
 *
 */
/*
$pointLong = 13.734444;
$pointLat  = 51.077499;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 9.974722;
$pointLat  = 53.562500;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 9.956100;
$pointLat  = 53.586899;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 9.946900;
$pointLat  = 53.580002;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 9.966400;
$pointLat  = 53.568100;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 8.859259;
$pointLat  = 53.140369;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 8.703900;
$pointLat  = 53.179699;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 13.398333;
$pointLat  = 52.529167;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 13.404722;
$pointLat  = 52.520557;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 13.448611;
$pointLat  = 52.509445;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 13.660000;
$pointLat  = 53.100361;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
$pointLong = 0.0;
$pointLat  = 0.0;
echo "($pointLong, $pointLat): " . findZipcodeArea($pointLong, $pointLat) . "<br>";
*/
/*
 * PLZ-Bereich finden, in dem der spezifizierte Punkt liegt
 *
 */

getDb2Connection();     // Try to connect request server because of performance

function findZipcodeArea($pointLong, $pointLat)
{
//  global $dbhost, $dblogin, $dbpassword;
  global $db2;

  trace_execution_time_start();
//  $mysqlLink = mysql_connect($dbhost, $dblogin, $dbpassword)
//    or reportDie (mysql_error($mysqlLink));
//  mysql_select_db(DATABASE, $mysqlLink)
//    or reportDie (mysql_error($mysqlLink));
  $max_search_radius = 0;
  $plz_sql = "";
  // Umkreissuche über die umliegenden PLZ-Bereiche.
  // Der Suchradius wird erhöht, wenn kein Bereich gefunden wurde,
  // solange bis er gefunden wird (irgendwo musser ja sein).
  do {
    $max_search_radius += MAX_SEARCH_RADIUS;
    if ($plz_sql != "")
      $plz_sql_clause = " AND " . FLD_PLZ_CODE . " NOT IN ($plz_sql)";
    $sqlQuery = "SELECT " . FLD_PLZ_CODE . " AS plz FROM " /* . DATABASE . "." */ . TBL_PLZ . " WHERE " .
      "(ACOS((SIN(" . deg2rad($pointLat) . ")*SIN(RADIANS(" . FLD_BREITE . "))) + " .      "(COS(" . deg2rad($pointLat) . ")*COS(RADIANS(" . FLD_BREITE . "))*COS(RADIANS(" . FLD_LAENGE . ")-" .
      deg2rad($pointLong) . "))) * " . GEO_EARTH_RADIUS . ") < " . $max_search_radius . $plz_sql_clause;
//    $res = mysql_query($sqlQuery, $mysqlLink) or reportDie ("Error in query: '$sqlQuery': " . mysql_error($mysqlLink));
    $res = $db2->query($sqlQuery);
    if (DB::isError($res)) reportDie ("$PHP_SELF: '$sqlQuery': " . $res->getMessage());
    $i = 0;
//    while ($row = mysql_fetch_array($res, MYSQL_ASSOC)):
    while ($row = $res->fetch_assoc()):
//echo "$max_search_radius: " . $row["plz"] . "<br>";
      if (isInZipcodeArea($pointLong, $pointLat, $row["plz"])) {
        writeLog(trace_execution_time_stop() . "  " . $row["plz"] . "  (" . sprintf("%-13s", $pointLong) . ", " . sprintf("%-13s", $pointLat) . "), \$max_search_radius =" . sprintf("%2s", $max_search_radius) . " Km, \$plz_sql = $plz_sql");
        return $row["plz"];
      }
      if ($plz_sql != "")
        $plz_sql .= ",";
      $plz_sql .= "'" . $row["plz"] . "'";  
    endwhile;
//    mysql_free_result($res);
    $res->free();
  } while($max_search_radius < MAX_SEARCH_RADIUS * MAX_SEARCHES);
  writeLog(trace_execution_time_stop() . "  " . "*****" . "  (" . sprintf("%-13s", $pointLong) . ", " . sprintf("%-13s", $pointLat) . "), \$max_search_radius =" . sprintf("%2s", $max_search_radius) . " Km, \$plz_sql = $plz_sql");
//  mysql_close($mysqlLink);
  return "";
}

/*
 * Testen ob der Punkt in $pointDeg (Array von Länge, Breite) im PLZ-Bereich zipcode liegt
 *
 */
function isInZipcodeArea($pointLong, $pointLat, $zipcode)
{
  global $db2;
  
  $plzp_polygons  = $db2->getOne("SELECT plzp_polygons FROM phoenix_special.plz_polygon WHERE plzp_code = '$zipcode'");  
  $polygons = explode("|", $plzp_polygons);
  foreach($polygons as $polygon) {  
    $polygonPointsX = array();
    $polygonPointsY = array();
//    $polygonPointsCnt = 0;
    $points = explode(",", $polygon);
    foreach($points as $point) {
      list($plzp_x_deg, $plzp_y_deg) = explode(" ", $point);
      array_push ($polygonPointsX, $plzp_x_deg);
      array_push ($polygonPointsY, $plzp_y_deg);
//      $polygonPointsCnt++;
    }
//    echo "<br>cn_PnPoly: ";
//    if (cn_PnPoly($polygonPointsX, $polygonPointsY, $pointDeg[0], $pointDeg[1]))
//      echo "IN<br>";
//    else
//      echo "OUT<br>";
//    echo "pnpoly:    ";
    if (pnpoly($polygonPointsX, $polygonPointsY, $pointLong, $pointLat))
      return true;
//      echo "IN<br>";
//    else
//      echo "OUT<br>";
  }
  return false;
}

/*
 * Quelle: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
 *
 * Original C-code:
 *
 * int pnpoly(int npol, float *xp, float *yp, float x, float y)
 * {
 *   int i, j, c = 0;
 *   for (i = 0, j = npol-1; i < npol; j = i++) {
 *     if ((((yp[i]<=y) && (y<yp[j])) ||
 *          ((yp[j]<=y) && (y<yp[i]))) &&
 *         (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
 * 
 *       c = !c;
 *   }
 *   return c;
 * }
 */
function pnpoly($px, $py, $x, $y)
{

  $npol = count($px);
  $c = false;
  for ($i = 0, $j = $npol - 1; $i < $npol; $j = $i++) {
    if (((($py[$i] <= $y) && ($y < $py[$j])) ||
         (($py[$j] <= $y) && ($y < $py[$i]))) &&
        ($x < ($px[$j] - $px[$i]) * ($y - $py[$i]) / ($py[$j] - $py[$i]) + $px[$i]))
      $c = !$c;
  }
  return $c;
}

/*
 * Quelle: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
 *
 * Original C-code:
 *
 * // cn_PnPoly(): crossing number test for a point in a polygon
 * //      Input:   P = a point,
 * //               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
 * //      Return:  0 = outside, 1 = inside
 * // This code is patterned after [Franklin, 2000]
 * int cn_PnPoly( Point P, Point* V, int n )
 * {
 *     int    cn = 0;    // the crossing number counter
 *
 *     // loop through all edges of the polygon
 *     for (int i=0; i<n; i++) {    // edge from V[i] to V[i+1]
 *        if (((V[i].y <= P.y) && (V[i+1].y > P.y))    // an upward crossing
 *         || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
 *             // compute the actual edge-ray intersect x-coordinate
 *             float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
 *             if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
 *                 ++cn;   // a valid crossing of y=P.y right of P.x
 *         }
 *     }
 *     return (cn&1);    // 0 if even (out), and 1 if odd (in)
 *
 * }
 *
 */
/* 
function cn_PnPoly($px, $py, $x, $y)
{
  $n = count($px) - 1;
  $cn = 0;    // the crossing number counter

  // loop through all edges of the polygon
  for ($i = 0; $i < $n; $i++) {    // edge from V[i] to V[i+1]
     if ((($py[$i] <= $y) && ($py[$i + 1] > $y))    // an upward crossing
      || (($py[$i] > $y) && ($py[$i + 1] <= $y))) { // a downward crossing
          // compute the actual edge-ray intersect x-coordinate
          $vt = (float)($y - $py[$i]) / ($py[$i + 1] - $py[$i]);
          if ($x < $px[$i] + $vt * ($px[$i + 1] - $px[$i])) // $x < intersect
              ++$cn;   // a valid crossing of y=$y right of $x
      }
  }
  return ($cn % 2);    // 0 if even (out), and 1 if odd (in)
}
*/

function writeLog($log_text) {
//  echo "[" . date("Y-m-d H:i:s O") . "] " . $log_text .  "\n";
  $today = getdate();
  $fileHandle = @fopen("../log/pnpoly_" . $today['year'] . sprintf("%02d", $today['mon']) . ".log", 'a');
  @fwrite($fileHandle, "[" . date("Y-m-d H:i:s") . "] " . $log_text .  "\n");
  @fclose($fileHandle);
  return;
}

?>
