function pbuild_walk_links($ptypes, &$p2p_links, $stops_xref = FALSE, $meters = 400) { global $wpdb; if (!is_array($ptypes)) { $ptypes = array($ptypes); } $toll_lat = $meters / METERS_PER_DEGREE; //$toll_lat is degrees equivalent of $meters in meters from the center $count = 0; //just count how many connections we are making $pos = array_search('plat', $ptypes); //dont include platforms in interchanged (we convert stations to platforms later) if (is_int($pos)) { unset($ptypes[$pos]); } $query = 'SELECT ' . RF_NODES_SELECT . ' FROM ' . RF_NODES_FROM . ' WHERE ' . RF_NODES_TYPE . ' IN ' . clb_join($ptypes, TRUE) . ' ORDER BY lat'; $places = $wpdb->get_results($query, ARRAY_A); if (is_array($places)) { $lats = clb_column($places, 'lat'); $lngs = clb_column($places, 'lng'); $max = count($places); array_multisort($lats, SORT_ASC, $lngs, SORT_ASC, $places); foreach ($lats as $idx => $lat1) { $lng1 = $lngs[$idx]; //allow larger degree difference in longitude because they represents fewer meters //also do it for each different lat $toll_lng = $toll_lat / cos($lat1); $scan = $idx + 1; //start one higher in the selection on each pass, as previous points already compared while ($scan < $max) { $lat2 = $lats[$scan]; if (abs($lat2 - $lat1) > $toll_lat) { break; } //all remaining latitudes will be too far away $lng2 = $lngs[$scan]; if (abs($lng2 - $lng1) <= $toll_lng) { //slightly extravegant, double check distance as a circle and not just a square. $dist = pline_surface_dist($lat1, $lng1, $lat2, $lng2); if ($dist < $meters) { //nodes are within spitting distance but check if they are already connected //but first check if the nodes have platforms $pnum = clb_val(FALSE, $places, $idx, RF_NODES_KEY); if ($stops_xref && !isset($stops_xref['stops'][$pnum])) { continue; } //if no routes using this stop no need to include the interchange to it $nodes1 = isset($p2p_links['stat2plat'][$pnum]) ? $p2p_links['stat2plat'][$pnum] : array($pnum); $pnum = clb_val(FALSE, $places, $scan, RF_NODES_KEY); if ($stops_xref && !isset($stops_xref['stops'][$pnum])) { continue; } //if no routes using this stop no need to include the interchange to it $nodes2 = isset($p2p_links['stat2plat'][$pnum]) ? $p2p_links['stat2plat'][$pnum] : array($pnum); //normally there will only be one element in each array but if one or both has platforms we need to work the permutations foreach ($nodes1 as $pnum1) { foreach ($nodes2 as $pnum2) { $txt = ''; $query = 'SELECT ' . RF_NODES_SELECT . ' FROM ' . RF_NODES_FROM . ' WHERE ' . RF_NODES_KEY . ' IN ' . clb_join(array($pnum1, $pnum2), TRUE); $ends = $wpdb->get_results($query, ARRAY_A); if (pbuild_make_link($p2p_links, $ends, $dist)) { $count++; $txt = ''; if (is_array($ends)) { $peep = reset($ends); $txt .= $peep[RF_NODES_KEY] . ' / ' . $peep[RF_NODES_TYPE] . ' / ' . $peep[RF_NODES_NAME] . ', '; $p1 = $peep[RF_LINKS_TYPE]; $peep = next($ends); $txt .= $peep[RF_NODES_KEY] . ' / ' . $peep[RF_NODES_TYPE] . ' / ' . $peep[RF_NODES_NAME] . ', '; if ($p1 == $peep[RF_LINKS_TYPE] && $p1 != 'rail') { $txt = '**** non rail ' . $txt; } } qpre(__LINE__, $count, $dist, $txt); } } } //producting of end nodes } } $scan++; } } } //now ensure walking links between platforms at same station $query = 'SELECT ' . RF_NODES_SELECT . ' FROM ' . RF_NODES_FROM . ' WHERE ' . RF_NODES_TYPE . ' IN ' . clb_join(array('plat'), TRUE) . ' ORDER BY ' . RF_NODES_DESC; $places = $wpdb->get_results($query, ARRAY_A); if (clb_count($places)) { //group records by station $list = array(); foreach ($places as $rec) { $list[$rec[RF_NODES_DESC]][] = $rec; } //get list of stations so we can access details $query = 'SELECT ' . RF_NODES_SELECT . ' FROM ' . RF_NODES_FROM . ' WHERE ' . RF_NODES_KEY . ' IN ' . clb_join(array_keys($list), TRUE); $places = $wpdb->get_results($query, ARRAY_A); $places = clb_rekey($places, RF_NODES_KEY); foreach ($list as $station => $plats) { if (clb_count($plats) > 1) { //check each platform has the name of its station in its name field $name = clb_val('', $places, $station, RF_NODES_NAME); $query = 'UPDATE ' . RF_NODES_FROM . ' SET ' . RF_NODES_NAME . '=' . clb_escape($name) . ' WHERE ' . RF_LINKS_KEY . '='; if ($name) { foreach ($plats as $rec) { if ($rec[RF_NODES_NAME] != $name) { $wpdb->query($query . clb_escape($rec[RF_NODES_KEY])); } } } //permute platform connections $count = count($plats); for ($x = 0; $x < $count - 1; $x++) { for ($y = $x + 1; $y < $count; $y++) { pbuild_make_link($p2p_links, array($plats[$x], $plats[$y])); } } } } } qlog(__LINE__, $count); }
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; }
function pline_splice($db, $route, $pnum1 = FALSE, $pnum2 = FALSE, $stopsegs = FALSE) { //$route is an array of link pnums if (!is_array($route) || count($route) == 0) { qlog(__LINE__, __FUNCTION__, 'bad route', $route, $pnum1, $pnum2); //exit(); return ''; } $raw = is_array($stopsegs); //TRUE => segment end points are points not pnums if ($raw) { //in the raw case we have to trim the ends so prepare information we will need $key1 = pline_arr_match($stopsegs['stoppnums'], $pnum1, $stopsegs['segpnums'], reset($route)); $key1 = reset($key1); //should only be one match but still need to turn array into single value $cut1 = clb_val(FALSE, $stopsegs['data'], $key1); $key2 = pline_arr_match($stopsegs['stoppnums'], $pnum2, $stopsegs['segpnums'], end($route)); $key2 = reset($key2); //should only be one match but still need to turn array into single value $cut2 = clb_val(FALSE, $stopsegs['data'], $key2); } //load all segs in one hit $query = 'SELECT ' . RF_LINKS_SELECT . ',' . RF_LINKS_POINTS . ' FROM ' . RF_LINKS_FROM . ' WHERE ' . RF_LINKS_KEY . ' IN ' . clb_join($route, TRUE); $segs = $db->get_results($query, ARRAY_A); $segs = clb_rekey($segs, 'pnum'); $last_end1 = $last_end2 = $flip = FALSE; $flipfirst = FALSE; //if the first seg is flipped this holds the nubmer of points so we can work out how many to trim $points = array(); $count = 0; $max = count($route); foreach ($route as $seg_pnum) { $count++; //so will be 1 on first loop, 2 on second etc $rec = clb_val(FALSE, $segs, $seg_pnum); $aoe_text = clb_val('', $rec, RF_LINKS_POINTS); $aoe_data = pline_pts_arr($aoe_text); if (!is_array($aoe_data)) { qlog(__FUNCTION__, __LINE__, 'failed to get points list for route segment', $seg_pnum, $count, $aoe_data); continue; } $this_end1 = clb_val('', $rec, RF_LINKS_END1); $this_end2 = clb_val('', $rec, RF_LINKS_END2); $flip = FALSE; if ($max == 1) { if ($raw) { //if the position within the segment of the second point is before the first, then we need to reverse $flip = isset($cut1['pos']) && isset($cut2['pos']) && $cut2['pos'] < $cut1['pos']; } else { if ($pnum1) { $flip = $this_end2 == $pnum1; //on a stop to stop link, need to reverse if start pnum is at tail end of link } } if ($flip) { $flipfirst = count($aoe_data); } } else { if ($count <= 1 || $this_end1 == $last_end2) { //no action } else { if ($count == 2 && $this_end1 == $last_end1) { $points = array_reverse($points, TRUE); //TRUE=preserve_keys $flipfirst = count($points); } else { if ($count == 2 && $this_end2 == $last_end1) { $points = array_reverse($points, TRUE); //TRUE=preserve_keys $flipfirst = count($points); $flip = TRUE; } else { $flip = $this_end2 == $last_end2; } } } } if ($flip) { $aoe_data = array_reverse($aoe_data, TRUE); //TRUE=preserve_keys list($this_end1, $this_end2) = array($this_end2, $this_end1); //reverse end points too } //check if first point on this seg is same as last point on previous seg and if so remove it if (count($points) && count($aoe_data)) { $last = end($points); $first = reset($aoe_data); //do an array_pop on the existing points rather than removing the duplicate from $aoe_data since that alters the cutting math on raw segs if (round($last[0], 5) == round($first[0], 5) && round($last[1], 5) == round($first[1], 5)) { array_pop($points); } } $points = array_merge($points, $aoe_data); //$seg_idx[$this_end2] = count($points) - 1; $last_end1 = $this_end1; $last_end2 = $this_end2; } if ($raw) { //if the first seg was reversed then we want to keep the number of points on the other side $pos = is_int($flipfirst) ? $flipfirst - $cut1['pos'] : $cut1['pos']; //cut off end points and insert the stops own point. If this is duplicate it will be cleaned by polyline later array_splice($points, 0, $pos, array(array($cut1['lat'], $cut1['lng'], 0))); //if the last seg was reversed then we want to keep the number of points on the other side $pos = $flip ? $cut2['pos'] : count($aoe_data) - $cut2['pos']; //aoe_data still around after last itteration of loop //cut off end points and insert the stops own point. If this is duplicate it will be cleaned by polyline later array_splice($points, -$pos, $pos, array(array($cut2['lat'], $cut2['lng'], 0))); //qlog(__FUNCTION__, __LINE__, $pnum1, $cut1, $pnum2, $cut2); } //return array($points, $seg_idx); //not using this because the positions may change when making a polyline and makes returning the points list more cumbersome return $points; }