Exemplo n.º 1
0
function blow_backout($node1, $node2, $network_d, $links)
{
    //now run the shortest path on the new network of shortest paths but measure shortest by number of changes
    $seq = 0;
    $network_c = array();
    $queue = array();
    $queue_sort = array();
    //since change distances will be the same a lot of the time inclde a parallel distance array to pick the shortest within change groups
    $dist_limit = FALSE;
    foreach ($node2 as $pnum) {
        $pos_c = $seq++;
        $network_c[$pos_c]['node'] = $pnum;
        //start at the destination end
        $network_c[$pos_c]['dist'] = 0;
        //distance up to the node on this leaf
        $network_c[$pos_c]['changes'] = 0;
        //number of changes to get to this leaf
        $network_c[$pos_c]['link'] = FALSE;
        //link pnum from the parent
        $network_c[$pos_c]['routes'] = array();
        //routes that the node was arrived through
        $network_c[$pos_c]['past'] = array();
        //will build up list of past nodes going forward
        $network_c[$pos_c]['ptype'] = '';
        //used to check we do not get multiple 'walk' links in a row
        $queue['x' . $pos_c] = 0;
        //the initial distance makes little difference since it is only used to choose which of the one items we use.
        $queue_sort['x' . $pos_c] = 0;
        if (isset($network_d[$pnum]) && ($dist_limit === FALSE || $dist_limit > $network_d[$pnum]['dist'])) {
            $dist_limit = $network_d[$pnum]['dist'];
        }
    }
    $dist_limit *= 1.1;
    //don't allow paths to get more than 20% longer than the best.
    $complete = array();
    //collects one or more paths
    $best = FALSE;
    //will hold the lowest score of an actual match so we can stop looking after other options get to large to be contenders
    $range = 2;
    //allow range of best+2 extra stops
    $used = array();
    $time = microtime(true);
    while (count($queue)) {
        //asort($queue);	//put lowest score first
        array_multisort($queue, SORT_ASC, $queue_sort, SORT_DESC);
        //$queue = changes, $queue_sort = dist
        if ((microtime(true) - $time) * 1000 > 5000) {
            $time = microtime(true);
            qlog(__LINE__, 'queue', count($queue));
        }
        $est_changes = reset($queue);
        //set first element current, and get estimated distance for this route
        $cursor = 1 * substr(key($queue), 1);
        //get the key removing 'x' which makes the keys strings and prevents renumbering on arrays shift.
        array_shift($queue);
        //remove item from queue since it will not need processing after this
        array_shift($queue_sort);
        //remove item from queue since it will not need processing after this
        //$cursor is our index into the $network_c
        if (!isset($network_c[$cursor])) {
            continue;
        }
        //the parent should always exist, just being safe.
        $noden = $network_c[$cursor]['node'];
        //$noden is the node identifier of the leaf we want to continue the path from
        $last_dist = $network_c[$cursor]['dist'];
        //distance up to the node on this leaf
        $last_changes = $network_c[$cursor]['changes'];
        //number of changes to get to this leaf
        $last_routes = $network_c[$cursor]['routes'];
        //routes that the node was arrived through
        $last_link = $network_c[$cursor]['link'];
        //link pnum from the parent
        $past = $network_c[$pos_c]['past'];
        //will build up list of past going forward
        $past[] = $last_link;
        $last_ptype = $network_c[$cursor]['ptype'];
        //link pnum from the parent
        //if we have a match and all remaining options are worse by a margin then stop looking
        if (count($complete) && $best + $range < $est_changes) {
            break;
        }
        $used[$noden] = $pos_c;
        //our "links" are now coming from the leaves of the previous shortest path structure
        //$old_leaf = clb_val(FALSE, $network_d, $noden);	//get actual node
        $old_leaf = isset($network_d[$noden]) ? $network_d[$noden] : FALSE;
        //get actual node
        //$preds = clb_val(FALSE, $old_leaf, 'prev');		//the predecessors to that node
        $preds = isset($old_leaf['prev']) ? $old_leaf['prev'] : FALSE;
        //the predecessors to that node
        //now loop though the candidate links from this node
        if (clb_count($preds)) {
            foreach ($preds as $via_link => $pre_link) {
                //$ptype = clb_val('', $pre_link, 'ptype');
                $ptype = isset($pre_link['ptype']) ? $pre_link['ptype'] : '';
                // qlog(__LINE__,$ptype, $last_ptype, $noden, (isset($pre_link['prev_node']) ? $pre_link['prev_node'] : ''));
                if ($ptype == 'walk' && $last_ptype == 'walk') {
                    continue;
                }
                //don't allow a route to be constructed from a series of walk interconnections
                if (in_array($via_link, $past)) {
                    continue;
                }
                //already have this link on this path so skip it
                if ($via_link == $last_link) {
                    continue;
                }
                //dont double back, this test should be redundent due to the previous line
                //$remainder = clb_val(0, $pre_link, 'path_dist');	//each pre link from the network_d array knows how far it is from the origin as a minimum
                $remainder = isset($pre_link['path_dist']) ? $pre_link['path_dist'] : 0;
                //each pre link from the network_d array knows how far it is from the origin as a minimum
                //qlog(__LINE__, $last_dist, $remainder, ($last_dist + $remainder), $dist_limit, ((($last_dist + $remainder) > $dist_limit)?' culled':''));
                if ($last_dist + $remainder > $dist_limit) {
                    continue;
                }
                //when expanding routes, combinations may get long so chop them out
                //$dest_node = clb_val('', $pre_link,'prev_node');
                $dest_node = isset($pre_link['prev_node']) ? $pre_link['prev_node'] : '';
                //$link_len = clb_val(0, $pre_link, 'link_len');
                $link_len = isset($pre_link['link_len']) ? $pre_link['link_len'] : 0;
                $dist = $last_dist + $link_len;
                //$link_routes = clb_val(FALSE, $pre_link, 'common');
                $link_routes = isset($pre_link['common']) ? $pre_link['common'] : FALSE;
                $link_routes = $link_routes ? explode(',', $link_routes) : array();
                //convert comma separated list into array
                $routes_common = array_intersect($last_routes, $link_routes);
                $changes = count($routes_common) == 0 ? 2 : 0;
                //if no common routes then give a change a 2 score
                if ($changes) {
                    //see if there are any routes which have the same beginning up to '_' which indicates a branch on a line like underground lines
                    foreach ($last_routes as $rl) {
                        if (clb_contains($rl, '_')) {
                            foreach ($link_routes as $rn) {
                                if (clb_contains($rn, '_')) {
                                    if (preg_replace('/_.*$/', '', $rl) == preg_replace('/_.*$/', '', $rn)) {
                                        $changes = 1;
                                        break;
                                    }
                                }
                            }
                            if ($changes == 1) {
                                break;
                            }
                        }
                    }
                    $routes_common = $link_routes;
                    //since we are changing, can now use all routes between the two nodes
                }
                $changes += $last_changes;
                if (is_int($best) && $changes > $best + $range) {
                    continue;
                }
                $pos_c = $seq++;
                //prepare the index for the $network_c array, we may skip the node but do not mind gaps
                $network_c[$pos_c]['node'] = $dest_node;
                //start at the destination end
                $network_c[$pos_c]['prev'] = $cursor;
                //way back to the previous leaf
                $network_c[$pos_c]['dist'] = $dist;
                //distance up to the node on this leaf
                $network_c[$pos_c]['changes'] = $changes;
                //number of changes to get to this leaf
                $network_c[$pos_c]['link'] = $via_link;
                //link pnum from the parent
                $network_c[$pos_c]['routes'] = $routes_common;
                //routes that the node was arrived through
                $network_c[$pos_c]['past'] = $past;
                //will build up list of past going forward
                $network_c[$pos_c]['ptype'] = $ptype;
                // qlog(__LINE__,$dest_node, $node1);
                if (in_array($dest_node, $node1)) {
                    $complete[] = $pos_c;
                    if (is_bool($best) || $changes < $best) {
                        $best = $changes;
                    }
                } else {
                    $queue['x' . $pos_c] = $changes;
                    //primary sort on changes keeps minimum changes top of list
                    $queue_sort['x' . $pos_c] = $dist;
                    //distance ranks routes with same number of changes.
                }
            }
        }
    }
    //if (count($complete) == 0) {qpre($node1, $node2, $network_d, $network_c, $preds);	exit();}
    $result = array();
    if (is_array($complete)) {
        foreach ($complete as $pos_c) {
            $run = array();
            while ($link = $network_c[$pos_c]['link']) {
                $run[$link] = $network_c[$pos_c];
                //['routes'];
                $pos_c = $network_c[$pos_c]['prev'];
            }
            $result[] = $run;
        }
    }
    //qlog(__LINE__,count($complete), count($used), count($network_c), count($network_d));
    //qlog(__LINE__, clb_timing('shortest changes'));
    //qlog(__LINE__, $node2, $complete);
    return $result;
}
Exemplo n.º 2
0
function pfind_interpret($entries, $mode, &$waypoints, $path, $node_types)
{
    global $wpdb;
    $result = array();
    $result['error'] = 200;
    //start optimistically
    $result['error_str'] = 'no errors';
    $waypoints = array();
    if (clb_count($entries) <= 0) {
        $result['error'] = 400;
        $result['error_str'] = 'no locations received to parse';
        return $result;
    }
    //if only one entry we expect a a "to" separating origin and dest
    if (count($entries) == 1) {
        $entries = preg_split('/\\s+to\\s+/i', trim(reset($entries)), -1, PREG_SPLIT_NO_EMPTY);
    }
    if (clb_count($entries) == 1) {
        $result['error'] = 400;
        $result['error_str'] = 'only one location';
        return $result;
    }
    if (is_file($path)) {
        $path = dirname($path);
    }
    $path = clb_dir($path) . 'metaphones.txt';
    //like spelling city
    $index = FALSE;
    if (is_file($path)) {
        $data = file_get_contents($path);
        //, FILE_BINARY);
        $index = clb_blob_dec($data);
    }
    $pickone = array();
    foreach ($entries as $loc) {
        $loc = strtolower($loc);
        $sel = FALSE;
        $select = 'SELECT ' . RF_NODES_SELECT . ' FROM ' . RF_NODES_FROM . ' ';
        //'SELECT pnum, ptype, title, more, lat, lng FROM places ';
        $where = ' WHERE ';
        //the list of ptypes that will be used to limit the names search
        $ptypes = $node_types;
        //if more than one mode check if the user has added clarifying name to the name eg "oxford rail"
        if (count($ptypes) > 1) {
            foreach ($ptypes as $type) {
                if (clb_contains($loc, $type)) {
                    //allow the user to force a mode, by adding the code to the name
                    $ptypes = array($type);
                    //replace all other modes
                    $loc = trim(preg_replace('/\\s*\\b' . preg_quote($type) . '\\b\\s*/i', ' ', $loc));
                    //remove the signal from the name
                }
            }
        }
        //get rid of quotes and escapes
        $loc = trim(str_replace(array('\\', '"'), ' ', $loc));
        if (preg_match('/^\\s*(-?[\\.\\d]+)\\s*,\\s*(-?[\\.\\d]+)\\s*$/', $loc, $m)) {
            //waypoint given as lat/lng pair
            //limit search by node types
            if (clb_count($ptypes)) {
                $where .= ' ' . RF_NODES_TYPE . ' IN ' . clb_join($ptypes, TRUE) . ' AND ';
            }
            list($junk, $lat, $lng) = $m;
            for ($dist = 200; $dist <= 500; $dist += 100) {
                $sel = $wpdb->get_results($select . $where . within_rect($lat, $lng, $dist), ARRAY_A);
                $sel = clb_rekey($sel, RF_NODES_KEY);
                if (clb_count($sel) > 1) {
                    break;
                }
            }
            if (clb_count($sel) <= 0) {
                $result = array('error' => 602, 'error_str' => 'no tranpost node could be found near coordinates ' . $lat . ', ' . $lng);
                return $result;
            }
        } else {
            if (preg_match('/^node:\\s*(\\w+)\\s*$/', $loc, $m)) {
                //waypoint specified by pnum
                $sel = $wpdb->get_results($select . ' WHERE ' . RF_NODES_KEY . '=' . clb_escape($m[1]), ARRAY_A);
                $sel = clb_rekey($sel, RF_NODES_KEY);
            } else {
                if (in_array('rail', $ptypes) && preg_match('/^\\s*(\\w{3})\\s*$/', $loc, $m)) {
                    //rail station three letter code
                    $sel = $wpdb->get_results($select . ' WHERE ' . RF_NODES_TYPE . '="rail" AND (extref=' . clb_escape($m[1]) . ' OR ' . RF_NODES_NAME . '=' . clb_escape($m[1]) . ')', ARRAY_A);
                    $sel = clb_rekey($sel, RF_NODES_KEY);
                } else {
                    /*
                    	the primary key of the sound index structrue is the metaphone.  Inside that are ptypes using that saound.
                    	within each ptype there is a list of specific pnums using that sound in the name.
                    	on each first word, get all pnums for that sound and type, on subsequent words intesect with pnums
                    	so that we end up with pnums which have all sounds.
                    	$index[$sound][ptype][] => pnum
                    */
                    $sel = FALSE;
                    $name = pfind_unify_names($loc);
                    if (is_array($index)) {
                        $intersection = FALSE;
                        $words = preg_split('/\\W+/', $name, -1, PREG_SPLIT_NO_EMPTY);
                        foreach ($words as $w) {
                            if ($w == '&') {
                                $w = 'and';
                            }
                            $sound = metaphone($w);
                            if ($sound && isset($index[$sound])) {
                                $set = array();
                                foreach ($index[$sound] as $ptype => $nodes) {
                                    if (in_array($ptype, $ptypes)) {
                                        $set = array_merge($set, $nodes);
                                    }
                                }
                                $intersection = !is_array($intersection) ? $set : array_intersect($set, $intersection);
                            }
                        }
                        if (clb_count($intersection)) {
                            $query = $select . $where . ' ' . RF_NODES_KEY . ' IN ' . clb_join($intersection, TRUE);
                            $sel = $wpdb->get_results($query, ARRAY_A);
                            $sel = clb_rekey($sel, RF_NODES_KEY);
                        }
                    } else {
                        $query = $select . $where . ' ' . RF_NODES_NAME . ' LIKE ' . clb_escape($name . '%');
                        if (clb_count($ptypes)) {
                            $query .= ' AND ' . RF_NODES_TYPE . ' IN ' . clb_join($ptypes, TRUE);
                        }
                        $sel = $wpdb->get_results($query, ARRAY_A);
                        $sel = clb_rekey($sel, RF_NODES_KEY);
                    }
                    //if more than one match scan for common words and remove the matches if they did not contain them
                    if (clb_count($sel) > 1) {
                        foreach ($sel as $i => $row) {
                            //exact match go for it alone
                            if (strtolower($row[RF_NODES_NAME]) == strtolower($loc)) {
                                $sel = array($i => $row);
                                break;
                            }
                            //if the full name contains "road" but the requested name does not
                            //omit this choice to avoid stations like "london road" when looking for london
                            if (preg_match('/\\b(road)\\b/i', $row[RF_NODES_NAME], $m)) {
                                if (!clb_contains($loc, $m[1], FALSE)) {
                                    unset($sel[$i]);
                                }
                            }
                        }
                    }
                }
            }
        }
        if (clb_count($sel) <= 0) {
            $result = array('error' => 602, 'error_str' => 'no stations with the name ' . $loc);
            return $result;
        }
        $waypoints[] = $sel;
    }
    return $result;
}
Exemplo n.º 3
0
                    $pt = reset($points);
                    $query .= ',' . RF_LINKS_END1 . '=' . clb_escape(clb_b64e($pt[0], 10000.0) . clb_b64e($pt[1], 10000.0));
                    $pt = end($points);
                    $query .= ',' . RF_LINKS_END2 . '=' . clb_escape(clb_b64e($pt[0], 10000.0) . clb_b64e($pt[1], 10000.0));
                    $query = 'UPDATE ' . RF_LINKS_FROM . ' SET ' . trim($query, ', ') . ' WHERE pnum=' . clb_escape($pnum);
                    $wpdb->query($query);
                }
            }
            break;
        case 'blowup':
            $path = dirname(__FILE__);
            require_once $path . '/blowup.php';
            break;
    }
} else {
    $cmd = (clb_contains(__FILE__, 'toby') ? '/usr/local/php5/bin/php' : 'php') . ' -f ' . __FILE__ . ' -- ';
    echo clb_tag('h1', 'Route finder lookup table generator');
    echo clb_tag('p', 'This page lists the processes and files that need to be generated to prepare the route finder to function.
	Generally things higher on the page need to be done before things lower down.
	When things need to be done, you should copy and paste the command into command line, and then refresh this page to see the new statuses.');
    //CHECK IF NEAR ARRAY NEEDS REBUILDING
    echo clb_tag('h3', 'Metaphone File');
    echo clb_tag('p', 'This file assists with looking up stations by name by keeping an index of the words and sounds in the names. 
	You only need this file if you are doing named lookups and it should be regenerated if you have changed or added stations.');
    $path = $metaphones;
    if (!is_file($path)) {
        echo clb_tag('p', 'There is no file, you need to create this', '', array('style' => 'color:red;'));
    } else {
        //check nodes of all stop/station types
        $time = clb_escape(clb_now_utc(filemtime($path)));
        $query = 'SELECT 1 FROM ' . RF_NODES_FROM . ' WHERE modified > ' . $time . ' AND ' . RF_NODES_TYPE . ' IN ' . clb_join($metaphone_types, TRUE);