1 ) { // called from the command-line parse_str(implode("&", $argv), $_GET); } else { header("Content-Type: text/plain"); header('Expires: ' . date("r")); } // // Check inputs for SQL statement injection and such. // Items not tested below are not vulnerable for SQL statement injection, e.g. // a time is always converted using 'strtotime()' and other items are only // used in the PHP but never in the composition of an SQL statement // [security audit 2013-10-23] // if( (isset($_GET['zkl']) && !is_numeric($_GET['zkl'])) || (isset($_GET['logfile']) && !preg_match('/^[0-9]+,[0-9]+,[0-9]+$/', $_GET['logfile']) && !in_array($_GET['logfile'], array('log_tcp','log_versienummer','log_realtime','log_secure_zkl'))) || (isset($_GET['db']) && dirname($_GET['db']) != ".") ) { // don't do anything fancy or interruptive as fiddling with the GET parameters // is intended by the author die("Parameter error"); } require_once("support.inc.php"); // read the database information if( isset($_GET['db']) ) $db_info = read_database(DBCONFIG_DIR, $_GET['db']); else $db_info = array(); if( !isset($db_info['host']) ) $db_info['host'] = "localhost"; if( !isset($db_info['user']) ) $db_info['user'] = "root"; if( !isset($db_info['passwd']) ) $db_info['passwd'] = ""; if( !isset($db_info['db']) ) $db_info['db'] = "di_zkl"; // open the database $db_handle = mysql_connect($db_info['host'], $db_info['user'], $db_info['passwd']); if( $db_handle === FALSE ) { echo mysql_error(); exit(1); } mysql_select_db($db_info['db'], $db_handle); if( $db_info['main'] ) { $db_main_info = read_database(DBCONFIG_DIR, $db_info['main']); $db_main_handle = mysql_connect($db_main_info['host'], $db_main_info['user'], $db_main_info['passwd'], true); if( $db_main_handle === FALSE ) { echo mysql_error(); exit(1); } mysql_select_db($db_main_info['database'], $db_main_handle); } else { $db_main_handle = $db_data_handle; $db_main_info = $db_info; } // logfile to use $zkl = $_GET['zkl']; list($sdcard,$rpgmcount,$startup) = explode(',', $_GET['logfile']); echo "logfile: " . $_GET['logfile'] . "\n"; echo str_repeat("-", 9 + strlen($_GET['logfile'])) . "\n"; // process log data global $designs; $designs = array(); $query = "SELECT * "; $query .= "FROM log_zkl "; $query .= "WHERE "; $query .= " zkl=" . $zkl . " AND "; $query .= " sdcard=" . $sdcard . " AND "; $query .= " rpgmcount=" . $rpgmcount . " AND "; $query .= " startup=" . $startup . " AND "; $query .= " ("; $query .= " ("; $query .= " major=0xB AND "; $query .= " minor BETWEEN 0x80 AND 0x8F"; $query .= " ) OR ("; $query .= " major=0xD AND "; $query .= " minor=0x30"; $query .= " )"; $query .= " ) "; $query .= "ORDER BY id"; $result = mysql_run($query, $db_handle); while( $row = mysql_fetch_assoc($result) ) { //echo "LOG " . $row['id'] . " " . $row['tijd'] . " " . sprintf("%X.%02X", $row['major'], $row['minor']) . "\n"; if( $row['major'] == 0xB ) { switch( $row['minor'] ) { case 0x80: // design $gquery = "SELECT * FROM log_geofence WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); $designs[$grow['design']] = array(); $designs[$grow['design']]['name'] = $grow['naam']; $designs[$grow['design']]['n'] = $grow['n_objects']; $designs[$grow['design']]['inside'] = 0; echo " []-+ CREATE " . $designs[$grow['design']]['name'] . "\n"; break; case 0x81: // object $gquery = "SELECT * FROM log_geofence_object WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); $designs[$grow['design']][$grow['object']] = array(); $designs[$grow['design']][$grow['object']]['n'] = $grow['n_points']; $designs[$grow['design']][$grow['object']]['type'] = $grow['type']; $designs[$grow['design']][$grow['object']]['r'] = $grow['radius']; echo " | ADD (" . $grow['design'] . "," . $grow['object'] . "), " . $grow['type'] . ", r=" . $grow['radius'] . ", n=" . $grow['n_points'] . "\n"; break; case 0x82: // point $gquery = "SELECT * FROM log_geofence_point WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); $p = array(); $p['x'] = deg2rad($grow['longitude']); $p['y'] = deg2rad($grow['latitude']); $designs[$grow['design']][$grow['object']][] = $p; geofence_update_bbox($grow['design'], $grow['object'], $p); break; case 0x87: // delete $gquery = "SELECT * FROM log_geofence WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); echo " []-- DELETE " . $designs[$grow['design']]['name'] . "\n"; array_splice($designs, $grow['design'], 1); break; case 0x88: // enter $gquery = "SELECT * FROM log_geofence WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); echo $row['tijd'] . ": log GEOFENCE ENTER " . $designs[$grow['design']]['name'] . "\n"; break; case 0x89: // exit $gquery = "SELECT * FROM log_geofence WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); echo $row['tijd'] . ": log GEOFENCE EXIT " . $designs[$grow['design']]['name'] . "\n"; break; default: break; } } else if( $row['major'] == 0xD ) { $gquery = "SELECT * FROM log_gps WHERE id=" . $row['id']; $grow = mysql_fetch_assoc(mysql_run($gquery, $db_handle)); if( $grow['fix'] ) { $p = array( 'x' => deg2rad($grow['longitude']), 'y' => deg2rad($grow['latitude']) ); geofence_test($p, $row['tijd'], $grow['speed'], $grow['heading']); } else { echo $row['tijd'] . ": fix lost\n"; } } } function print_pt($p) { // print in 'latitude,longitude' order return sprintf("(%.6f,%.6f)", rad2deg($p['y']), rad2deg($p['x'])); } function print_rect($rect) { return print_pt($rect['p1']) . "-" . print_pt($rect['p2']); } function geofence_normalize($rect) { if( $rect['p1']['x'] > $rect['p2']['x'] ) { $t = $rect['p1']['x']; $rect['p1']['x'] = $rect['p2']['x']; $rect['p2']['x'] = $t; } if( $rect['p1']['y'] > $rect['p2']['y'] ) { $t = $rect['p1']['y']; $rect['p1']['y'] = $rect['p2']['y']; $rect['p2']['y'] = $t; } return $rect; } function bbox_get($p, $r) { $bbox = array(); if( $r == 0.0 ) { $bbox['p1']['x'] = $bbox['p2']['x'] = $p['x']; $bbox['p1']['y'] = $bbox['p2']['y'] = $p['y']; return $bbox; } else { $r = ($r / M_CIRC_EARTH) * 2.0 * M_PI; $sin_px = sin($p['x']); $cos_px = cos($p['x']); //$sin_py = sin($p['y']); $cos_py = cos($p['y']); $r -= $r * M_Y_FLATTENING * $cos_py; $sin_r = sin($r); $cos_r = cos($r); $bbox['p1']['x'] = asin($sin_px * $cos_r - $cos_px * $sin_r); $bbox['p2']['x'] = asin($sin_px * $cos_r + $cos_px * $sin_r); $bbox['p1']['y'] = $p['y'] - asin( $sin_r / $cos_px); $bbox['p2']['y'] = $p['y'] - asin(-$sin_r / $cos_px); return geofence_normalize($bbox); } } function bbox_set($bbox, $rect) { $empty = !isset($bbox); if( $empty || $rect['p1']['x'] < $bbox['p1']['x'] ) $bbox['p1']['x'] = $rect['p1']['x']; if( $empty || $rect['p1']['y'] < $bbox['p1']['y'] ) $bbox['p1']['y'] = $rect['p1']['y']; if( $empty || $rect['p2']['x'] > $bbox['p2']['x'] ) $bbox['p2']['x'] = $rect['p2']['x']; if( $empty || $rect['p2']['y'] > $bbox['p2']['y'] ) $bbox['p2']['y'] = $rect['p2']['y']; return $bbox; } function geofence_update_bbox($i, $j, $p) { global $designs; $r = $designs[$i][$j]['r']; switch( $designs[$i][$j]['type'] ) { case 'point': case 'line': case 'track': $bbox = bbox_get($p, $r); break; default: $bbox = bbox_get($p, 0.0); break; } $designs[$i][$j]['bbox'] = bbox_set($designs[$i][$j]['bbox'], $bbox); $designs[$i]['bbox'] = bbox_set($designs[$i]['bbox'], $bbox); } function georect_within($rect, $p) { $inside = $p['x'] >= $rect['p1']['x'] && $p['x'] <= $rect['p2']['x'] && $p['y'] >= $rect['p1']['y'] && $p['y'] <= $rect['p2']['y']; // if( $inside ) { // echo "Inside rect: " . print_pt($p) . " - " . print_rect($rect) . "\n"; // } // else { // echo "Outside rect: " . print_pt($p) . " - " . print_rect($rect) . "\n"; // } return $inside; } function geopos_dist($p, $q) { // calculate the sine and cosine of the co-ordinates $sin_px = sin($p['x']); $cos_px = cos($p['x']); $sin_py = sin($p['y']); $cos_py = cos($p['y']); $sin_qx = sin($q['x']); $cos_qx = cos($q['x']); $sin_qy = sin($q['y']); $cos_qy = cos($q['y']); // intermediate values $a = $cos_qy * $cos_qx - $cos_py * $cos_px; $b = $cos_qy * $sin_qx - $cos_py * $sin_px; $c = $sin_qy - $sin_py ; // compensate radius for the flattening of the earth $r_x2 = M_2R_EARTH; $r_x2 -= $r_x2 * $cos_qy * M_Y_FLATTENING; return $r_x2 * asin( 0.5 * sqrt($a * $a + $b * $b + $c * $c) ); } function geoline_dist($p1, $p2, $q) { $x2_x1 = $p2['x'] - $p1['x']; $y2_y1 = $p2['y'] - $p1['y']; $x1_x0 = $p1['x'] - $q['x']; $y1_y0 = $p1['y'] - $q['y']; // distance in radians $d = abs(($x2_x1 * $y1_y0) - ($x1_x0 * $y2_y1)) / sqrt(($x2_x1 * $x2_x1) + ($y2_y1 * $y2_y1)); // compensate radius for the flattening of the earth $r = M_R_EARTH; $r -= $r * cos($q['y']) * M_Y_FLATTENING; return $r * $d; } function geoline_project_pt($p1, $p2, $q) { $x2_x1 = $p2['x'] - $p1['x']; $y2_y1 = $p2['y'] - $p1['y']; $x0_x1 = $q['x'] - $p1['x']; $y0_y1 = $q['y'] - $p1['y']; $x0_x2 = $q['x'] - $p2['x']; $y0_y2 = $q['y'] - $p2['y']; return (($x0_x1 * $x2_x1) + ($y0_y1 * $y2_y1)) / (($x2_x1 * $x2_x1) + ($y2_y1 * $y2_y1)); } function geofence_test_point($i, $j, $p) { global $designs; $has_match = FALSE; $r = $designs[$i][$j]['r']; $d = geopos_dist($designs[$i][$j][0], $p); if( $d <= $r ) { echo " + point " . print_pt($designs[$i][$j][0]) . " matches (d=" . sprintf("%.1f", $d) . ")\n"; return TRUE; } else if( $d <= ($r * 1.5) ) { // near miss echo " - point " . print_pt($designs[$i][$j][0]) . " does NOT match (d=" . sprintf("%.1f", $d) . ")\n"; return FALSE; } } function geofence_test_lines($i, $j, $p) { global $designs; $has_match = FALSE; $r = $designs[$i][$j]['r']; // // TRIAL // // Test lines but endcap "butt" instead of "round" // for( $k = 1; $k < $designs[$i][$j]['n'] - 1; $k++ ) { $d = geopos_dist($designs[$i][$j][$k], $p); if( $d <= $r ) { if( !$has_match ) { echo " + point " . print_pt($designs[$i][$j][$k]) . " matches (d=" . sprintf("%.1f", $d) . ")\n"; $has_match = TRUE; } else { echo " point " . print_pt($designs[$i][$j][$k]) . " matches (d=" . sprintf("%.1f", $d) . ") NOT TESTED\n"; } } else if( $d <= ($r * 1.5) ) { // near miss echo " - point " . print_pt($designs[$i][$j][$k]) . " does NOT match (d=" . sprintf("%.1f", $d) . ")\n"; } } for( $k = 1; $k < $designs[$i][$j]['n']; $k++ ) { if( !( $designs[$i][$j][$k - 1]['x'] == $designs[$i][$j][$k]['x'] && $designs[$i][$j][$k - 1]['y'] == $designs[$i][$j][$k]['y'] ) ) { $d_p1_p0 = geoline_project_pt($designs[$i][$j][$k - 1], $designs[$i][$j][$k], $p); $d = geoline_dist($designs[$i][$j][$k - 1], $designs[$i][$j][$k], $p); if( 0.0 <= $d_p1_p0 && $d_p1_p0 <= 1.0 && $d <= $r ) { if( !$has_match ) { echo " + line " . print_pt($designs[$i][$j][$k - 1]) . "-" . print_pt($designs[$i][$j][$k]) . " matches (d=" . sprintf("%.1f, %.2f%%", $d, $d_p1_p0 * 100.0) . ")\n"; $has_match = TRUE; } else { echo " line " . print_pt($designs[$i][$j][$k - 1]) . "-" . print_pt($designs[$i][$j][$k]) . " matches (d=" . sprintf("%.1f, %.2f%%", $d, $d_p1_p0 * 100.0) . ") NOT TESTED\n"; } } else if( -0.5 <= $d_p1_p0 && $d_p1_p0 <= 1.5 && $d <= ($r * 1.5) ) { // near miss echo " - line " . print_pt($designs[$i][$j][$k - 1]) . "-" . print_pt($designs[$i][$j][$k]) . " does NOT match (d=" . sprintf("%.1f, %.2f%%", $d, $d_p1_p0 * 100.0) . ")\n"; } } } return $has_match; } // Test if point 'p' lies strictly within the rectangle described by 'points' function geofence_test_rect($i, $j, $p) { global $designs; // the object must have at least two points if( $designs[$i][$j]['n'] < 2 ) return FALSE; // create a normalized rectangle from the first two points in the array; // excessive points are ignored $rect = geofence_normalize(array('p1' => $designs[$i][$j][0], 'p2' => $designs[$i][$j][1])); if( georect_within($rect, $p) ) { echo " + rect " . print_pt($designs[$i][$j][0]) . "-" . print_pt($designs[$i][$j][1]) . " matches\n"; return TRUE; } else { echo " - rect " . print_pt($designs[$i][$j][0]) . "-" . print_pt($designs[$i][$j][1]) . " does NOT match\n"; return FALSE; } } // Test if point 'p' lies strictly within the polygon described by 'points', using // odd/even rule and the crossing algorithm, casting a ray from 'p' to the right function geofence_test_polygon($i, $j, $p) { global $designs; $crossings = 0; $p_x = $p['x']; $p_y = $p['y']; for( $k = 0; $k < $designs[$i][$j]['n']; $k++ ) { // get the vertex $p1_x = $designs[$i][$j][$k]['x']; $p1_y = $designs[$i][$j][$k]['y']; if( $k == $designs[$i][$j]['n'] - 1 ) { // last point: close the polygon $p2_x = $designs[$i][$j][0]['x']; $p2_y = $designs[$i][$j][0]['y']; } else { // next point; also increment the point in the array $p2_x = $designs[$i][$j][$k+1]['x']; $p2_y = $designs[$i][$j][$k+1]['y']; } // the vertex is not interesting if it is fully to the left of 'p' if( $p1_x < $p_x && $p2_x < $p_x ) { echo " vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": left)\n"; continue; } // nor is it interesting if it is fully below or above 'p' if( $p1_y < $p_y && $p2_y < $p_y ) { echo " vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": below)\n"; continue; } if( $p1_y > $p_y && $p2_y > $p_y ) { echo " vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": above)\n"; continue; } // potentially interesting line; is it fully to the right of 'p'? if( $p1_x > $p_x && $p2_x > $p_x ) { // yup, it crosses the ray casted from 'p' to the right echo " + vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": right)\n"; $crossings++; continue; } // some extra work is needed to see if the line crosses 'p' on its // lefthand side or on its righthand side // project point 'p1' such that that it has the same y co-ordinate as 'p' $p1_x += ($p_y - $p1_y) * (($p2_x - $p1_x) / ($p2_y - $p1_y)); // 'p1' to the left of 'p'? then it does not cross our scanline if( $p1_x >= $p_x ) { echo " + vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": crosses right)\n"; $crossings++; } else { echo " vertex " . print_pt(array('x' => $p1_x, 'y' => $p1_y)) . "-" . print_pt(array('x' => $p2_x, 'y' => $p2_y)) . ": crosses left)\n"; } } if( ($crossings & 1) != 0 ) { echo " + polygon matches, " . $crossings . " vertices to the right\n"; return TRUE; } else { echo " - polygon does NOT match, " . $crossings . " vertices to the right\n"; return FALSE; } } function geofence_test_object($i, $j, $p) { global $designs; if( !georect_within($designs[$i][$j]['bbox'], $p) ) return FALSE; switch( $designs[$i][$j]['type'] ) { case 'point': return geofence_test_point($i, $j, $p); case 'line': case 'track': return geofence_test_lines($i, $j, $p); case 'rect': return geofence_test_rect($i, $j, $p); case 'polygon': return geofence_test_polygon($i, $j, $p); default: echo "Unknown type: " . $designs[$i][$j]['type'] . "\n"; return FALSE; } } function geofence_test($p, $tijd, $speed, $tc) { global $designs; echo $tijd . ": " . print_pt($p) . ", " . $speed . " km/h, tc=" .$tc . "\n"; for( $i = 0; $i < count($designs); $i++ ) { $inside = 0; if( georect_within($designs[$i]['bbox'], $p) ) { for( $j = 0; $j < $designs[$i]['n']; $j++ ) { if( geofence_test_object($i, $j, $p) ) { $inside = 1; echo " INSIDE (" . $i . "," . $j . ")\n"; } } } if( $inside != $designs[$i]['inside'] ) { $designs[$i]['inside'] = $inside; if( $inside ) echo " ----> ENTER " . $designs[$i]['name'] . "\n"; else echo " <---- EXIT " . $designs[$i]['name'] . "\n"; } } } ?>