X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/ca5305661e4517f2aebfebfc61bfae920e615339..c0d9ba7e0cca0e1ec0ebd6764c5d5c871e5e443b:/website/search.php diff --git a/website/search.php b/website/search.php index a23ed9d7..1d544df4 100755 --- a/website/search.php +++ b/website/search.php @@ -36,7 +36,7 @@ // Show address breakdown $bShowAddressDetails = isset($_GET['addressdetails']) && $_GET['addressdetails']; - // Prefered language + // Preferred language $aLangPrefOrder = getPreferredLanguages(); if (isset($aLangPrefOrder['name:de'])) $bReverseInPlan = true; if (isset($aLangPrefOrder['name:ru'])) $bReverseInPlan = true; @@ -53,7 +53,7 @@ } } - // Only certain ranks of feature + // Only certain ranks of feature if (isset($_GET['featureType']) && !isset($_GET['featuretype'])) $_GET['featuretype'] = $_GET['featureType']; if (isset($_GET['featuretype'])) @@ -96,11 +96,44 @@ { $sQuery = substr($_SERVER['PATH_INFO'], 1); - // reverse order of '/' seperated string + // reverse order of '/' separated string $aPhrases = explode('/', $sQuery); $aPhrases = array_reverse($aPhrases); $sQuery = join(', ',$aPhrases); } + + function structuredAddressElement(&$aStructuredQuery, &$iMinAddressRank, &$iMaxAddressRank, $aParams, $sKey, $iNewMinAddressRank, $iNewMaxAddressRank) + { + if (!isset($_GET[$sKey])) return false; + $sValue = trim($_GET[$sKey]); + if (!$sValue) return false; + $aStructuredQuery[$sKey] = $sValue; + if ($iMinAddressRank == 0 && $iMaxAddressRank == 30) { + $iMinAddressRank = $iNewMinAddressRank; + $iMaxAddressRank = $iNewMaxAddressRank; + } + return true; + } + + // Structured query? + $aStructuredOptions = array( + array('amenity', 26, 30), + array('street', 26, 30), + array('city', 14, 24), + array('county', 9, 13), + array('state', 8, 8), + array('country', 4, 4), + array('postalcode', 16, 25), + ); + $aStructuredQuery = array(); + foreach($aStructuredOptions as $aStructuredOption) + { + loadStructuredAddressElement($aStructuredQuery, $iMinAddressRank, $iMaxAddressRank, $_GET, $aStructuredOption[0], $aStructuredOption[1], $aStructuredOption[2]); + } + if (sizeof($aStructuredQuery) > 0) { + $sQuery = join(', ', $aStructuredQuery); + } + if ($sQuery) { $hLog = logStart($oDB, 'search', $sQuery, $aLangPrefOrder); @@ -133,6 +166,8 @@ $aCoOrdinates[1] += $fWidth; $aCoOrdinates[3] -= $fWidth; $sViewboxLargeSQL = "ST_SetSRID(ST_MakeBox2D(ST_Point(".(float)$aCoOrdinates[0].",".(float)$aCoOrdinates[1]."),ST_Point(".(float)$aCoOrdinates[2].",".(float)$aCoOrdinates[3].")),4326)"; + } else { + $bBoundingBoxSearch = false; } if (isset($_GET['route']) && $_GET['route'] && isset($_GET['routewidth']) && $_GET['routewidth']) { @@ -210,7 +245,7 @@ } } - if ($sQuery) + if ($sQuery || $aStructuredQuery) { // Start with a blank search $aSearches = array( @@ -238,12 +273,17 @@ preg_match_all('/\\[([a-zA-Z]*)\\]/', $sQuery, $aSpecialTermsRaw, PREG_SET_ORDER); $aSpecialTerms = array(); + if (isset($aStructuredQuery['amenity']) && $aStructuredQuery['amenity']) + { + $aSpecialTermsRaw[] = array('['.$aStructuredQuery['amenity'].']', $aStructuredQuery['amenity']); + unset($aStructuredQuery['amenity']); + } foreach($aSpecialTermsRaw as $aSpecialTerm) { $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery); $sToken = $oDB->getOne("select make_standard_name('".$aSpecialTerm[1]."') as string"); $sSQL = 'select * from (select word_id,word_token, word, class, type, location, country_code, operator'; - $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class not in (\'place\',\'highway\')) or country_code is not null'; + $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class not in (\'place\')) or country_code is not null'; if (CONST_Debug) var_Dump($sSQL); $aSearchWords = $oDB->getAll($sSQL); $aNewSearches = array(); @@ -272,7 +312,17 @@ // Split query into phrases // Commas are used to reduce the search space by indicating where phrases split - $aPhrases = explode(',',$sQuery); + if (sizeof($aStructuredQuery) > 0) + { + $aPhrases = $aStructuredQuery; + $bStructuredPhrases = true; + } + else + { + $aPhrases = explode(',',$sQuery); + $bStructuredPhrases = false; + } + // Convert each phrase to standard form // Create a list of standard words @@ -302,6 +352,7 @@ } // reindex phrases - we make assumptions later on + $aPhraseTypes = array_keys($aPhrases); $aPhrases = array_values($aPhrases); if (sizeof($aTokens)) @@ -310,7 +361,7 @@ // Check which tokens we have, get the ID numbers $sSQL = 'select word_id,word_token, word, class, type, location, country_code, operator'; $sSQL .= ' from word where word_token in ('.join(',',array_map("getDBQuoted",$aTokens)).')'; - $sSQL .= ' and (class is null or class not in (\'highway\'))'; + $sSQL .= ' and search_name_count < '.CONST_Max_Word_Frequency; // $sSQL .= ' group by word_token, word, class, type, location, country_code'; if (CONST_Debug) var_Dump($sSQL); @@ -406,28 +457,30 @@ 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(); + if ($bStructuredPhrases) $sPhraseType = $aPhraseTypes[$iPhrase]; + else $sPhraseType = ''; - foreach($aPhrases[$iPhrase]['wordsets'] as $iWordset => $aWordset) + foreach($aPhrases[$iPhrase]['wordsets'] as $aWordset) { $aWordsetSearches = $aSearches; // Add all words from this wordset - foreach($aWordset as $sToken) + foreach($aWordset as $iToken => $sToken) { //echo "
$sToken"; $aNewWordsetSearches = array(); - + foreach($aWordsetSearches as $aCurrentSearch) { //echo ""; @@ -441,13 +494,13 @@ { $aSearch = $aCurrentSearch; $aSearch['iSearchRank']++; - if ($aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0') + if (($sPhraseType == '' || $sPhraseType == 'country') && $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; + // Country is almost always at the end of the string - increase score for finding it anywhere else (optimisation) + if ($iToken+1 != sizeof($aWordset) || $iPhrase+1 != sizeof($aPhrases)) $aSearch['iSearchRank'] += 5; if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch; } } @@ -461,7 +514,7 @@ if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch; } } - elseif ($aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house') + elseif (($sPhraseType == '' || $sPhraseType == 'street') && $aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house') { if ($aSearch['sHouseNumber'] === '') { @@ -475,7 +528,7 @@ */ } } - elseif ($aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null) + elseif ($sPhraseType == '' && $aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null) { if ($aSearch['sClass'] === '') { @@ -504,7 +557,7 @@ { if (sizeof($aSearch['aName'])) { - if (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false) + if (($sPhraseType != 'street' && $sPhraseType != 'country') && (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false)) { $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id']; } @@ -529,25 +582,23 @@ { if (isset($aSearchTerm['word_id']) && $aSearchTerm['word_id']) { -//var_Dump('
',$aSearch['aName']); - - if (sizeof($aCurrentSearch['aName']) && strlen($sToken) >= 4) - { - $aSearch = $aCurrentSearch; - $aSearch['iSearchRank'] += 1; - $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id']; - if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch; - } + if (($sPhraseType != 'street') && sizeof($aCurrentSearch['aName']) && strlen($sToken) >= 4) + { + $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'] += 2; - if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2; - $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id']; - $aSearch['iNamePhrase'] = $iPhrase; - if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch; - } + if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase) + { + $aSearch = $aCurrentSearch; + $aSearch['iSearchRank'] += 2; + if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2; + $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id']; + $aSearch['iNamePhrase'] = $iPhrase; + if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch; + } } } } @@ -703,22 +754,22 @@ $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 = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct"; if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)"; - $sSQL .= " where st_contains($sViewboxSmallSQL, centroid)"; + $sSQL .= " where st_contains($sViewboxSmallSQL, ct.centroid) and linked_place_id is null"; if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)"; - if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc"; + if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.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 = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct"; if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)"; - $sSQL .= " where st_contains($sViewboxLargeSQL, centroid)"; + $sSQL .= " where st_contains($sViewboxLargeSQL, ct.centroid) and linked_place_id is null"; if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)"; - if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc"; + if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.centroid) asc"; $sSQL .= " limit $iLimit"; if (CONST_Debug) var_dump($sSQL); $aPlaceIDs = $oDB->getCol($sSQL); @@ -727,7 +778,7 @@ else { $sSQL = "select place_id from placex where class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'"; - $sSQL .= " and st_contains($sViewboxSmallSQL, centroid)"; + $sSQL .= " and st_contains($sViewboxSmallSQL, geometry) and linked_place_id is null"; if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)"; if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc"; $sSQL .= " limit $iLimit"; @@ -867,6 +918,7 @@ { // 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 = " and linked_place_id is null"; if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)"; $sSQL .= " order by rank_search asc limit $iLimit"; if (CONST_Debug) var_dump($sSQL); @@ -1168,6 +1220,10 @@ { preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER); } + elseif (preg_match('#MULTIPOLYGON\\(\\(\\(([- 0-9.,]+)#',$aPointPolygon['outlinestring'],$aMatch)) + { + preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER); + } elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#',$aPointPolygon['outlinestring'],$aMatch)) { $fRadius = 0.01; @@ -1266,7 +1322,7 @@ { if (stripos($sAddress, $sWord)!==false) $iCountWords++; } - $aResult['importance'] = $aResult['importance'] + $iCountWords; + $aResult['importance'] = $aResult['importance'] + ($iCountWords*0.1); // 0.1 is a completely arbitrary number but something in the range 0.1 to 0.5 would seem right //if (CONST_Debug) var_dump($aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']); /*