예제 #1
0
 function constructor4($segment, $box, $v, $start_time)
 {
     $this->finish_time = $start_time + $segment->t;
     parent::constructor3($segment, $box, $v);
 }
예제 #2
0
/**
    @param $end_marker<Marker> can be null if S2S search
    @return<Path2> a path from the start to the end or null if none is found
*/
function findPath($open, $end_stations, $end_marker)
{
    $debug = false;
    $debug_all = false;
    $WALKING_SPEED = 2 / 3600;
    $STATION_STOP_TIME = 30;
    $TRANSFER_TIME = 45;
    $RATIO = 0.75;
    $completed_time = -1;
    $completed_stops = -1;
    if ($debug) {
        echo "findPath<br/>";
    }
    global $markers;
    // create array of completed paths
    $completed = array();
    // there's a bug in PHP where passing an array by value that contains objects passes the objects by reference
    $new_open = array();
    foreach ($open as $path_array) {
        $new_array = array();
        $time = 0;
        foreach ($path_array as $path) {
            $new_array[] = $path->createCopy();
            $time = $path->time;
        }
        $new_open["{$time}"] = $new_array;
    }
    $one_path = findAPath($new_open, $end_stations, $end_marker);
    if ($one_path != null) {
        if ($debug) {
            echo "path by shortest distance: " . $one_path->toString() . "<br/>";
        }
        /*if (count($one_path->path) <= 2) {
              // this path does not even take the subway. no point searching for anything else.
              return $one_path;
          }*/
        $completed[] = $one_path;
        // set time
        $completed_time = $one_path->time;
        $completed_stops = count($one_path->path);
    }
    // create closed paths
    $closed = array();
    $restart = false;
    #$counter = 0;
    $multiple_paths = array();
    while (count($open) > 0) {
        #echo "loop: $counter<br/>";
        #$counter++;
        if (!is_array($open)) {
            return null;
        }
        if ($debug_all) {
            echo "open size = " . count($open) . "<br/>";
        }
        ksort($open);
        // look at the path at the front of OPEN, remove from list
        $keys = array_keys($open);
        $key = $keys[0];
        $multiple_paths = array_shift($open);
        if (is_array($multiple_paths)) {
            $path = array_shift($multiple_paths);
        } else {
            return null;
        }
        if (count($multiple_paths) > 0) {
            $open["{$key}"] = $multiple_paths;
        }
        if ($completed_time > 0 && $completed_time < $path->time) {
            if ($debug_all) {
                echo "path is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
            }
            continue;
        }
        $par = $completed_time * pow($completed_stops, 2);
        if ($completed_stops > 0 && $completed_time > 0) {
            $metric = pow(count($path->path), 2) * $path->time;
            #echo "metric = $metric | par = $par<br/>";
            if ($par / $metric < 0.8) {
                if ($debug_all) {
                    echo "path metric is too big (" . $par / $metric . "). discard.<br/>";
                }
                continue;
            }
        }
        $boundary_box = $path->bounding_box;
        #echo "box area = " . $boundary_box->getArea() . "<br/>";
        // print path
        if ($debug_all) {
            echo $path->toString();
        }
        // find degree of freedom for each station at the end of each path
        // DoF is defined as the number of outbound connections from the marker
        $last_marker = $path->getLastMarker();
        if ($debug_all) {
            $last_marker->printInfo();
        }
        // for some reason the last marker and the end marker are the same.
        // 4 E 28th St, New York, NY 10016 to 350 5th Ave, New York, NY 10118
        // add to path and continue
        if ($last_marker->id == $end_marker->id) {
            $completed[] = $path;
            continue;
        }
        //$box = new Box($last_marker->point, $end_marker->point);
        #echo "box area = " . $box->getArea() . "<br/>";
        #$stl_marker = $path->getSTLMarker();
        #echo $stl_marker->toString() . " looking at marker = [" . $last_marker->toString() . "]<br/>";
        $lines_array = $last_marker->getLines();
        /*if ($debug_all) {
              echo "lines_array<br/>\n";
              print_r($lines_array);
              echo "<br/>\n";
          }*/
        $DoF = getDegreeOfFreedom($lines_array);
        $connection_added = array();
        if ($DoF - 1 <= 1) {
            // station only has two connections
            // grab the next station
            $old_marker = $path->getSTLMarker();
            // loop through each connection and find the new marker
            #echo "connections = <br/>";
            foreach ($lines_array[0]->getConnections() as $connection) {
                #echo $connection->id . ": ";
                if ($connection->id != $old_marker->id) {
                    // new marker found, add to path
                    $next_marker = $markers[$connection->id];
                    //if ($debug) {
                    //    echo "next_marker: $next_marker->id<br/>";
                    //}
                    $original_dist = $last_marker->point->getDistanceMiles($end_marker->point);
                    $dist = $next_marker->point->getDistanceMiles($end_marker->point);
                    #echo "p2p distance = $dist ";
                    #echo "from " . $next_marker->toString() . "<br/>";
                    // calculate the box
                    if ($single_path_length < 3) {
                        $new_box = new Box($next_marker->point, $end_marker->point);
                    } else {
                        $new_box = null;
                    }
                    $single_path_length = $path->single_path_length;
                    if ($original_dist < $dist && $single_path_length > 2) {
                        if ($new_box->getArea() > $boundary_box->getArea()) {
                            // outside our scope, move to closed list
                            ##echo "spl: $single_path_length :: bounding box (". $next_marker->id .") is too big: " . $new_box->getArea() . ">" . $boundary_box->getArea() . ". will not add path...<br/>";
                            $closed[] = $path;
                            continue;
                        }
                    }
                    $single_path_length++;
                    if (in_array($next_marker, $path->visited)) {
                        if ($debug_all) {
                            echo "connection (" . $connection->id . ") is backtracking to " . $next_marker->toString() . ". ignoring.<br/>";
                        }
                        continue;
                    }
                    if (in_array($next_marker->id, $connection_added)) {
                        if ($debug_all) {
                            echo "[{$next_marker->id}] marker was already added. skipping.<br/>\n";
                        }
                        continue;
                    }
                    $new_path = new Path2($path, $next_marker, $lines_array[0], $connection->duration + $STATION_STOP_TIME, $connection->type, $new_box, $path->visited, $single_path_length);
                    $connection_added[] = $next_marker->id;
                    // check to see if the new station is one of the end stations. if so, then mark the path as completed and remove from OPEN
                    foreach ($end_stations as $station) {
                        /*
                            If the station marker is the next marker, then we know we're at one of the end points. We will definitely add this path, but we should also make one more check to see if the next possible marker is also an end station.
                        */
                        if ($station->marker == $next_marker) {
                            // grab the next set of markers from this marker and see if any of them are also end stations
                            $next_lines = $next_marker->lines;
                            foreach ($next_lines as $next_line) {
                                $next_connections = $next_line->connections;
                                foreach ($next_connections as $next_connection) {
                                    // check if each connection matches
                                    foreach ($end_stations as $station) {
                                        $connection_id = $next_connection->id;
                                        if ($connection_id == $station->marker->id) {
                                            // we found a marker that's valid
                                            // create a path
                                            $alternate_path = new Path2($new_path, $markers[$connection_id], $next_line, $next_connection->duration, $next_connection->type, $box, $new_path->visited, $single_path_length);
                                            // add the walking segment
                                            $alternate_path->addSegment(new Segment($alternate_path->getLastMarker(), $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking"), null);
                                            // verify path is valid
                                            if ($completed_time > 0 && $completed_time < $alternate_path->time) {
                                                // discarding because the time for this path is slower than one we already have
                                                if ($debug_all) {
                                                    echo "path ({$next_marker->id}) is too long ({$completed_time} < " . $alternate_path->time . "). discard.<br/>";
                                                }
                                                break;
                                            }
                                            // calculate par
                                            $par = $completed_time * pow($completed_stops, 2);
                                            if ($completed_stops > 0 && $completed_time > 0) {
                                                // if we have something to compare it to, then compare
                                                $metric = pow(count($path->path), 2) * $path->time;
                                                if ($par / $metric < $RATIO) {
                                                    if ($debug_all) {
                                                        echo "path metric ({$next_marker->id}) is too big (" . $par / $metric . "). discard.<br/>";
                                                    }
                                                    break;
                                                }
                                            }
                                            if ($debug) {
                                                echo "adding (" . $alternate_path->toString() . ") to COMPLETED<br/>";
                                            }
                                            // add the path to completed
                                            $completed[] = $alternate_path;
                                            if ($completed_time < 0 || $completed_time > $alternate_path->time) {
                                                $completed_time = $alternate_path->time;
                                                $completed_stops = count($alternate_path->path);
                                            }
                                            if ($debug_all) {
                                                echo "completed time = {$completed_time}<br/>";
                                                echo "completed stops = {$completed_stops}<br/>";
                                            }
                                            break;
                                        }
                                        // if ($connection_id
                                    }
                                    // foreach ($end_stations
                                }
                                // foreach ($next_connections
                            }
                            // foreach ($next_lines
                            // check to see if it is the end marker
                            if ($end_marker != $next_marker) {
                                // add end location marker
                                $new_path2 = new Path2($new_path, $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking", $box, $new_path->visited, $single_path_length);
                            } else {
                                // otherwise use the existing path
                                $new_path2 = $new_path;
                            }
                            if ($completed_time > 0 && $completed_time < $new_path2->time) {
                                // discarding because the time for this path is slower than one we already have
                                if ($debug_all) {
                                    echo "path ({$next_marker->id}) is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
                                }
                                continue;
                            }
                            // calculate par
                            $par = $completed_time * pow($completed_stops, 2);
                            if ($completed_stops > 0 && $completed_time > 0) {
                                // if we have something to compare it to, then compare
                                $metric = pow(count($path->path), 2) * $path->time;
                                if ($par / $metric < $RATIO) {
                                    if ($debug_all) {
                                        echo "path metric ({$next_marker->id}) is too big (" . $par / $metric . "). discard.<br/>";
                                    }
                                    continue;
                                }
                            }
                            // add path to COMPLETED and remove from OPEN
                            if ($debug) {
                                echo "adding (" . $new_path2->toString() . ") to COMPLETED<br/>";
                            }
                            $completed[] = $new_path2;
                            if ($completed_time < 0 || $completed_time > $new_path2->time) {
                                $completed_time = $new_path2->time;
                                $completed_stops = count($new_path2->path);
                            }
                            if ($debug_all) {
                                echo "completed time = {$completed_time}<br/>";
                                echo "completed stops = {$completed_stops}<br/>";
                            }
                            #$restart = true;
                            break;
                        }
                    }
                    // add path back to open
                    if (!$restart) {
                        #echo "adding path to OPEN<br/>";
                        $box = $new_path->bounding_box;
                        if (!is_null($box)) {
                            $area = $new_path->bounding_box->getArea();
                        } else {
                            $area = 0;
                        }
                        $multi_paths = $open["{$area}"];
                        if ($multi_paths == null) {
                            $multi_paths = array();
                        }
                        $add_path = true;
                        foreach ($multi_paths as $check_path) {
                            $check_box = $check_path->bounding_box;
                            if (!is_null($check_box)) {
                                if ($area > $check_box->getArea()) {
                                    echo "1 PATH bounding box ({$area}) is too big: " . $new_path->toString() . "<br/>";
                                    $add_path = false;
                                    break;
                                }
                            }
                        }
                        if ($add_path) {
                            $multi_paths[] = $new_path;
                            $open["{$area}"] = $multi_paths;
                        }
                    } else {
                        break;
                    }
                }
            }
            // foreach ($lines_array[0]
        } else {
            // more than one line so we need to determine whether the connection is outside the box
            // find connecting stations
            foreach ($lines_array as $line) {
                $connections = $line->getConnections();
                #echo "[" . $line->name . "] number of connections = " . count($connections) . "<br/>";
                // for each station
                foreach ($connections as $connection) {
                    $next_marker = $markers[$connection->id];
                    #echo "connection = " . $connection->id . "<br/>";
                    #echo "next_marker = " . $next_marker->toString() . "<br/>";
                    // if station is in the path
                    if (in_array($next_marker, $path->visited)) {
                        if ($debug_all) {
                            echo "connection (" . $connection->id . ") is backtracking to " . $next_marker->toString() . ". ignoring.<br/>";
                        }
                        continue;
                    }
                    $original_dist = $last_marker->point->getDistanceMiles($end_marker->point);
                    $dist = $next_marker->point->getDistanceMiles($end_marker->point);
                    #echo "p2p distance = $dist ";
                    #echo "from " . $next_marker->toString() . "<br/>";
                    // calculate the box
                    $new_box = new Box($next_marker->point, $end_marker->point);
                    if (!is_null($boundary_box)) {
                        if ($original_dist < $dist) {
                            if ($new_box->getArea() > $boundary_box->getArea()) {
                                // outside our scope, move to closed list
                                if ($debug_all) {
                                    echo "new bounding box (" . $next_marker->id . ") is too big: " . $new_box->getArea() . ">" . $boundary_box->getArea() . ". will not add path<br/>";
                                }
                                $closed[] = $path;
                                continue;
                            }
                        }
                    }
                    // if station is in new box area
                    if ((is_null($boundary_box) || $boundary_box->withinBox($next_marker)) && !in_array($next_marker->id, $connection_added)) {
                        // add marker to path array
                        $new_path = new Path2($path, $next_marker, $line, $connection->duration + $STATION_STOP_TIME + $TRANSFER_TIME, $connection->type, $new_box, $path->visited, 0);
                        foreach ($path->getLastSegment()->lines as $last_line) {
                            if ($line->name == $last_line->name) {
                                $new_path = new Path2($path, $next_marker, $line, $connection->duration + $STATION_STOP_TIME, $connection->type, $new_box, $path->visited, 0);
                                #echo "no transfer..." . $line->name . " | " . $new_path->toString() . "<br/>";
                                break;
                            }
                        }
                        if ($debug_all) {
                            echo "new_path: " . $new_path->toString() . "<br/>";
                        }
                        $connection_added[] = $next_marker->id;
                        // check to see if the new station is one of the end stations. if so, then mark the path as completed and remove from OPEN
                        foreach ($end_stations as $station) {
                            // if path has ending station
                            if ($station->marker == $next_marker) {
                                // grab the next set of markers from this marker and see if any of them are also end stations
                                $next_lines = $next_marker->lines;
                                foreach ($next_lines as $next_line) {
                                    $next_connections = $next_line->connections;
                                    foreach ($next_connections as $next_connection) {
                                        // check if each connection matches
                                        foreach ($end_stations as $station) {
                                            $connection_id = $next_connection->id;
                                            if ($connection_id == $station->marker->id) {
                                                // we found a marker that's valid
                                                // create a path
                                                $alternate_path = new Path2($new_path, $markers[$connection_id], $next_line, $next_connection->duration, $next_connection->type, $box, $new_path->visited, $single_path_length);
                                                // add the walking segment
                                                $alternate_path->addSegment(new Segment($alternate_path->getLastMarker(), $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking"), null);
                                                // verify path is valid
                                                if ($completed_time > 0 && $completed_time < $alternate_path->time) {
                                                    // discarding because the time for this path is slower than one we already have
                                                    if ($debug_all) {
                                                        echo "path ({$next_marker->id}) is too long ({$completed_time} < " . $alternate_path->time . "). discard.<br/>";
                                                    }
                                                    break;
                                                }
                                                // calculate par
                                                $par = $completed_time * pow($completed_stops, 2);
                                                if ($completed_stops > 0 && $completed_time > 0) {
                                                    // if we have something to compare it to, then compare
                                                    $metric = pow(count($path->path), 2) * $path->time;
                                                    if ($par / $metric < $RATIO) {
                                                        if ($debug_all) {
                                                            echo "path metric ({$next_marker->id}) is too big (" . $par / $metric . "). discard.<br/>";
                                                        }
                                                        break;
                                                    }
                                                }
                                                if ($debug) {
                                                    echo "adding (" . $alternate_path->toString() . ") to COMPLETED<br/>";
                                                }
                                                // add the path to completed
                                                $completed[] = $alternate_path;
                                                if ($completed_time < 0 || $completed_time > $alternate_path->time) {
                                                    $completed_time = $alternate_path->time;
                                                    $completed_stops = count($alternate_path->path);
                                                }
                                                if ($debug_all) {
                                                    echo "completed time = {$completed_time}<br/>";
                                                    echo "completed stops = {$completed_stops}<br/>";
                                                }
                                                break;
                                            }
                                            // if ($connection_id
                                        }
                                        // foreach ($end_stations
                                    }
                                    // foreach ($next_connections
                                }
                                // foreach ($next_lines
                                if ($end_marker != $next_marker) {
                                    // add end location marker
                                    $new_path2 = new Path2($new_path, $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking", null, $new_path->visited, 0);
                                    if ($debug_all) {
                                        echo "created new path:: " . $new_path2->toString() . "<br/>";
                                    }
                                } else {
                                    $new_path2 = $new_path;
                                }
                                if ($completed_time > 0 && $completed_time < $new_path2->time) {
                                    if ($debug_all) {
                                        echo "path ({$next_marker->id}) is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
                                    }
                                    continue;
                                }
                                $par = $completed_time * pow($completed_stops, 2);
                                if ($completed_stops > 0 && $completed_time > 0) {
                                    $metric = pow(count($path->path), 2) * $path->time;
                                    #echo "par/metric = " . $par/$metric . "<br/>";
                                    if ($par / $metric < $RATIO) {
                                        #echo "path metric is too big (" . $par/$metric . "). discard.<br/>";
                                        continue;
                                    }
                                }
                                // add path to COMPLETED and remove from OPEN
                                if ($debug) {
                                    echo "adding (" . $new_path2->toString() . ") to COMPLETED<br/>";
                                }
                                $completed[] = $new_path2;
                                if ($completed_time < 0 || $completed_time > $new_path2->time) {
                                    $completed_time = $new_path2->time;
                                    $completed_stops = count($new_path2->path);
                                }
                                if ($debug_all) {
                                    echo "completed time = {$completed_time}<br/>";
                                    echo "completed stops = {$completed_stops}<br/>";
                                }
                                #$restart = true;
                                break;
                            }
                        }
                        // foreach ($end_station
                        // add path back to open
                        if (!$restart) {
                            #echo "adding path to OPEN<br/>";
                            $new_box = $new_path->bounding_box;
                            $area = 0;
                            if (!is_null($new_box)) {
                                $area = $new_box->getArea();
                            }
                            $multi_paths = $open["{$area}"];
                            if ($multi_paths == null) {
                                $multi_paths = array();
                            }
                            $add_path = true;
                            foreach ($multi_paths as $check_path) {
                                $check_box = $check_path->bounding_box;
                                $check_area = 0;
                                if (!is_null($check_box)) {
                                    $check_area = $check_box->getArea();
                                }
                                if ($area >= $check_area && $area != 0) {
                                    if ($debug_all) {
                                        echo "2 PATH bounding box ({$area}) is too big: " . $new_path->toString() . "<br/>";
                                    }
                                    $add_path = false;
                                    break;
                                }
                            }
                            if ($add_path) {
                                $multi_paths[] = $new_path;
                                $open["{$area}"] = $multi_paths;
                            }
                        } else {
                            break;
                        }
                    } else {
                        if ($debug_all) {
                            echo "PATH to ({$next_marker->id}) is outside of bounding box or connection has already been added.<br/>";
                        }
                    }
                }
                // foreach ($connections
            }
        }
        // else
        if (count($open) == 0 && count($completed) == 0) {
            // we didn't find a path! expand our search
            $open = $closed;
        }
        $restart = false;
    }
    // while (count($open) > 0)
    // return path with the shortest time
    if ($debug) {
        echo "number of COMPLETED = " . count($completed) . "<br/>";
    }
    $x = 0;
    $path = array_shift($completed);
    if (count($completed) > 0) {
        foreach ($completed as $apath) {
            if ($path->time > $apath->time) {
                $path = $apath;
            } else {
                if ($path->time == $apath->time) {
                    if (count($path->path) > count($apath->path)) {
                        $path = $apath;
                    }
                }
            }
        }
    }
    if ($debug) {
        echo "displaying path -- " . $path->toString() . "<br/>";
    }
    return $path;
}
예제 #3
0
/**
    @param $end_marker<Marker> can be null if S2S search
    @return<Path2> a path from the start to the end or null if none is found
*/
function findPath($open, $end_stations, $end_marker)
{
    $debug = false;
    $debug_all = false;
    $WALKING_SPEED = 2 / 3600;
    $STATION_STOP_TIME = 30;
    $TRANSFER_TIME = 45;
    $completed_time = -1;
    $completed_stops = -1;
    if ($debug) {
        echo "findPath<br/>";
    }
    global $markers;
    // create array of completed paths
    $completed = array();
    $one_path = findAPath($open, $end_stations, $end_marker);
    if ($one_path != null) {
        if ($debug) {
            echo "path by shortest distance: " . $one_path->toString() . "<br/>";
        }
        $completed[] = $one_path;
        // set time
        $completed_time = $one_path->time;
        $completed_stops = count($one_path->path);
    }
    // create closed paths
    $closed = array();
    $restart = false;
    #$counter = 0;
    $multiple_paths = array();
    while (count($open) > 0) {
        #echo "loop: $counter<br/>";
        #$counter++;
        if (!is_array($open)) {
            return null;
        }
        if ($debug_all) {
            echo "open size = " . count($open) . "<br/>";
        }
        ksort($open);
        // look at the path at the front of OPEN, remove from list
        $keys = array_keys($open);
        $key = $keys[0];
        $multiple_paths = array_shift($open);
        if (is_array($multiple_paths)) {
            $path = array_shift($multiple_paths);
        } else {
            return null;
        }
        if (count($multiple_paths) > 0) {
            $open["{$key}"] = $multiple_paths;
        }
        if ($completed_time > 0 && $completed_time < $path->time) {
            if ($debug_all) {
                echo "path is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
            }
            continue;
        }
        $par = $completed_time * pow($completed_stops, 2);
        if ($completed_stops > 0 && $completed_time > 0) {
            $metric = pow(count($path->path), 2) * $path->time;
            #echo "metric = $metric | par = $par<br/>";
            if ($par / $metric < 0.8) {
                if ($debug_all) {
                    echo "path metric is too big (" . $par / $metric . "). discard.<br/>";
                }
                continue;
            }
        }
        $boundary_box = $path->bounding_box;
        #echo "box area = " . $boundary_box->getArea() . "<br/>";
        // print path
        if ($debug_all) {
            echo $path->toString();
        }
        // find degree of freedom for each station at the end of each path
        // DoF is defined as the number of outbound connections from the marker
        $last_marker = $path->getLastMarker();
        //$box = new Box($last_marker->point, $end_marker->point);
        #echo "box area = " . $box->getArea() . "<br/>";
        #$stl_marker = $path->getSTLMarker();
        #echo $stl_marker->toString() . " looking at marker = [" . $last_marker->toString() . "]<br/>";
        $lines_array = $last_marker->getLines();
        $DoF = getDegreeOfFreedom($lines_array);
        $connection_added = array();
        if ($DoF - 1 <= 1) {
            // station only has two connections
            // grab the next station
            $old_marker = $path->getSTLMarker();
            // loop through each connection and find the new marker
            #echo "connections = <br/>";
            foreach ($lines_array[0]->getConnections() as $connection) {
                #echo $connection->id . ": ";
                if ($connection->id != $old_marker->id) {
                    // new marker found, add to path
                    $next_marker = $markers[$connection->id];
                    $original_dist = $last_marker->point->getDistanceMiles($end_marker->point);
                    $dist = $next_marker->point->getDistanceMiles($end_marker->point);
                    #echo "p2p distance = $dist ";
                    #echo "from " . $next_marker->toString() . "<br/>";
                    // calculate the box
                    if ($single_path_length < 3) {
                        $new_box = new Box($next_marker->point, $end_marker->point);
                    } else {
                        $new_box = null;
                    }
                    $single_path_length = $path->single_path_length;
                    if ($original_dist < $dist && $single_path_length > 2) {
                        if ($new_box->getArea() > $boundary_box->getArea()) {
                            // outside our scope, move to closed list
                            ##echo "spl: $single_path_length :: bounding box (". $next_marker->id .") is too big: " . $new_box->getArea() . ">" . $boundary_box->getArea() . ". will not add path...<br/>";
                            $closed[] = $path;
                            continue;
                        }
                    }
                    $single_path_length++;
                    if (in_array($next_marker, $path->visited)) {
                        if ($debug_all) {
                            echo "connection (" . $connection->id . ") is backtracking to " . $next_marker->toString() . ". ignoring.<br/>";
                        }
                        continue;
                    }
                    if (in_array($next_marker->id, $connection_added)) {
                        if ($debug_all) {
                            echo "[{$next_marker->id}] marker was already added. skipping.<br/>\n";
                        }
                        continue;
                    }
                    $new_path = new Path2($path, $next_marker, $lines_array[0], $connection->duration + $STATION_STOP_TIME, $connection->type, $new_box, $path->visited, $single_path_length);
                    $connection_added[] = $next_marker->id;
                    // check to see if the new station is one of the end stations. if so, then mark the path as completed and remove from OPEN
                    foreach ($end_stations as $station) {
                        if ($station->marker == $next_marker) {
                            if ($end_marker != $next_marker) {
                                // add end location marker
                                $new_path2 = new Path2($new_path, $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking", $box, $new_path->visited, $single_path_length);
                            } else {
                                $new_path2 = $new_path;
                            }
                            if ($completed_time > 0 && $completed_time < $new_path2->time) {
                                if ($debug_all) {
                                    echo "path is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
                                }
                                continue;
                            }
                            $par = $completed_time * pow($completed_stops, 2);
                            if ($completed_stops > 0 && $completed_time > 0) {
                                $metric = pow(count($path->path), 2) * $path->time;
                                if ($par / $metric < 0.75) {
                                    if ($debug_all) {
                                        echo "path metric is too big (" . $par / $metric . "). discard.<br/>";
                                    }
                                    continue;
                                }
                            }
                            // add path to COMPLETED and remove from OPEN
                            if ($debug) {
                                echo "adding (" . $new_path2->toString() . ") to COMPLETED<br/>";
                            }
                            $completed[] = $new_path2;
                            if ($completed_time < 0 || $completed_time > $new_path2->time) {
                                $completed_time = $new_path2->time;
                                $completed_stops = count($new_path2->path);
                            }
                            if ($debug_all) {
                                echo "completed time = {$completed_time}<br/>";
                                echo "completed stops = {$completed_stops}<br/>";
                            }
                            #$restart = true;
                            break;
                        }
                    }
                    // add path back to open
                    if (!$restart) {
                        #echo "adding path to OPEN<br/>";
                        $box = $new_path->bounding_box;
                        if (!is_null($box)) {
                            $area = $new_path->bounding_box->getArea();
                        } else {
                            $area = 0;
                        }
                        $multi_paths = $open["{$area}"];
                        if ($multi_paths == null) {
                            $multi_paths = array();
                        }
                        $add_path = true;
                        foreach ($multi_paths as $check_path) {
                            $check_box = $check_path->bounding_box;
                            if (!is_null($check_box)) {
                                if ($area > $check_box->getArea()) {
                                    echo "1 PATH bounding box ({$area}) is too big: " . $new_path->toString() . "<br/>";
                                    $add_path = false;
                                    break;
                                }
                            }
                        }
                        if ($add_path) {
                            $multi_paths[] = $new_path;
                            $open["{$area}"] = $multi_paths;
                        }
                    } else {
                        break;
                    }
                }
            }
            // foreach ($lines_array[0]
        } else {
            // more than one line so we need to determine whether the connection is outside the box
            // find connecting stations
            foreach ($lines_array as $line) {
                $connections = $line->getConnections();
                #echo "[" . $line->name . "] number of connections = " . count($connections) . "<br/>";
                // for each station
                foreach ($connections as $connection) {
                    $next_marker = $markers[$connection->id];
                    #echo "connection = " . $connection->id . "<br/>";
                    #echo "next_marker = " . $next_marker->toString() . "<br/>";
                    // if station is in the path
                    if (in_array($next_marker, $path->visited)) {
                        if ($debug_all) {
                            echo "connection (" . $connection->id . ") is backtracking to " . $next_marker->toString() . ". ignoring.<br/>";
                        }
                        continue;
                    }
                    $original_dist = $last_marker->point->getDistanceMiles($end_marker->point);
                    $dist = $next_marker->point->getDistanceMiles($end_marker->point);
                    #echo "p2p distance = $dist ";
                    #echo "from " . $next_marker->toString() . "<br/>";
                    // calculate the box
                    $new_box = new Box($next_marker->point, $end_marker->point);
                    if (!is_null($boundary_box)) {
                        if ($original_dist < $dist) {
                            if ($new_box->getArea() > $boundary_box->getArea()) {
                                // outside our scope, move to closed list
                                if ($debug_all) {
                                    echo "new bounding box (" . $next_marker->id . ") is too big: " . $new_box->getArea() . ">" . $boundary_box->getArea() . ". will not add path<br/>";
                                }
                                $closed[] = $path;
                                continue;
                            }
                        }
                    }
                    // if station is in new box area
                    if ((is_null($boundary_box) || $boundary_box->withinBox($next_marker)) && !in_array($next_marker->id, $connection_added)) {
                        // add marker to path array
                        $new_path = new Path2($path, $next_marker, $line, $connection->duration + $STATION_STOP_TIME + $TRANSFER_TIME, $connection->type, $new_box, $path->visited, 0);
                        foreach ($path->getLastSegment()->lines as $last_line) {
                            if ($line->name == $last_line->name) {
                                $new_path = new Path2($path, $next_marker, $line, $connection->duration + $STATION_STOP_TIME, $connection->type, $new_box, $path->visited, 0);
                                #echo "no transfer..." . $line->name . " | " . $new_path->toString() . "<br/>";
                                break;
                            }
                        }
                        if ($debug_all) {
                            echo "new_path: " . $new_path->toString() . "<br/>";
                        }
                        $connection_added[] = $next_marker->id;
                        // check to see if the new station is one of the end stations. if so, then mark the path as completed and remove from OPEN
                        foreach ($end_stations as $station) {
                            // if path has ending station
                            if ($station->marker == $next_marker) {
                                if ($end_marker != $next_marker) {
                                    // add end location marker
                                    $new_path2 = new Path2($new_path, $end_marker, null, $station->getDistance() / $WALKING_SPEED, "walking", null, $new_path->visited, 0);
                                } else {
                                    $new_path2 = $new_path;
                                }
                                if ($completed_time > 0 && $completed_time < $new_path2->time) {
                                    if ($debug_all) {
                                        echo "path is too long ({$completed_time} < " . $new_path2->time . "). discard.<br/>";
                                    }
                                    continue;
                                }
                                $par = $completed_time * pow($completed_stops, 2);
                                if ($completed_stops > 0 && $completed_time > 0) {
                                    $metric = pow(count($path->path), 2) * $path->time;
                                    #echo "par/metric = " . $par/$metric . "<br/>";
                                    if ($par / $metric < 0.75) {
                                        #echo "path metric is too big (" . $par/$metric . "). discard.<br/>";
                                        continue;
                                    }
                                }
                                // add path to COMPLETED and remove from OPEN
                                if ($debug) {
                                    echo "adding (" . $new_path2->toString() . ") to COMPLETED<br/>";
                                }
                                $completed[] = $new_path2;
                                if ($completed_time < 0 || $completed_time > $new_path2->time) {
                                    $completed_time = $new_path2->time;
                                    $completed_stops = count($new_path2->path);
                                }
                                if ($debug_all) {
                                    echo "completed time = {$completed_time}<br/>";
                                    echo "completed stops = {$completed_stops}<br/>";
                                }
                                #$restart = true;
                                break;
                            }
                        }
                        // foreach ($end_station
                        // add path back to open
                        if (!$restart) {
                            #echo "adding path to OPEN<br/>";
                            $new_box = $new_path->bounding_box;
                            $area = 0;
                            if (!is_null($new_box)) {
                                $area = $new_box->getArea();
                            }
                            $multi_paths = $open["{$area}"];
                            if ($multi_paths == null) {
                                $multi_paths = array();
                            }
                            $add_path = true;
                            foreach ($multi_paths as $check_path) {
                                $check_box = $check_path->bounding_box;
                                $check_area = 0;
                                if (!is_null($check_box)) {
                                    $check_area = $check_box->getArea();
                                }
                                if ($area >= $check_area && $area != 0) {
                                    if ($debug_all) {
                                        echo "2 PATH bounding box ({$area}) is too big: " . $new_path->toString() . "<br/>";
                                    }
                                    $add_path = false;
                                    break;
                                }
                            }
                            if ($add_path) {
                                $multi_paths[] = $new_path;
                                $open["{$area}"] = $multi_paths;
                            }
                        } else {
                            break;
                        }
                    } else {
                        if ($debug_all) {
                            echo "PATH to ({$next_marker->id}) is outside of bounding box or connection has already been added.<br/>";
                        }
                    }
                }
                // foreach ($connections
            }
        }
        // else
        if (count($open) == 0 && count($completed) == 0) {
            // we didn't find a path! expand our search
            $open = $closed;
        }
        $restart = false;
    }
    // while (count($open) > 0)
    // return path with the shortest time
    if ($debug) {
        echo "number of COMPLETED = " . count($completed) . "<br/>";
    }
    $x = 0;
    $path = array_shift($completed);
    if (count($completed) > 0) {
        foreach ($completed as $apath) {
            if ($path->time > $apath->time) {
                $path = $apath;
            } else {
                if ($path->time == $apath->time) {
                    if (count($path->path) > count($apath->path)) {
                        $path = $apath;
                    }
                }
            }
        }
    }
    if ($debug) {
        echo $path->toString() . "<br/>";
    }
    return $path;
}