Example #1
0
function pfind_service($prms, $pfind_defs, $type_names, $p2p_paths, $node_types = FALSE)
{
    clb_timing(__LINE__);
    pfind_def_tables($pfind_defs);
    $result = array('error' => 200, 'error_str' => 'no errors');
    //get the query or way points from the request
    $entries = array();
    if (isset($prms['q']) && $prms['q']) {
        $entries[] = $prms['q'];
    } else {
        $w = 0;
        while (isset($prms['w' . $w])) {
            $entries[] = $prms['w' . $w];
            $w++;
        }
    }
    $prop = array();
    $prop['service'] = strtolower(clb_val('json', $prms, 's'));
    //json, xml
    $prop['mode'] = strtolower(clb_val('', $prms, 'm'));
    $prop['units'] = strtolower(clb_val('mi', $prms, 'u'));
    //km, mi
    $prop['opto'] = strtolower(clb_val('best', $prms, 'o'));
    //dist, change, best
    $prop['opacity'] = strtolower(clb_val(0.5, $prms, 't'));
    $prop['color'] = strtolower(clb_val('#0000FF', $prms, 'c'));
    $prop['stroke'] = strtolower(clb_val(5, $prms, 'k'));
    $dist_only = FALSE != clb_val(0, $prms, 'gd');
    //get result from precalculated distances
    $prop['dist_only'] = $dist_only;
    $prop['getSteps'] = !$dist_only && FALSE != clb_val(0, $prms, 'gs');
    $prop['getPolyline'] = !$dist_only && FALSE != clb_val(0, $prms, 'gp');
    $path = FALSE;
    $mode = $prop['mode'];
    if (is_string($p2p_paths)) {
        $path = $p2p_paths;
    } else {
        if (!is_array($p2p_paths)) {
            $result = array('error' => 500, 'error_str' => 'server configuration error: route tables not specified');
        } else {
            if ($mode && isset($p2p_paths[$mode])) {
                $path = $p2p_paths[$mode];
            } else {
                if (isset($p2p_paths['rtm'])) {
                    $node_types = array('rail', 'tube', 'tram');
                    $path = $p2p_paths['rtm'];
                } else {
                    $path = reset($p2p_paths);
                }
            }
        }
    }
    if (is_array($path)) {
        if (!$node_types) {
            $node_types = clb_val(FALSE, $path, 'types');
        }
        //get the types
        $path = clb_val(FALSE, $path, 'path');
    }
    //if types not given as param or with paths, but the mode name is a node type use it as default.
    if (!$node_types && clb_val(FALSE, $type_names, $prop['mode'])) {
        $node_types = $prop['mode'];
    }
    if ($node_types && !is_array($node_types)) {
        $node_types = array($node_types);
    }
    //make types an array if just a single
    if (!$path || !is_string($path) || !is_file($path)) {
        $result = array('error' => 500, 'error_str' => 'server configuration error: route tables could not be loaded');
    } else {
        $waypoints = array();
        $result = pfind_interpret($entries, $mode, $waypoints, $path, $node_types);
        qlog(__LINE__, $mode, $entries, $result);
        if (IS_LOCAL) {
            foreach ($waypoints as $i => $stage) {
                foreach ($stage as $see) {
                    qlog(__LINE__, $i, join(', ', $see));
                }
            }
        }
        clb_timing('interpret');
    }
    if ($result['error'] == 200) {
        $links = FALSE;
        if (!$prop['dist_only'] && is_file($path)) {
            $data = file_get_contents($path);
            //, FILE_BINARY);	//need different p2p files for different combinations of modes
            $links = clb_blob_dec($data);
        }
        clb_timing('load array');
        if (!$links && !$prop['dist_only']) {
            $result = array('error' => 500, 'error_str' => 'point to point data file could not be found/loaded ' . $mode);
            qlog(__LINE__, $result, $mode, $path, $p2p_paths);
        } else {
            $result = pfind_routes($waypoints, $links, $prop, $type_names);
        }
        qlog(__LINE__, clb_timing('find path'));
    }
    //qlog(__LINE__,clb_xml($result, 'RouteFinder'));
    // qlog(__LINE__,clb_json($result, "'"));
    switch ($prop['service']) {
        case 'json':
            $return_data = json_encode($result);
            rs_response('application/json', $return_data, 'UTF-8');
            break;
        case 'javascript':
            $sid = clb_val('', $prms, 'sid');
            $func = clb_val('mfw_dir_result', $prms, 'callback');
            $return_data = clb_json($result, "'");
            $callback = $func . '(\'' . $sid . '\',' . $return_data . ');';
            clb_response('application/javascript', $callback, 'UTF-8');
            break;
        case 'xml':
            $return_data = clb_xml($result, 'RouteFinder');
            clb_response('xml', $return_data, 'UTF-8');
            break;
        case 'php':
            return $result;
            break;
    }
}
Example #2
0
function pbuild_shortpath($seg1, $seg2, $data1, $data2, &$primitives, $use_angles = TRUE)
{
    $allowance = 4;
    //factor of crow fly distance allowed before abandoning route, if (dist > (allowance * crowfly)) dont follow
    $margin = 1.2;
    //factor of the shortest path to stop collecting alternatives.
    $bends = 80;
    //bend angles up to 80 degrees
    //qlog(__FUNCTION__, __LINE__, $node1, $node2, $target);
    clb_timing(__LINE__);
    //check we have entries in the primitives for the start and end segments in the first two params
    if (!isset($primitives['links'][$seg1])) {
        qlog(__FUNCTION__, __LINE__, 'raw path not found because end node unknown', $seg1, $seg1 . '->' . $seg2);
        return array();
    }
    if (!isset($primitives['links'][$seg2])) {
        qlog(__FUNCTION__, __LINE__, 'raw path not found because end node unknown', $seg2, $seg1 . '->' . $seg2);
        return array();
    }
    //if start and end segments are the same we are done.
    if ($seg1 == $seg2) {
        return array(array('dist' => 0, 'path' => array($seg1)));
    }
    //keep track of actual end point locations which may not be near the end of the start and end segments
    $starter = array($data1['lat'], $data1['lng']);
    $target = array($data2['lat'], $data2['lng']);
    //the direct distance gives some indication of reasonable tollarances when evaluating segments
    $crowfly = pline_surface_dist($target[0], $target[1], $starter[0], $starter[1]);
    if ($crowfly == 0) {
        return array();
    }
    //if no distance from start to end then no path to be found
    $seq = 0;
    //key for the $leaves array
    $leaves = array();
    //info on nodes visited by shortest path
    //holds info on segments to be evaluated, always pick best ones not first added.
    //key values are 'x'.leaf_key to make them strings and preserve them when sorting by values
    //and values are distance from leaf to target
    $queue = array();
    /*
    	we don't know which end of the start segment is going to lead to the end segment, so add both ends
    	to the queue, so long as the distance to each end is not already beyond the tollerance
    */
    $pt = $primitives['links'][$seg1]['pt1'];
    $pythag = pline_surface_dist($target[0], $target[1], $pt[0], $pt[1]);
    //distance from end point to target
    if ($data1['dist1'] + $pythag < $crowfly * $allowance) {
        $node = $seq++;
        $leaves[$node]['par'] = FALSE;
        //root node has no parent
        $leaves[$node]['node'] = $primitives['links'][$seg1]['end1'];
        $leaves[$node]['link'] = $seg1;
        $leaves[$node]['dist'] = $data1['dist1'];
        $leaves[$node]['angle'] = clb_val(FALSE, $primitives, 'links', $seg1, 'angle1');
        $queue['x' . $node] = round($leaves[$node]['dist'] + $pythag);
    }
    $pt = $primitives['links'][$seg1]['pt2'];
    $pythag = pline_surface_dist($target[0], $target[1], $pt[0], $pt[1]);
    if ($data1['dist2'] + $pythag < $crowfly * $allowance) {
        $node = $seq++;
        $leaves[$node]['par'] = FALSE;
        //root node has no parent
        $leaves[$node]['node'] = $primitives['links'][$seg1]['end2'];
        $leaves[$node]['link'] = $seg1;
        $leaves[$node]['dist'] = $data1['dist2'];
        $leaves[$node]['angle'] = clb_val(FALSE, $primitives, 'links', $seg1, 'angle2');
        $queue['x' . $node] = round($leaves[$node]['dist'] + $pythag);
    }
    /*
    	if neither segment end was added then both were too long to reach the target within tollerance
    	this is not an error as some segments are simply not in the route we are looking for
    */
    //if ($seq == 0) qlog(__FUNCTION__, __LINE__, 'raw path rejected because ends of first segment beyond tollerances',$seg1, $seg2);
    if ($seq == 0) {
        return array();
    }
    $complete = array();
    //collects end nodes of one or more "shortest" 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
    $used = array();
    //real shortest path does not backtrack or reuse segments, so once seen add to used list
    while (count($queue)) {
        asort($queue);
        //put lowest score first
        //qlog(__LINE__, $crowfly, $queue);
        $dist = reset($queue);
        //set first element current
        $leaf = (int) trim(key($queue), 'x');
        //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
        //if we have a match and all remaining options are worse by a margin then stop looking
        if (!is_bool($best) && $best * $margin < $dist) {
            break;
        }
        /*
        	$leaf is an integer index into the $leaves array which represents a segment that has already been added
        	we now want to find further segments attached to the other end of the $leaf segment
        	the element should always exist so the following test is just belt and braces
        */
        if (!isset($leaves[$leaf])) {
            continue;
        }
        $code_from = $leaves[$leaf]['node'];
        //the end_code of the end of the leaf segment we want to continue from
        $last_dist = $leaves[$leaf]['dist'];
        //distance of the path this is in including the leaf segment
        $last_angle = $leaves[$leaf]['angle'];
        //angle at the end we are trying to follow on from
        $last_link = $leaves[$leaf]['link'];
        //link from the parent
        //loop though the primitive links which are listed as having this code_from node as an end
        if (clb_count($primitives['nodes'][$code_from])) {
            foreach ($primitives['nodes'][$code_from] as $linkpnum) {
                //this is really a shortest path so don't reuse any segs as predecessor will have scored better than this try can
                if (isset($used[$linkpnum])) {
                    continue;
                }
                $linkdata = $primitives['links'][$linkpnum];
                //details for this primitive segment
                $end1 = clb_val(FALSE, $linkdata, 'end1');
                $end2 = clb_val(FALSE, $linkdata, 'end2');
                $reverse = $code_from == $end2;
                //non reversable link and an attempt to use it in the wrong direction, skip it
                if ($reverse && clb_val(0, $linkdata, 'reverse') & 1) {
                    continue;
                }
                //reverse value of 1 means not in reverse direction
                if (!$reverse && clb_val(0, $linkdata, 'reverse') & 2) {
                    continue;
                }
                //reverse value with 2 means not in forward direction
                if ($use_angles && $last_angle !== FALSE) {
                    //compare angle from end of last seg with angle on the beginning of this one
                    $this_angle = !$reverse ? clb_val(FALSE, $linkdata, 'angle1') : clb_val(FALSE, $linkdata, 'angle2');
                    $kink = abs(rad2deg($last_angle - $this_angle));
                    /*
                    	the difference of the two angles will be 180 if they connect on a straight line, 
                    	we are allowing connecting angles of 180 +/- 80 degrees, if outside the range skip this segment.
                    */
                    if ($kink < 180 - $bends || $kink > 180 + $bends) {
                        continue;
                    }
                }
                //no more tests we are adding this segment as leaf in the paths tree
                $otherend = $reverse ? $end1 : $end2;
                $dist = $last_dist + clb_val(0, $linkdata, 'dist');
                //add this link distance to the total
                //if this is the final segment only add the part of the length to the point
                if ($linkpnum == $seg2) {
                    $dist = $last_dist + (!$reverse ? clb_val(0, $linkdata, 'dist1') : clb_val(0, $linkdata, 'dist2'));
                }
                $used[$linkpnum] = 1;
                //so we dont reuse this segment
                $node = $seq++;
                $leaves[$node]['par'] = $leaf;
                //record how we got to this node
                $leaves[$node]['node'] = $otherend;
                //the end we will continue on from
                $leaves[$node]['link'] = $linkpnum;
                //the pnum of this primitive segment
                $leaves[$node]['dist'] = $dist;
                //total distance including this segment
                //angle of the end of this segment, to be compared with angles of follow on segments, so train does not turn sharp corners.
                $leaves[$node]['angle'] = $reverse ? clb_val(0, $linkdata, 'angle1') : clb_val(0, $linkdata, 'angle2');
                // qlog(__FUNCTION__, __LINE__, $node, $otherend, $last_link, $linkpnum, $reverse, $dist);
                //if this was the target segment then record it as a complete route, otherwise add the added leaf to the queue
                if ($linkpnum == $seg2) {
                    //we have reached our destination, hoozah!
                    //remember the end leaf and the distance so we can easily rank results.
                    $complete[$node] = $dist;
                    if (is_bool($best)) {
                        $best = $dist;
                    }
                    break;
                    //*** since this is now a true shortest path we will not continue after getting a result
                } else {
                    //see how far away this link leaves us and add this to the dist
                    $pt = clb_val(FALSE, $linkdata, $reverse ? 'pt1' : 'pt2');
                    $pythag = pline_surface_dist($target[0], $target[1], $pt[0], $pt[1]);
                    if ($crowfly && $allowance && $pythag > $crowfly * $allowance) {
                        //this is a natural thing to happen as less promising paths are abandoned, no need to log these unless debugging
                        //qlog(__FUNCTION__, __LINE__, 'branch abondoned as route longer than distance', $node1, $linkpnum, $last_dist, $dist, $pythag, $crowfly);
                        continue;
                    }
                    $queue['x' . $node] = round($dist + $pythag);
                    //add this leaf to the processing queue with the total distance
                }
            }
        }
    }
    //qlog(__LINE__,'raw path time', clb_timing(__LINE__));
    //qlog(__FUNCTION__, __LINE__, $leaves);
    /*
    	originally this method found a selection of short routes, but now only finds the true shortest
    	however, the ability to handle multiples has not been removed
    	
    	the $complete array holds the leaf index of the last segment in the chain.  
    	need to follow backwards to from parent to parent to get the full list of segs in the sequence.
    */
    $result = array();
    if (count($complete)) {
        foreach ($complete as $key => $dist) {
            $path = array();
            while (is_int($key)) {
                if ($leaves[$key]['link']) {
                    array_unshift($path, $leaves[$key]['link']);
                }
                $key = $leaves[$key]['par'];
            }
            $result[] = array('dist' => $dist, 'path' => $path);
        }
    }
    return $result;
}
Example #3
0
function blow_load_nodes($node1, &$links)
{
    clb_timing(__LINE__);
    //check origin(s)
    if (!is_array($node1)) {
        $node1 = array($node1);
    }
    foreach ($node1 as $i => $pnum) {
        if (!isset($links['nodes'][$pnum]) || empty($links['nodes'][$pnum])) {
            unset($node1[$i]);
            qlog(__FUNCTION__, __LINE__, 'unknown origin in shortpath', $pnum);
        }
    }
    //if no origin or destination then return without searching for a route
    if (clb_count($node1) == 0) {
        return array();
    }
    $network_d = array();
    $queue = array();
    foreach ($node1 as $c => $pnum) {
        $network_d[$pnum]['node'] = $pnum;
        $network_d[$pnum]['dist'] = 0;
        $network_d[$pnum]['depth'] = chr(48 + $c % 207);
        $network_d[$pnum]['prev'] = array();
        //this is where we start so no previous links
        $network_d[$pnum]['ptype'] = '';
        //ptype of link not node
        $queue[$pnum] = 0;
        //the initial distance only really important when more than one origin.
    }
    $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
    $max_depth = 0;
    //just for interest
    while (count($queue)) {
        asort($queue);
        //put lowest score first
        // qlog(__LINE__, $queue);
        $max_depth = max($max_depth, count($queue));
        $noden = key($queue);
        //get the key (node pnum)
        array_shift($queue);
        //remove item from queue since it will not need processing after this
        //$noden is the pnum of the node and is our index into the $network_d
        if (!isset($network_d[$noden])) {
            continue;
        }
        //the parent should always exist, just being safe.
        $last_dist = $network_d[$noden]['dist'];
        //distance up to the node on this leaf
        $last_depth = $network_d[$noden]['depth'];
        //number of links up to the node on this leaf
        //if the only link to this node was by walking remember this and don't add walking links from here
        $walking_only = FALSE;
        if (isset($network_d[$noden]['prev']) && count($network_d[$noden]['prev'])) {
            $only = reset($network_d[$noden]['prev']);
            $walking_only = $only['ptype'] == 'walk';
        }
        //now loop though the candidate links from this node
        if (isset($links['nodes'][$noden]) && $links['nodes'][$noden]) {
            $links_list = explode(',', $links['nodes'][$noden]);
            foreach ($links_list as $c => $linkpnum) {
                if (array_key_exists($linkpnum, $network_d[$noden]['prev'])) {
                    continue;
                }
                //dont rescan any links already traversed towards this node
                $linkrec = FALSE;
                if (isset($links['links'][$linkpnum])) {
                    parse_str($links['links'][$linkpnum], $linkrec);
                }
                //$dist = round($last_dist + clb_val(0, $linkrec, 'dist'), 3);	//add this link distance to the total
                $dist = round($last_dist + (isset($linkrec['dist']) ? $linkrec['dist'] : 0), 3);
                //add this link distance to the total
                //$ptype = clb_val('', $linkrec, 'ptype');	//link ptype (same as nodes except for 'walk'
                $ptype = isset($linkrec['ptype']) ? $linkrec['ptype'] : '';
                //link ptype (same as nodes except for 'walk'
                if ($walking_only && $ptype == 'walk') {
                    continue;
                }
                $end1 = isset($linkrec['end1']) ? $linkrec['end1'] : '';
                //clb_val('', $linkrec, 'end1');
                $end2 = isset($linkrec['end2']) ? $linkrec['end2'] : '';
                //clb_val('', $linkrec, 'end2');
                $reverse = $noden == $end2;
                // qlog(__FUNCTION__, __LINE__, $linkpnum, $reverse, clb_val(0, $linkrec, 'reverse'));
                //when we have multiple origins, we are doing our short path backwards so we need to reverse our reverse for the real direction
                $real_direction = $reverse;
                //($backwards ? !$reverse : $reverse);
                //non reversable link and an attempt to use it in the wrong direction, skip it
                //if ($real_direction && (clb_val(0, $linkrec, 'reverse') & 1)) continue;	//reverse value of 1 means not in reverse direction
                //if (!$real_direction && (clb_val(0, $linkrec, 'reverse') & 2)) continue;	//reverse value with 2 means not in forward direction
                if ($real_direction && (isset($linkrec['reverse']) ? $linkrec['reverse'] : 0) & 1) {
                    continue;
                }
                //reverse value of 1 means not in reverse direction
                if (!$real_direction && (isset($linkrec['reverse']) ? $linkrec['reverse'] : 0) & 2) {
                    continue;
                }
                //reverse value with 2 means not in forward direction
                //the $otherend will be different on each loop here because each link points to a different other node.
                $otherend = $reverse ? $end1 : $end2;
                if (isset($network_d[$otherend])) {
                    //if the node has been seen before then we will not add it to the queue, but we will record how we got to it after the else.
                    //if the node reached happens to be the destination, then it is already recorded as such and no need to add it again.
                    //scan previous routes to this node
                    if (is_array($network_d[$otherend]['prev'])) {
                        foreach ($network_d[$otherend]['prev'] as $link => $prev) {
                            $round_err = max(strlen($last_depth) + 1, strlen($prev['depth']));
                            //allow a meter of rounding error for each connection
                            if (abs($prev['path_dist'] - $dist) < $round_err) {
                                if (strpos($last_depth, $prev['depth']) === 0) {
                                    /*
                                    	'depth' is a map of branching and depth on current path and curren node 
                                    	match then we have been here before on this thread and are looping back so stop
                                    	and dont follow this link
                                    */
                                    $linkpnum = FALSE;
                                    break;
                                } else {
                                    if (strlen($last_depth) + 1 > strlen($prev['depth'])) {
                                        /*
                                        	if the distance to the node within a small margin is the same, we will assume 
                                        	these are the same path but perhaps with different numbers of stops
                                        	this path has more stops so keep it rather than the other one
                                        */
                                        unset($network_d[$otherend]['prev'][$link]);
                                    } else {
                                        //existing route had more stops so will not add this link
                                        $linkpnum = FALSE;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    //do this after above as above may delete a prev
                    //if MAX_PATH_OPTIONS=1 the shortpath process is very quick but ONLY gets the shortest path regardless of number of changes
                    //MAX_PATH_OPTIONS = 2 is the default.
                    if (clb_count($network_d[$otherend]['prev']) >= MAX_PATH_OPTIONS) {
                        continue;
                    }
                    //to only the best N predecessors
                } else {
                    //add this leaf to the processing queue
                    $queue[$otherend] = round($dist);
                    //this will find shortest path first
                    $network_d[$otherend]['node'] = $otherend;
                    $network_d[$otherend]['depth'] = $last_depth . chr(48 + $c % 207);
                    $network_d[$otherend]['dist'] = $dist;
                    //first one here will be shortest so keep that
                }
                if ($linkpnum) {
                    $network_d[$otherend]['prev'][$linkpnum] = array('prev_node' => $noden, 'link_pnum' => $linkpnum, 'link_len' => isset($linkrec['dist']) ? $linkrec['dist'] : 0, 'common' => $reverse ? isset($linkrec['routes2']) ? $linkrec['routes2'] : FALSE : (isset($linkrec['routes1']) ? $linkrec['routes1'] : FALSE), 'ptype' => $ptype, 'depth' => $network_d[$otherend]['depth'], 'path_dist' => $dist);
                }
            }
        }
    }
    return $network_d;
}
Example #4
0
function pline_make(&$points, $attr = FALSE)
{
    $color = clb_val('#0000FF', $attr, 'color');
    $weight = clb_val(7, $attr, 'weight');
    $opacity = clb_val(1, $attr, 'opacity');
    $pregran = clb_val(FALSE, $attr, 'pregran');
    //if points already have granularity calculated
    $max_pts = clb_val(FALSE, $attr, 'max_pts');
    //sets a target number of points for result, which is acheived by omiting detail levels
    $calc_len = clb_val(TRUE, $attr, 'calc_len');
    //option not to calculate length of segment
    $split = clb_val(FALSE, $attr, 'split');
    //split path into segments, based on segment length in meters, returns array of polylines
    $do_bounds = clb_val(FALSE, $attr, 'do_bounds');
    //include bounds in structure
    //$points: 0=>lat, 1=>lng, 2=>elev, 3=>comment from sges list on route record
    // replaced [3] with ['comment'] and ['pnum']
    $timing = FALSE;
    //timing marks for testing
    if ($timing) {
        clb_timing(__LINE__);
    }
    //initial loop here scans points to find points of greatest distance and records granularity directly in the points array
    //only points which do not lie directly between the predecessor and follower will be included in the polyline
    //the scale of the distance held allows an approprate zoom level to be assigned to each point.
    //the distance cut of point roughlyt equates to one screen pixel at full zoom, but actaul zoomlevel info added in next part not this.
    /*
    	see below for explanation and proof of formula
    	http://www.intmath.com/Plane-analytic-geometry/Perpendicular-distance-point-line.php
    	
    	distance from point (m,n) to line Ax + By + C = 0 is given by abs(Am + Bn + C) / sqrt(A^2 + B^2)
    	slope of the line is -A/B
    	to simplify the line subtract the first point from the second so that there is no incept C (ie C=0)
    	divide by A so that A becomes 1 and B becomes B/A and the distance formula becomes
    	abs(1m + (B/A)n + 0) / sqrt(1^2 + (B/A)^2)
    	abs(m + (B/A) n) / sqrt(1 + (B/A)^2)
    	in our case A is the difference of latitude and B is the differnece of longitudes
    	
    	if A is zero then we cannot divide by it but it also means the line is horizontal so the distance is just the difference in line lat and point lat
    	
    	a thrid case is when the line is only a point in which case just use pythag to get the distance
    */
    $verySmall = 1.0E-5;
    //0.00001 happens to equate to 1 screen pixel at zoom level 17
    $range = 18;
    //0= outerspce, 18=rooftops
    $numlevels = $range;
    //hard coding this so that one zoom group = one zoom level giving prceise control over point granularity
    $maxZoom = $numlevels - 1;
    $zoomFactor = pow(2, floor($range / $numlevels));
    //if every zoom level has its own zoom group then each one has a zoom factor of 2, if there are two zoom levels per group then the factor is 4.
    //set minimum distances that matter at each reverse zoom level (ie zoom level 0 has $break[$range-1])
    $breaks = array();
    for ($i = $maxZoom; $i >= 0; $i--) {
        $breaks[$i] = $verySmall * pow($zoomFactor, $i);
    }
    $points = array_values($points);
    //ensure indexes are from 0 to n-1
    $last_pt = count($points) - 1;
    if ($last_pt > 0) {
        if (!$pregran) {
            //if we have granularity on the points then we do not need to rescan
            //ensure any old granularity is removed.
            foreach ($points as $no => $pt) {
                if (isset($points[$no]['gran'])) {
                    unset($points[$no]['gran']);
                }
            }
            //using stack to scan points within line.  Start with the end points and divide into two subsets broken at
            //the point of greatest difference from the direct line between ends of the current subset
            $detail = end($breaks);
            $stack = array();
            array_push($stack, array(0, $last_pt));
            //initial values array with first and last array indicies
            $adjust = cos(deg2rad(($points[0][0] + $points[count($points) - 1][0]) / 2));
            //use one longitude adjustment factor across all latitudes based on the half way mark
            while (count($stack) > 0) {
                //stack based loop rather than recursion
                list($first, $last) = array_pop($stack);
                //since all points within subset are to be compared to the same line we can precalculate some factors of the equation
                $A = $points[$last][0] - $points[$first][0];
                $B = ($points[$last][1] - $points[$first][1]) * $adjust;
                if ($A != 0) {
                    $method = 2;
                    //assume two points in line method
                    $B = $B / $A;
                    //$A also divided by $A so becomes 1
                    $denom = sqrt(1 + pow($B, 2));
                    //since $A = 1 there is no point in using the pow function to square it and just plug in 1
                    //distance for point $x, $y now given by $dist = abs(($x - ($B * $y)) / $denom);
                } else {
                    if ($points[$last][1] == $points[$first][1]) {
                        $method = 1;
                        //if end points are coincident then simply do distance from test point to first
                    } else {
                        $method = 0;
                        //if the latitiudes of the end points are the same we would have a div by zero so will simply get difference of latitudes
                    }
                }
                $maxDist = 0;
                for ($i = $first + 1; $i < $last; $i++) {
                    $x = ($points[$i][1] - $points[$first][1]) * $adjust;
                    $y = $points[$i][0] - $points[$first][0];
                    switch ($method) {
                        case 2:
                            //two points, do point distance from line
                            $dist = abs(($x - $B * $y) / $denom);
                            break;
                        case 1:
                            //one point do pythag to get distance between points.
                            $dist = sqrt($x * $x + $y * $y);
                            break;
                        case 0:
                            //find distance by difference of lattitudes
                            //this looks wrong but si right.  The end points have the same lat so form a vertical line,
                            //the ponit we are testing therefore differs from the line by its lat also
                            $dist = abs($points[$i][0] - $points[$first][0]);
                            break;
                    }
                    //$points[$i]['dist'] = $dist;
                    if ($dist > $maxDist) {
                        $maxDist = $dist;
                        $maxLoc = $i;
                        //if($maxDist > $absMaxDist) $absMaxDist = $maxDist;	//$absMaxDist is the largest variation from the straight line, but we dont need it
                    }
                }
                //end scan of intermediate points
                if ($maxDist >= $detail) {
                    //if any point stuck out far enough
                    //the point that sticks out the most is given a distance value and will be included in the polyline
                    $gran = $maxZoom;
                    foreach ($breaks as $lvl => $limit) {
                        if ($maxDist > $limit) {
                            $gran = max(0, $lvl);
                            break;
                        }
                    }
                    $points[$maxLoc]['gran'] = $gran;
                    //also push the subsegments created when this points divides the segment just handled
                    array_push($stack, array($first, $maxLoc));
                    array_push($stack, array($maxLoc, $last));
                }
            }
        }
    }
    //set these after the granularity loop to ensure everyone keeps end points
    $points[0]['gran'] = $points[$last_pt]['gran'] = $maxZoom;
    //make sure the end points have top granularity
    $reduce = 0;
    $total = 0;
    if (is_int($max_pts)) {
        $scores = array_fill(0, count($breaks), 0);
        foreach ($points as $no => $pt) {
            if (isset($pt['gran']) && isset($scores[$pt['gran']])) {
                $scores[$pt['gran']]++;
            }
        }
        $reduce = $maxZoom;
        for ($i = $maxZoom; $i >= 0; $i--) {
            if ($total + $scores[$i] > $max_pts) {
                break;
            }
            $total += $scores[$i];
            $reduce = $i;
        }
        qlog(__LINE__, 'number of points to add', $total);
        //point count
    }
    if ($timing) {
        clb_timing('polyline gran');
    }
    $poly = array('color' => $color, 'weight' => $weight * 1, 'opacity' => $opacity * 1, 'zoomFactor' => $zoomFactor * 1, 'numLevels' => $numlevels * 1);
    $batches = array();
    //when splitting polyline collect batches in this array
    //now scan actual points again and build up polylines
    $seg_len = 0;
    $last = count($points) - 1;
    //index of last point in seg
    $pt_txt = '';
    $levels = '';
    $last_pt = FALSE;
    $accum = array(0, 0);
    //rather than use the last point each time, use the accumulated differences to reduce error on each link
    $pt = reset($points);
    $pt[0] = round($pt[0], 5);
    //clean and consistent and ensures these are numbers not strings
    $pt[1] = round($pt[1], 5);
    $bounds = array('n' => $pt[0], 's' => $pt[0], 'e' => $pt[1], 'w' => $pt[1]);
    $count = 0;
    foreach ($points as $no => $pt) {
        if ($no === 0 || $no === $last) {
            //ensure end points get highest level of importance
            $gran = $maxZoom;
        } else {
            if (isset($pt['gran'])) {
                $gran = $pt['gran'];
                if ($gran < $reduce) {
                    continue;
                }
            } else {
                $gran = 0;
                continue;
            }
        }
        $pt[0] = round($pt[0], 5);
        //work to five decimal places as that is the precision of the polylines
        $pt[1] = round($pt[1], 5);
        //do not allow two points in a row that are identical
        if (!is_array($last_pt) || $last_pt[0] != $pt[0] || $last_pt[1] != $pt[1]) {
            $count++;
            if ($do_bounds) {
                $bounds['w'] = min($bounds['w'], $pt[1]);
                //find bounds from extreme coords
                $bounds['e'] = max($bounds['e'], $pt[1]);
                $bounds['s'] = min($bounds['s'], $pt[0]);
                $bounds['n'] = max($bounds['n'], $pt[0]);
            }
            if (!$pt_txt) {
                //first loop use actual values, afterwards use differences
                $diff = clb_b64e($pt[0]) . clb_b64e($pt[1]);
                $accum[0] = $pt[0];
                $accum[1] = $pt[1];
            } else {
                $dlat = $pt[0] - $accum[0];
                $dlng = $pt[1] - $accum[1];
                $accum[0] += $dlat;
                $accum[1] += $dlng;
                $diff = clb_b64e($dlat) . clb_b64e($dlng);
                if ($calc_len && $last_pt || $split) {
                    $dist = pline_surface_dist($pt[0], $pt[1], $last_pt[0], $last_pt[1]);
                    $seg_len += $dist;
                }
            }
            $level_str = clb_b64e($gran, FALSE);
        } else {
            $diff = '';
            $level_str = '';
        }
        //close off the polyline because it is the end or we are splitting it
        if (is_numeric($split) && $seg_len > $split || $no === $last) {
            $level_str = clb_b64e($maxZoom, FALSE);
            //end this seg and start next with max zoom value
            $poly['points'] = $pt_txt . $diff;
            //finish off current line with last point
            $poly['levels'] = $levels .= $level_str;
            if ($do_bounds && clb_count($bounds) == 4) {
                $poly['bounds'] = $bounds;
            }
            if ($calc_len && is_numeric($seg_len)) {
                $poly['meters'] = round($seg_len);
            }
            //this is not part of the google spec but should not cuase problems (famous last words)
            if ($split) {
                if (!isset($batches[0])) {
                    $batches[0] = $bounds;
                }
                $batches[0]['w'] = min($bounds['w'], $batches[0]['w']);
                //find bounds from extreme coords
                $batches[0]['e'] = max($bounds['e'], $batches[0]['e']);
                $batches[0]['s'] = min($bounds['s'], $batches[0]['s']);
                $batches[0]['n'] = max($bounds['n'], $batches[0]['n']);
                $batches[] = $poly;
                $seg_len = 0;
            }
            $accum[0] = $pt[0];
            $accum[1] = $pt[1];
            //start the new with the same point we just put at the end of the last sub line
            $pt_txt = clb_b64e($pt[0]) . clb_b64e($pt[1]);
            $levels = $level_str;
            $bounds = array('n' => $pt[0], 's' => $pt[0], 'e' => $pt[1], 'w' => $pt[1]);
        } else {
            $pt_txt .= $diff;
            $levels .= $level_str;
        }
        $last_pt[0] = $pt[0];
        $last_pt[1] = $pt[1];
    }
    if ($timing) {
        qlog(__FUNCTION__, __LINE__, clb_timing('encoding'), $total, $count);
    }
    //if we broke the polyline into batches then return that array not just the last $poly
    if (count($batches)) {
        return $batches;
    } else {
        return $poly;
    }
}
Example #5
0
        $data = file_get_contents($path, FILE_BINARY);
        //need different p2p files for different combinations of modes
        $index = clb_blob_dec($data);
        ksort($index);
        qpre($index);
}
if (IS_CLI) {
    db_close();
    exit;
} else {
    if ($path) {
        clb_timing(__LINE__);
        $data = file_get_contents($path, FILE_BINARY);
        clb_timing('load');
        $map = clb_blob_dec($data);
        qpre(clb_timing('decode'), 'count', count($map, 1));
        qpre('file content length/array', $path, strlen($data), $map);
    }
}
db_close();
$base = clb_make_url() . '?m=';
echo clb_tag('p', '', clb_tag('a', 'near', '', array('href' => $base . 'near')));
echo clb_tag('p', '', clb_tag('a', 'raw', '', array('href' => $base . 'raw')));
echo clb_tag('p', '', clb_tag('a', 'stops', '', array('href' => $base . 'stops')));
echo clb_tag('p', '', clb_tag('a', 'links', '', array('href' => $base . 'links')));
echo clb_tag('p', '', clb_tag('a', 'p2p', '', array('href' => $base . 'p2p')));
echo clb_tag('p', '', clb_tag('a', 'interchange', '', array('href' => $base . 'interchange')));
echo clb_tag('p', '', clb_tag('a', 'check_raw_ends', '', array('href' => $base . '7')));
echo clb_tag('p', '', clb_tag('a', 'hardcoded test case', '', array('href' => $base . '8')));
echo clb_tag('p', '', clb_tag('a', 'audit of tyube stops', '', array('href' => $base . '9')));
echo clb_tag('p', '', clb_tag('a', 'hardcoded tests for specific short paths', '', array('href' => $base . '10')));