- if ($bSuggestion) $sSuggestion = join(', ',$aSuggestion);
-/*
- // Try and calculate GB postcodes we might be missing
- foreach($aTokens as $sToken)
- {
- if (!isset($aValidTokens[$sToken]) && !isset($aValidTokens[' '.$sToken]) && preg_match('/^([A-Z][A-Z]?[0-9][0-9A-Z]? ?[0-9])([A-Z][A-Z])$/', strtoupper(trim($sToken)), $aData))
- {
- if (substr($aData[1],-2,1) != ' ')
- {
- $aData[0] = substr($aData[0],0,strlen($aData[1]-1)).' '.substr($aData[0],strlen($aData[1]-1));
- $aData[1] = substr($aData[1],0,-1).' '.substr($aData[1],-1,1);
- }
- $aGBPostcodeLocation = gbPostcodeCalculate($aData[0], $aData[1], $aData[2], $oDB);
- if ($aGBPostcodeLocation)
- {
- $aValidTokens[$sToken] = $aGBPostcodeLocation;
- }
- }
- }
-*/
- // Any words that have failed completely?
- // TODO: suggestions
-
- // Start the search process
- $aResultPlaceIDs = array();
-
- /*
- Calculate all searches using aValidTokens i.e.
-
- 'Wodsworth Road, Sheffield' =>
-
- Phrase Wordset
- 0 0 (wodsworth road)
- 0 1 (wodsworth)(road)
- 1 0 (sheffield)
-
- Score how good the search is so they can be ordered
- */
-
- foreach($aPhrases as $iPhrase => $sPhrase)
- {
- $aNewPhraseSearches = array();
-
- foreach($aPhrases[$iPhrase]['wordsets'] as $iWordset => $aWordset)
- {
- $aWordsetSearches = $aSearches;
-
- // Add all words from this wordset
- foreach($aWordset as $sToken)
- {
-//echo "<br><b>$sToken</b>";
- $aNewWordsetSearches = array();
-
- foreach($aWordsetSearches as $aCurrentSearch)
- {
-//echo "<i>";
-//var_dump($aCurrentSearch);
-//echo "</i>";
-
- // If the token is valid
- if (isset($aValidTokens[' '.$sToken]))
- {
- foreach($aValidTokens[' '.$sToken] as $aSearchTerm)
- {
- $aSearch = $aCurrentSearch;
- $aSearch['iSearchRank']++;
- if ($aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
- {
- if ($aSearch['sCountryCode'] === false)
- {
- $aSearch['sCountryCode'] = strtolower($aSearchTerm['country_code']);
- // Country is almost always at the end of the string - increase score for finding it anywhere else (opimisation)
- if ($iWordset+1 != sizeof($aPhrases[$iPhrase]['wordsets']) || $iPhrase+1 != sizeof($aPhrases)) $aSearch['iSearchRank'] += 5;
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
- }
- elseif ($aSearchTerm['lat'] !== '' && $aSearchTerm['lat'] !== null)
- {
- if ($aSearch['fLat'] === '')
- {
- $aSearch['fLat'] = $aSearchTerm['lat'];
- $aSearch['fLon'] = $aSearchTerm['lon'];
- $aSearch['fRadius'] = $aSearchTerm['radius'];
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
- }
- elseif ($aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
- {
- if ($aSearch['sHouseNumber'] === '')
- {
- $aSearch['sHouseNumber'] = $sToken;
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-/*
- // Fall back to not searching for this item (better than nothing)
- $aSearch = $aCurrentSearch;
- $aSearch['iSearchRank'] += 1;
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-*/
- }
- }
- elseif ($aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
- {
- if ($aSearch['sClass'] === '')
- {
- $aSearch['sOperator'] = $aSearchTerm['operator'];
- $aSearch['sClass'] = $aSearchTerm['class'];
- $aSearch['sType'] = $aSearchTerm['type'];
- if (sizeof($aSearch['aName'])) $aSearch['sOperator'] = 'name';
- else $aSearch['sOperator'] = 'near'; // near = in for the moment
-
- // Do we have a shortcut id?
- if ($aSearch['sOperator'] == 'name')
- {
- $sSQL = "select get_tagpair('".$aSearch['sClass']."', '".$aSearch['sType']."')";
- if ($iAmenityID = $oDB->getOne($sSQL))
- {
- $aValidTokens[$aSearch['sClass'].':'.$aSearch['sType']] = array('word_id' => $iAmenityID);
- $aSearch['aName'][$iAmenityID] = $iAmenityID;
- $aSearch['sClass'] = '';
- $aSearch['sType'] = '';
- }
- }
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
- }
- else
- {
- if (sizeof($aSearch['aName']))
- {
- if (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4)
- {
- $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
- }
- else
- {
- $aSearch['iSearchRank'] += 1000; // skip;
- }
- }
- else
- {
- $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
-// $aSearch['iNamePhrase'] = $iPhrase;
- }
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
- }
- }
- if (isset($aValidTokens[$sToken]) && strlen($sToken) >= 4)
- {
- // Allow searching for a word - but at extra cost
- foreach($aValidTokens[$sToken] as $aSearchTerm)
- {
-//var_Dump('<hr>',$aSearch['aName']);
-
- if (sizeof($aCurrentSearch['aName']))
- {
- $aSearch = $aCurrentSearch;
- $aSearch['iSearchRank'] += 1;
- $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
-
- if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
- {
- $aSearch = $aCurrentSearch;
- $aSearch['iSearchRank'] += 4;
- $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
- $aSearch['iNamePhrase'] = $iPhrase;
- if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
- }
- }
- }
- else
- {
- // Allow skipping a word - but at EXTREAM cost
- //$aSearch = $aCurrentSearch;
- //$aSearch['iSearchRank']+=100;
- //$aNewWordsetSearches[] = $aSearch;
- }
- }
- // Sort and cut
- usort($aNewWordsetSearches, 'bySearchRank');
- $aWordsetSearches = array_slice($aNewWordsetSearches, 0, 50);
- }
-// var_Dump('<hr>',sizeof($aWordsetSearches)); exit;
-
- $aNewPhraseSearches = array_merge($aNewPhraseSearches, $aNewWordsetSearches);
- usort($aNewPhraseSearches, 'bySearchRank');
- $aNewPhraseSearches = array_slice($aNewPhraseSearches, 0, 50);
- }
-
- // Re-group the searches by their score, junk anything over 20 as just not worth trying
- $aGroupedSearches = array();
- foreach($aNewPhraseSearches as $aSearch)
- {
- if ($aSearch['iSearchRank'] < $iMaxRank)
- {
- if (!isset($aGroupedSearches[$aSearch['iSearchRank']])) $aGroupedSearches[$aSearch['iSearchRank']] = array();
- $aGroupedSearches[$aSearch['iSearchRank']][] = $aSearch;
- }
- }
- ksort($aGroupedSearches);
-
- $iSearchCount = 0;
- $aSearches = array();
- foreach($aGroupedSearches as $iScore => $aNewSearches)
- {
- $iSearchCount += sizeof($aNewSearches);
- $aSearches = array_merge($aSearches, $aNewSearches);
- if ($iSearchCount > 50) break;
- }
-
-// if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
-
- }
- }
- else
- {
- // Re-group the searches by their score, junk anything over 20 as just not worth trying
- $aGroupedSearches = array();
- foreach($aSearches as $aSearch)
- {
- if ($aSearch['iSearchRank'] < $iMaxRank)
- {
- if (!isset($aGroupedSearches[$aSearch['iSearchRank']])) $aGroupedSearches[$aSearch['iSearchRank']] = array();
- $aGroupedSearches[$aSearch['iSearchRank']][] = $aSearch;
- }
- }
- ksort($aGroupedSearches);
- }
-
- if (CONST_Debug) var_Dump($aGroupedSearches);
-
- if ($bReverseInPlan && false)
- {
- foreach($aGroupedSearches as $iGroup => $aSearches)
- {
- foreach($aSearches as $iSearch => $aSearch)
- {
- if (sizeof($aSearch['aAddress']))
- {
- $aReverseSearch = $aSearch;
- $iReverseItem = array_pop($aSearch['aAddress']);
- $aReverseSearch['aName'][$iReverseItem] = $iReverseItem;
- $aGroupedSearches[$iGroup][] = $aReverseSearch;
- }
- }
- }
- }
-
- // Filter out duplicate searches
- $aSearchHash = array();
- foreach($aGroupedSearches as $iGroup => $aSearches)
- {
- foreach($aSearches as $iSearch => $aSearch)
- {
- $sHash = serialize($aSearch);
- if (isset($aSearchHash[$sHash]))
- {
- unset($aGroupedSearches[$iGroup][$iSearch]);
- if (sizeof($aGroupedSearches[$iGroup]) == 0) unset($aGroupedSearches[$iGroup]);
- }
- else
- {
- $aSearchHash[$sHash] = 1;
- }
- }
- }
-
- if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
-
- $iGroupLoop = 0;
- $iQueryLoop = 0;
- foreach($aGroupedSearches as $iGroupedRank => $aSearches)
- {
- $iGroupLoop++;
- foreach($aSearches as $aSearch)
- {
- $iQueryLoop++;
-
- // Must have a location term
- if (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && !$aSearch['fLon'])
- {
- if (!$bBoundingBoxSearch && !$aSearch['fLon']) continue;
- if (!$aSearch['sClass']) continue;
- if (CONST_Debug) var_dump('<hr>',$aSearch);
- if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);
-
- $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
- if ($oDB->getOne($sSQL))
- {
- $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType'];
- $sSQL .= " where st_contains($sViewboxSmallSQL, centroid)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
-
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType'];
- $sSQL .= " where st_contains($sViewboxLargeSQL, centroid)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- else
- {
- $sSQL = "select place_id from placex where class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
- $sSQL .= " and st_contains($sViewboxSmallSQL, centroid)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- else
- {
- if (CONST_Debug) var_dump('<hr>',$aSearch);
- if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);
- $aPlaceIDs = array();
-
- // First we need a position, either aName or fLat or both
- $aTerms = array();
- $aOrder = array();
- if (sizeof($aSearch['aName'])) $aTerms[] = "name_vector @> ARRAY[".join($aSearch['aName'],",")."]";
- if (sizeof($aSearch['aAddress']) && $aSearch['aName'] != $aSearch['aAddress']) $aTerms[] = "nameaddress_vector @> ARRAY[".join($aSearch['aAddress'],",")."]";
- if ($aSearch['sCountryCode']) $aTerms[] = "country_code = '".pg_escape_string($aSearch['sCountryCode'])."'";
- if ($aSearch['sHouseNumber']) $aTerms[] = "address_rank in (26,27)";
- if ($aSearch['fLon'] && $aSearch['fLat'])
- {
- $aTerms[] = "ST_DWithin(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326), ".$aSearch['fRadius'].")";
- $aOrder[] = "ST_Distance(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326)) ASC";
- }
- if (sizeof($aExcludePlaceIDs))
- {
- $aTerms[] = "place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($bBoundingBoxSearch) $aTerms[] = "centroid && $sViewboxSmallSQL";
- if ($sNearPointSQL) $aOrder[] = "ST_Distance($sNearPointSQL, centroid) asc";
- if ($sViewboxSmallSQL) $aOrder[] = "ST_Contains($sViewboxSmallSQL, centroid) desc";
- if ($sViewboxLargeSQL) $aOrder[] = "ST_Contains($sViewboxLargeSQL, centroid) desc";
- $aOrder[] = "search_rank ASC";
-
- if (sizeof($aTerms))
- {
- $sSQL = "select place_id";
- if ($sViewboxSmallSQL) $sSQL .= ",ST_Contains($sViewboxSmallSQL, centroid) as in_small";
- else $sSQL .= ",false as in_small";
- if ($sViewboxLargeSQL) $sSQL .= ",ST_Contains($sViewboxLargeSQL, centroid) as in_large";
- else $sSQL .= ",false as in_large";
- $sSQL .= " from search_name";
- $sSQL .= " where ".join(' and ',$aTerms);
- $sSQL .= " order by ".join(', ',$aOrder);
- if ($aSearch['sHouseNumber'])
- $sSQL .= " limit 50";
- elseif (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && $aSearch['sClass'])
- $sSQL .= " limit 1";
- else
- $sSQL .= " limit ".$iLimit;
-
- if (CONST_Debug) var_dump($sSQL);
- $aViewBoxPlaceIDs = $oDB->getAll($sSQL);
- if (PEAR::IsError($aViewBoxPlaceIDs))
- {
- var_dump($sSQL, $aViewBoxPlaceIDs);
- exit;
- }
-
- // Did we have an viewbox matches?
- $aPlaceIDs = array();
- $bViewBoxMatch = false;
- foreach($aViewBoxPlaceIDs as $aViewBoxRow)
- {
- if ($bViewBoxMatch == 1 && $aViewBoxRow['in_small'] == 'f') break;
- if ($bViewBoxMatch == 2 && $aViewBoxRow['in_large'] == 'f') break;
- if ($aViewBoxRow['in_small'] == 't') $bViewBoxMatch = 1;
- else if ($aViewBoxRow['in_large'] == 't') $bViewBoxMatch = 2;
- $aPlaceIDs[] = $aViewBoxRow['place_id'];
- }
- }
-
- if ($aSearch['sHouseNumber'] && sizeof($aPlaceIDs))
- {
- $aRoadPlaceIDs = $aPlaceIDs;
- $sPlaceIDs = join(',',$aPlaceIDs);
-
- // Now they are indexed look for a house attached to a street we found
- $sHouseNumberRegex = '\\\\m'.str_replace(' ','[-, ]',$aSearch['sHouseNumber']).'\\\\M';
- $sSQL = "select place_id from placex where parent_place_id in (".$sPlaceIDs.") and housenumber ~* E'".$sHouseNumberRegex."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
-
- // If not try the aux fallback table
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from location_property_aux where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
-// $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
-
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from location_property_tiger where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
-// $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
-
- // Fallback to the road
- if (!sizeof($aPlaceIDs))
- {
- $aPlaceIDs = $aRoadPlaceIDs;
- }
-
- }
-
- if ($aSearch['sClass'] && sizeof($aPlaceIDs))
- {
- $sPlaceIDs = join(',',$aPlaceIDs);
-
- if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'name')
- {
- // If they were searching for a named class (i.e. 'Kings Head pub') then we might have an extra match
- $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
- $sSQL .= " order by rank_search asc limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
-
- if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'near') // & in
- {
- $sSQL = "select rank_search from placex where place_id in ($sPlaceIDs) order by rank_search asc limit 1";
- if (CONST_Debug) var_dump($sSQL);
- $iMaxRank = ((int)$oDB->getOne($sSQL)) + 5;
-
- $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- $sPlaceIDs = join(',',$aPlaceIDs);
-
- $fRange = 0.01;
- $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
- if ($oDB->getOne($sSQL))
- {
- // More efficient - can make the range bigger
- $fRange = 0.05;
- $sSQL = "select l.place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." as l";
- $sSQL .= ",placex as f where ";
- $sSQL .= "f.place_id in ($sPlaceIDs) and ST_DWithin(l.centroid, st_centroid(f.geometry), $fRange) ";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($sNearPointSQL) $sSQL .= " order by ST_Distance($sNearPointSQL, l.centroid) ASC";
- else $sSQL .= " order by ST_Distance(l.centroid, f.geometry) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- else
- {
- if (isset($aSearch['fRadius']) && $aSearch['fRadius']) $fRange = $aSearch['fRadius'];
- $sSQL = "select l.place_id from placex as l,placex as f where ";
- $sSQL .= "f.place_id in ($sPlaceIDs) and ST_DWithin(l.geometry, st_centroid(f.geometry), $fRange) ";
- $sSQL .= "and l.class='".$aSearch['sClass']."' and l.type='".$aSearch['sType']."' ";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($sNearPointSQL) $sSQL .= " order by ST_Distance($sNearPointSQL, l.geometry) ASC";
- else $sSQL .= " order by ST_Distance(l.geometry, f.geometry) asc, l.rank_search ASC";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- }
-
- }
-
- if (PEAR::IsError($aPlaceIDs))
- {
- var_dump($sSQL, $aPlaceIDs);
- exit;
- }