Ejemplo n.º 1
0
function save_line_query($points, $spec)
{
    $query = '';
    //identify line and points fields based on the table spec
    list($line_fld, $points_fld) = explode('/', clb_val(FALSE, $spec, 'polyline') . '/');
    if ($points && ($mid = pline_midpoint($points))) {
        list($_REQUEST['lat'], $_REQUEST['lng']) = $mid;
    }
    $pt = reset($points);
    $_REQUEST['end1'] = clb_b64e($pt[0], 10000.0) . clb_b64e($pt[1], 10000.0);
    $pt = end($points);
    $_REQUEST['end2'] = clb_b64e($pt[0], 10000.0) . clb_b64e($pt[1], 10000.0);
    if ($points_fld) {
        $val = '';
        foreach ($points as $pt) {
            $val .= join(',', $pt) . "\n";
        }
        $query .= '`' . $points_fld . '`=' . clb_escape($val) . ',';
    }
    if ($line_fld) {
        $line = $points ? pline_make($points, array('color' => '#0000FF')) : '';
        if ($line) {
            $line = clb_join($line, '', '&', '=');
        }
        $query .= '`' . $line_fld . '`=' . clb_escape($line) . ',';
    }
    return $query;
}
Ejemplo n.º 2
0
function pbuild_daisychain($rnums, $primitives, $stopsegs)
{
    global $wpdb;
    if (!is_array($rnums)) {
        $rnums = array($rnums);
    }
    $now = time();
    $processed = clb_now_utc($now);
    $links_count = 0;
    //platforms mapped to stations with converging lines
    $plats = pbuild_platforms();
    $stat2plat = $plats['stat2plat'];
    $plat2stat = $plats['plat2stat'];
    //select route records that list stops rather than segments
    $query = 'SELECT ' . RF_ROUTES_SELECT . ',' . RF_ROUTES_STOPS . ',' . RF_ROUTES_SBACK . ' FROM ' . RF_ROUTES_FROM . ' WHERE ' . RF_ROUTES_KEY . ' IN ' . clb_join($rnums, TRUE);
    $routes = $wpdb->get_results($query, ARRAY_A);
    if (is_array($routes)) {
        $max_routes = count($routes);
        foreach ($routes as $rt_no => $route) {
            if ($rt_no % round($max_routes / 100) == 0) {
                echo round(100 * ($rt_no / $max_routes)) . '% complete' . "\n";
            }
            $rnum = clb_val(FALSE, $route, RF_ROUTES_KEY);
            $ptype = clb_val(FALSE, $route, RF_ROUTES_TYPE);
            if (!$ptype) {
                qlog(__FUNCTION__, __LINE__, 'route did not have ptype which is necessary to give the result links types', $route);
                continue;
            }
            //assume a route is reversable unless it is blocked (by '*' in the field making it not
            //empty but not listing anythign) or explicitly lists the reverse version
            if (empty($route[RF_ROUTES_SBACK])) {
                $lines = preg_split('/[\\r\\n]+/', $route[RF_ROUTES_STOPS]);
                $route[RF_ROUTES_SBACK] = join("\n", array_reverse($lines)) . "\n";
            }
            //scanning directions separately
            foreach (array(RF_ROUTES_STOPS => '', RF_ROUTES_SBACK => '.') as $dir => $dot) {
                $last_comment = '';
                //split out stop pnums from the segs list on the route record
                if (preg_match_all('/^([\\w<>]+)(\\S*\\s(.*))?$/m', $route[$dir], $lines, PREG_SET_ORDER)) {
                    $nodes1 = FALSE;
                    $max_segs = count($lines);
                    qlog(__FUNCTION__, __LINE__, $max_routes, $rt_no, $max_segs, $rnum);
                    //loop thorugh each stop/station pnum just extracted from stops_list / stops_back
                    foreach ($lines as $ln_no => $stop) {
                        $nodes2 = clb_val(FALSE, $stop, 1);
                        //get stop pnum from the regular expression above
                        if (preg_match('/<\\w+>/', $nodes2)) {
                            //this is a section breaker, this stops the previous and next stops from getting a connecting link
                            //at 09/09/09 there were no routes using this feature.
                            $nodes1 = $nodes2 = FALSE;
                            continue;
                        }
                        /*
                        	node2 is the stop/station pnum just extracted from the list above
                        	if the station has plaforms we will make nodes2 an array of plafroms
                        	but if not we just wrap the station pnum in an array
                        */
                        $nodes2 = isset($stat2plat[$nodes2]) ? $stat2plat[$nodes2] : array($nodes2);
                        //get the comments after the pnum and look for a time in minutes
                        $comment = clb_val(FALSE, $stop, 3);
                        $mins = FALSE;
                        //get time to travel path as minutes from the end of the comment which will end "*22"
                        if (preg_match('/\\*(\\d+)$/', $comment, $parts)) {
                            $mins = $parts[1];
                        }
                        $comment = preg_replace('/\\s+\\*(\\d+)$/', '', $comment);
                        /*
                        	$segs1/2 will hold an array of segment pnums that are near to the station or platforms in $node1/2
                        	the key of each $segs2 element is also the key to the data for that segment/node intersection in $stopsegs
                        	ie $segs1[stop_pnum][$k] = seg_pnum, $stopsegs['data'][$k] == segment data
                        */
                        $segs2 = array();
                        foreach ($nodes2 as $k => $pnum2) {
                            $keys = array_keys($stopsegs['stoppnums'], $pnum2);
                            if (!count($keys)) {
                                qlog(__FUNCTION__, __LINE__, 'stop not found in segmap: ', $pnum2);
                                //assume this is a garbage line and remove from array as it will cause more errors later if kept
                                unset($nodes2[$k]);
                                continue;
                            } else {
                                foreach ($keys as $key) {
                                    $segs2[$pnum2][$key] = $stopsegs['segpnums'][$key];
                                }
                            }
                        }
                        //when we have two stops (ie after first loop) we need to find/make a link
                        $links_made = 0;
                        //since matching to any given platform may legitmately fail, count total links within loop and give err after if still 0
                        $dist = 0;
                        $linkrec = FALSE;
                        if ($nodes1 != FALSE) {
                            $set_set = array();
                            //in case we get more than one path because of multiple platforms accumulate them in an array first
                            $best_dist = FALSE;
                            //will track the best distance on a set
                            //producting platforms but this will usually be only one node in each array
                            foreach ($nodes1 as $pnum1) {
                                foreach ($nodes2 as $pnum2) {
                                    //see if we already have such a link and update it if necessary
                                    $linkpnum = FALSE;
                                    $process = TRUE;
                                    $use_angles = TRUE;
                                    $forward = TRUE;
                                    $set = array();
                                    $query = 'SELECT ' . RF_LINKS_SELECT . ',' . RF_LINKS_POINTS . ' FROM ' . RF_LINKS_FROM . ' WHERE ' . RF_LINKS_TYPE . '= ' . clb_escape($ptype);
                                    $query .= ' AND ((' . RF_LINKS_END1 . '=' . clb_escape($pnum2) . ' AND ' . RF_LINKS_END2 . '=' . clb_escape($pnum1) . ') ';
                                    $query .= 'OR (' . RF_LINKS_END1 . '=' . clb_escape($pnum1) . ' AND ' . RF_LINKS_END2 . '=' . clb_escape($pnum2) . '))';
                                    $linkrec = $wpdb->get_results($query, ARRAY_A);
                                    //should only be one
                                    if (clb_count($linkrec) > 1) {
                                        //bad, dont want multiple links between same nodes
                                        qlog(__FUNCTION__, __LINE__, 'multiple links for same nodes and mode', $pnum1, $pnum2, array_keys($linkrec));
                                        do_query('DELETE FROM ' . RF_LINKS_FROM . ' WHERE pnum IN ' . clb_join(array_keys($linkrec), TRUE), __FILE__, __LINE__);
                                        //delete them all and start again
                                        $wpdb->query($query);
                                        $linkrec = FALSE;
                                    }
                                    if (is_array($linkrec)) {
                                        //links requiring reversing need to have "<>" added to the title field of the generated link
                                        // <>Newark Castle - Newark North Gate - this is across a junction not from a terminus
                                        // <>Liskeard- St Keyne - backs out of a terminus before splitting off at next junction
                                        $linkrec = reset($linkrec);
                                        $set[RF_LINKS_KEY] = $linkrec[RF_LINKS_KEY];
                                        $set[RF_LINKS_DIST] = $linkrec[RF_LINKS_DIST];
                                        $use_angles = !preg_match('/^<>/', $linkrec[RF_LINKS_NAME]);
                                        //hand altered links that have <> at the beginning of the name do not require angles to match as trains must reverse
                                        $process = clb_get_stamp(clb_val(FALSE, $linkrec, RF_LINKS_MODIFIED)) < $now;
                                        //0 if fails
                                        /*
                                        	reverse value of 1 means not in reverse direction, 2 means not in forward direction
                                        	If the record has not been processed in this session, we start by blocking the other direction
                                        	but if we end up processing it in the other direction too, we will unblock it.
                                        */
                                        $end1 = clb_val(FALSE, $linkrec, RF_LINKS_END1);
                                        $forward = $end1 == $pnum1;
                                        //direction we are going this time
                                        $reverse = clb_val(FALSE, $linkrec, RF_LINKS_REVERSE);
                                        //the existing reverse value on the link
                                        if ($process) {
                                            $set[RF_LINKS_REVERSE] = $forward ? 1 : 2;
                                            //first run, block the other direction
                                        } else {
                                            if ($reverse == 1 && !$forward) {
                                                $set[RF_LINKS_REVERSE] = 0;
                                                //seen both directions, unblock
                                            } else {
                                                if ($reverse == 2 && $forward) {
                                                    $set[RF_LINKS_REVERSE] = 0;
                                                    //seen both directions, unblock
                                                }
                                            }
                                        }
                                    } else {
                                        $linkrec = FALSE;
                                        $set[RF_LINKS_REVERSE] = 1;
                                        //new records are forwards not reverse by definition
                                    }
                                    /*
                                    									the only time we dont process is if this has been processed in this run, 
                                    									such as the reverse direction of same route, or two routes with a common path section
                                    								
                                    									both stations can be on multiple segs (when near junctions or cross roads) so product 
                                    									combinations and accumulate resulting paths but usually only going to get connections 
                                    									on one of the producted list of segments, 
                                    									so if point A on segs 1 & 2 and point B on segs 3 & 4 may only get connection on 1 with 3
                                    */
                                    if ($process) {
                                        $thru_paths = array();
                                        /*
                                        	we will look at the closeness of stops to different segments and try the one that the 
                                        	stops are closest to first.  this not only saves time as we dont try to connect unconnected
                                        	segs but prevents stops near junctions getting attached to the one they are not closest too
                                        	$k1 & $k2 are the numerical key values that link the separate parts of $stopsegs
                                        	$n1 & $n2 are the segment pnums on which the $pnum1 and $pnum2 nodes are near.
                                        */
                                        if (count($segs1[$pnum1]) > 1 || count($segs2[$pnum2]) > 1) {
                                            $c1 = $c2 = FALSE;
                                            foreach ($segs1[$pnum1] as $k1 => $n1) {
                                                if (isset($stopsegs['data'][$k1]) && (is_bool($c1) || $stopsegs['data'][$k1]['near'] < $stopsegs['data'][$c1]['near'])) {
                                                    $c1 = $k1;
                                                }
                                            }
                                            foreach ($segs2[$pnum2] as $k2 => $n2) {
                                                if (isset($stopsegs['data'][$k2]) && (is_bool($c2) || $stopsegs['data'][$k2]['near'] < $stopsegs['data'][$c2]['near'])) {
                                                    $c2 = $k2;
                                                }
                                            }
                                            //	ACTUAL CALL TO SHORTEST PATH: pbuild_shortpath(seg_pnum1, seg_pnum2, seg1_data, seg2_data)
                                            $thru_paths = pbuild_shortpath($segs1[$pnum1][$c1], $segs2[$pnum2][$c2], $stopsegs['data'][$c1], $stopsegs['data'][$c2], $primitives, $use_angles);
                                            //ACTUAL CALL TO SHORTEST PATH
                                        }
                                        /*
                                        	if the above managed to find a path from the segments closest to each station then we are done, but if not
                                        	we now need to loop through the possibilities
                                        */
                                        if (count($thru_paths) == 0) {
                                            foreach ($segs1[$pnum1] as $k1 => $n1) {
                                                foreach ($segs2[$pnum2] as $k2 => $n2) {
                                                    /*
                                                    	because we are going to do shortest path on segments we need to pass in the actual 
                                                    	stop end points because they could be on very long segments
                                                    	ACTUAL CALL TO SHORTEST PATH: pbuild_shortpath(seg_pnum1, seg_pnum2, seg1_data, seg2_data)
                                                    */
                                                    $temp = pbuild_shortpath($n1, $n2, $stopsegs['data'][$k1], $stopsegs['data'][$k2], $primitives, $use_angles);
                                                    if (clb_count($temp)) {
                                                        $thru_paths = array_merge($thru_paths, $temp);
                                                    }
                                                    //add results after each loop
                                                }
                                            }
                                        }
                                        if (count($thru_paths) <= 0) {
                                            //if one or both end points was a platform, dont give an error as we expect some platforms to fail
                                            if (!array_key_exists($pnum1, $plat2stat) && !array_key_exists($pnum2, $plat2stat)) {
                                                qlog(__FUNCTION__, __LINE__, 'no link path found for', $dir, $pnum1, $pnum2, $use_angles ? 'angles' : 'flex', array_key_exists($pnum1, $plat2stat), array_key_exists($pnum2, $plat2stat));
                                            }
                                            $set = array();
                                            //clear this to prevent record being saved, because the reverse will have been set
                                        } else {
                                            /*
                                            	although each call to pbuild_shortpath can at most return one path, multiple lines or platforms means
                                            	there are several calls and can result in more than one path.  Need to scan results to find shortest.
                                            */
                                            $best = FALSE;
                                            $min = clb_val(FALSE, $thru_paths, 0, 'dist');
                                            foreach ($thru_paths as $rec) {
                                                if (clb_val(FALSE, $rec, 'dist') <= $min) {
                                                    $best = clb_val(FALSE, $rec, 'path');
                                                }
                                            }
                                            $links_count++;
                                            //qlog(__FUNCTION__, __LINE__, count($thru_paths), 'path found: ', $pnum1, $pnum2, $links_count, $ln_no, $best);
                                            /*
                                            	if we are processing the path, it is the first pass which is usually the forward pass
                                            	but if one route considers this backwards and another one considers it forwards and the backwards one
                                            	processes first, then need to reverse this result rather than swapping the end points on the record
                                            */
                                            $points = pline_splice($wpdb, $best, $pnum1, $pnum2, $stopsegs);
                                            if (!$forward) {
                                                $points = array_reverse($points);
                                            }
                                            if (clb_count($points)) {
                                                if (is_numeric($mins)) {
                                                    $set[RF_LINKS_TIME] = $mins;
                                                }
                                                if (!$linkrec) {
                                                    $set[RF_LINKS_END1] = $pnum1;
                                                    //may already be set
                                                    $set[RF_LINKS_END2] = $pnum2;
                                                }
                                                //the polyline is only made for debugging and inspection not for final display
                                                $polyline = pline_make($points, array('color' => '#0000FF'));
                                                $set[RF_LINKS_LINE] = clb_join($polyline, '', '&', '=');
                                                $dist = clb_val(0, $polyline, 'meters');
                                                $set[RF_LINKS_DIST] = $dist;
                                                //remove any unused points from the points list and save as a structure
                                                $temp = $points;
                                                $points = array();
                                                foreach ($temp as $pt) {
                                                    if (isset($pt['gran'])) {
                                                        $points[] = $pt;
                                                    }
                                                }
                                                $set[RF_LINKS_POINTS] = clb_blob_enc($points);
                                                //set the "position" for the segment marker as the mid point
                                                if ($midpoint = pline_midpoint($points)) {
                                                    $set['lat'] = $midpoint[0];
                                                    $set['lng'] = $midpoint[1];
                                                    $set['elev'] = $midpoint[2];
                                                }
                                                //last comment is the previous station/stop name so that this link gets the "A - B" name
                                                $name = $last_comment . ' - ' . $comment;
                                                if (!$use_angles) {
                                                    $name = '<>' . $name;
                                                }
                                                $set[RF_LINKS_NAME] = substr($name, 0, 70);
                                            }
                                        }
                                    }
                                    /*
                                    	we may have more than one set of results if there were multiple routes
                                    	save each set and remember best distance so we can find the best one below
                                    */
                                    if (clb_count($set)) {
                                        $set_set[] = $set;
                                        if (is_bool($best_dist) || $set[RF_LINKS_DIST] < $best_dist) {
                                            $best_dist = $set[RF_LINKS_DIST];
                                        }
                                        //will track the best distance on a set
                                    }
                                }
                            }
                            //loop producting platforms, only 1 loop if both stations
                            //now only create best distance link
                            $links_made = 0;
                            foreach ($set_set as $set) {
                                /*
                                	may need to allow more than one connection eg waterloo to clapham junction, some trails take north platform some south
                                	*** the above is true and important, lines may come in as one but then diverge at a station.  there needs
                                	to be a line in to each of the platforms so that journeys can continue out from both platforms.
                                	limit to within 200m of best distance which should include all platforms but wont allow for long alternative routes.
                                */
                                if ($set[RF_LINKS_DIST] <= $best_dist + 200) {
                                    $links_made++;
                                    //if the direction is not being set then this signals that nothing else needs saving either
                                    if (isset($set[RF_LINKS_REVERSE])) {
                                        $set[RF_LINKS_MODIFIED] = $processed;
                                        $set[RF_LINKS_TYPE] = $ptype;
                                        if (isset($set[RF_LINKS_KEY])) {
                                            $query = 'UPDATE ' . RF_LINKS_FROM . ' SET ' . clb_join($set, '"', ',', '=') . ' WHERE ' . RF_LINKS_KEY . '=' . clb_escape($set[RF_LINKS_KEY]);
                                        } else {
                                            $set[RF_LINKS_KEY] = pbuild_new_pnum(RF_LINKS_FROM, RF_LINKS_KEY);
                                            $set[RF_LINKS_CREATED] = $processed;
                                            $query = 'INSERT INTO ' . RF_LINKS_FROM . ' SET ' . clb_join($set, '"', ',', '=');
                                            qlog(__LINE__, 'inserting', count($set_set), $nodes1, $nodes2, $set[RF_LINKS_DIST], $set[RF_LINKS_END1], $set[RF_LINKS_END2], $set[RF_LINKS_KEY]);
                                        }
                                        $wpdb->query($query);
                                    }
                                } else {
                                    if (isset($set[RF_LINKS_KEY])) {
                                        //this is a saved record that has been bettered so must be removed
                                        qlog(__FUNCTION__, __LINE__, 'deleting bettered link to a platform', $set[RF_LINKS_KEY], $set[RF_LINKS_DIST], $best_dist);
                                        pbuild_del_rec(RF_LINKS_FROM, RF_LINKS_KEY . '=' . clb_escape($set[RF_LINKS_KEY]));
                                    }
                                }
                            }
                            //if we found an existing link then $linkrec is not false and if links_made >0 then we made one
                            if ($links_made <= 0) {
                                qpre(__FUNCTION__, __LINE__, '***** no paths found between nodes: ', $rnum, join(', ', $nodes1), join(', ', $nodes2), 'p1 on segs: ' . join(', ', $segs1[$pnum1]), 'p2 on segs: ' . join(', ', $segs2[$pnum2]), $comment);
                            }
                        }
                        //have two nodes to link
                        //carry values to the next loop
                        $segs1 = $segs2;
                        $nodes1 = $nodes2;
                        $last_comment = $comment;
                        //carry comment (ie station name) over so next link can get name "this - next"
                    }
                }
            }
        }
    }
}