protected $iLimit = 20;
protected $iFinalLimit = 10;
protected $iOffset = 0;
+ protected $bFallback = false;
protected $aCountryCodes = false;
protected $aNearPoint = false;
$this->iOffset = $iOffset;
}
+ function setFallback($bFallback = true)
+ {
+ $this->bFallback = (bool)$bFallback;
+ }
+
function setExcludedPlaceIDs($a)
{
// TODO: force to int
return $this->sQuery;
}
+
+ function loadParamArray($aParams)
+ {
+ if (isset($aParams['addressdetails'])) $this->bIncludeAddressDetails = (bool)$aParams['addressdetails'];
+ if (isset($aParams['bounded'])) $this->bBoundedSearch = (bool)$aParams['bounded'];
+ if (isset($aParams['dedupe'])) $this->bDeDupe = (bool)$aParams['dedupe'];
+
+ if (isset($aParams['limit'])) $this->setLimit((int)$aParams['limit']);
+ if (isset($aParams['offset'])) $this->iOffset = (int)$aParams['offset'];
+
+ if (isset($aParams['fallback'])) $this->bFallback = (bool)$aParams['fallback'];
+
+ // List of excluded Place IDs - used for more acurate pageing
+ if (isset($aParams['exclude_place_ids']) && $aParams['exclude_place_ids'])
+ {
+ foreach(explode(',',$aParams['exclude_place_ids']) as $iExcludedPlaceID)
+ {
+ $iExcludedPlaceID = (int)$iExcludedPlaceID;
+ if ($iExcludedPlaceID) $aExcludePlaceIDs[$iExcludedPlaceID] = $iExcludedPlaceID;
+ }
+ $this->aExcludePlaceIDs = $aExcludePlaceIDs;
+ }
+
+ // Only certain ranks of feature
+ if (isset($aParams['featureType'])) $this->setFeatureType($aParams['featureType']);
+ if (isset($aParams['featuretype'])) $this->setFeatureType($aParams['featuretype']);
+
+ // Country code list
+ if (isset($aParams['countrycodes']))
+ {
+ $aCountryCodes = array();
+ foreach(explode(',',$aParams['countrycodes']) as $sCountryCode)
+ {
+ if (preg_match('/^[a-zA-Z][a-zA-Z]$/', $sCountryCode))
+ {
+ $aCountryCodes[] = strtolower($sCountryCode);
+ }
+ }
+ $this->aCountryCodes = $aCountryCodes;
+ }
+
+ if (isset($aParams['viewboxlbrt']) && $aParams['viewboxlbrt'])
+ {
+ $aCoOrdinatesLBRT = explode(',',$aParams['viewboxlbrt']);
+ $this->setViewBox($aCoOrdinatesLBRT[0], $aCoOrdinatesLBRT[1], $aCoOrdinatesLBRT[2], $aCoOrdinatesLBRT[3]);
+ }
+ else if (isset($aParams['viewbox']) && $aParams['viewbox'])
+ {
+ $aCoOrdinatesLTRB = explode(',',$aParams['viewbox']);
+ $this->setViewBox($aCoOrdinatesLTRB[0], $aCoOrdinatesLTRB[3], $aCoOrdinatesLTRB[2], $aCoOrdinatesLTRB[1]);
+ }
+
+ if (isset($aParams['route']) && $aParams['route'] && isset($aParams['routewidth']) && $aParams['routewidth'])
+ {
+ $aPoints = explode(',',$aParams['route']);
+ if (sizeof($aPoints) % 2 != 0)
+ {
+ userError("Uneven number of points");
+ exit;
+ }
+ $fPrevCoord = false;
+ $aRoute = array();
+ foreach($aPoints as $i => $fPoint)
+ {
+ if ($i%2)
+ {
+ $aRoute[] = array((float)$fPoint, $fPrevCoord);
+ }
+ else
+ {
+ $fPrevCoord = (float)$fPoint;
+ }
+ }
+ $this->aRoutePoints = $aRoute;
+ }
+ }
+
+ function setQueryFromParams($aParams)
+ {
+ // Search query
+ $sQuery = (isset($aParams['q'])?trim($aParams['q']):'');
+ if (!$sQuery)
+ {
+ $this->setStructuredQuery(@$aParams['amenity'], @$aParams['street'], @$aParams['city'], @$aParams['county'], @$aParams['state'], @$aParams['country'], @$aParams['postalcode']);
+ $this->setReverseInPlan(false);
+ }
+ else
+ {
+ $this->setQuery($sQuery);
+ }
+ }
+
function loadStructuredAddressElement($sValue, $sKey, $iNewMinAddressRank, $iNewMaxAddressRank, $aItemListValues)
{
$sValue = trim($sValue);
{
$this->sQuery = false;
+ // Reset
+ $this->iMinAddressRank = 0;
+ $this->iMaxAddressRank = 30;
+ $this->aAddressRankList = array();
+
$this->aStructuredQuery = array();
$this->sAllowedTypesSQLList = '';
$sAllowedTypesSQLList = '(\'place\',\'boundary\')';
}
}
+ }
+
+ function fallbackStructuredQuery()
+ {
+ if (!$this->aStructuredQuery) return false;
+
+ $aParams = $this->aStructuredQuery;
+
+ if (sizeof($aParams) == 1) return false;
+ $aOrderToFallback = array('postalcode', 'street', 'city', 'county', 'state');
+
+ foreach($aOrderToFallback as $sType)
+ {
+ if (isset($aParams[$sType]))
+ {
+ unset($aParams[$sType]);
+ $this->setStructuredQuery(@$aParams['amenity'], @$aParams['street'], @$aParams['city'], @$aParams['county'], @$aParams['state'], @$aParams['country'], @$aParams['postalcode']);
+ return true;
+ }
+ }
+
+ return false;
}
function getDetails($aPlaceIDs)
$sCountryCodesSQL = join(',', array_map('addQuotes', $this->aCountryCodes));
}
- // Hack to make it handle "new york, ny" (and variants) correctly
- //$sQuery = str_ireplace(array('New York, ny','new york, new york', 'New York ny','new york new york'), 'new york city, ny', $this->sQuery);
$sQuery = $this->sQuery;
// Conflicts between US state abreviations and various words for 'the' in different languages
if (isset($this->aLangPrefOrder['name:en']))
{
- $sQuery = preg_replace('/,\s*il\s*(,|$)/',', illinois\1', $sQuery);
- $sQuery = preg_replace('/,\s*al\s*(,|$)/',', alabama\1', $sQuery);
- $sQuery = preg_replace('/,\s*la\s*(,|$)/',', louisiana\1', $sQuery);
+ $sQuery = preg_replace('/(^|,)\s*il\s*(,|$)/','\1illinois\2', $sQuery);
+ $sQuery = preg_replace('/(^|,)\s*al\s*(,|$)/','\1alabama\2', $sQuery);
+ $sQuery = preg_replace('/(^|,)\s*la\s*(,|$)/','\1louisiana\2', $sQuery);
}
// View Box SQL
- $sViewboxCentreSQL;
+ $sViewboxCentreSQL = false;
$bBoundingBoxSearch = false;
if ($this->aViewBox)
{
if ($this->aRoutePoints)
{
$sViewboxCentreSQL = "ST_SetSRID('LINESTRING(";
- $bFirst = false;
- foreach($this->aRouteaPoints as $aPoint)
+ $bFirst = true;
+ foreach($this->aRoutePoints as $aPoint)
{
if (!$bFirst) $sViewboxCentreSQL .= ",";
$sViewboxCentreSQL .= $aPoint[1].' '.$aPoint[0];
+ $bFirst = false;
}
$sViewboxCentreSQL .= ")'::geometry,4326)";
if ($aSearch['sHouseNumber'] === '')
{
$aSearch['sHouseNumber'] = $sToken;
+ // sanity check: if the housenumber is not mainly made
+ // up of numbers, add a penalty
+ if (preg_match_all("/[^0-9]/", $sToken, $aMatches) > 2) $aSearch['iSearchRank']++;
if ($aSearch['iSearchRank'] < $this->iMaxRank) $aNewWordsetSearches[] = $aSearch;
/*
// Fall back to not searching for this item (better than nothing)
{
if (isset($aSearchTerm['word_id']) && $aSearchTerm['word_id'])
{
- if ((!$bStructuredPhrases || $iPhrase > 0) && sizeof($aCurrentSearch['aName']) && strlen($sToken) >= 4)
+ if ((!$bStructuredPhrases || $iPhrase > 0) && sizeof($aCurrentSearch['aName']) && strpos($sToken, ' ') === false)
{
$aSearch = $aCurrentSearch;
$aSearch['iSearchRank'] += 1;
$aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
if ($aSearch['iSearchRank'] < $this->iMaxRank) $aNewWordsetSearches[] = $aSearch;
}
- elseif (isset($aValidTokens[' '.$sToken])) // revert to the token version?
+ elseif (isset($aValidTokens[' '.$sToken]) && strlen($sToken) >= 4) // revert to the token version?
{
+ $aSearch['aAddressNonSearch'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+ $aSearch['iSearchRank'] += 1;
+ if ($aSearch['iSearchRank'] < $this->iMaxRank) $aNewWordsetSearches[] = $aSearch;
foreach($aValidTokens[' '.$sToken] as $aSearchTermToken)
{
if (empty($aSearchTermToken['country_code'])
else
{
$aSearch['aAddressNonSearch'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+ if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
if ($aSearch['iSearchRank'] < $this->iMaxRank) $aNewWordsetSearches[] = $aSearch;
}
}
if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
{
$aSearch = $aCurrentSearch;
- $aSearch['iSearchRank'] += 2;
+ $aSearch['iSearchRank'] += 1;
+ if (!sizeof($aCurrentSearch['aName'])) $aSearch['iSearchRank'] += 1;
if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
if ($aWordFrequencyScores[$aSearchTerm['word_id']] < CONST_Max_Word_Frequency)
$aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
if (CONST_Debug) var_dump($sSQL);
$aPlaceIDs = $this->oDB->getCol($sSQL);
}
+ else
+ {
+ $aPlaceIDs = array();
+ }
}
else
{
// If excluded place IDs are given, it is fair to assume that
// there have been results in the small box, so no further
// expansion in that case.
- if (!sizeof($aPlaceIDs) && !sizeof($this->aExcludePlaceIDs))
+ // Also don't expand if bounded results were requested.
+ if (!sizeof($aPlaceIDs) && !sizeof($this->aExcludePlaceIDs) && !$this->bBoundedSearch)
{
$sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct";
if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)";
if ($bBoundingBoxSearch) $aTerms[] = "centroid && $this->sViewboxSmallSQL";
if ($sNearPointSQL) $aOrder[] = "ST_Distance($sNearPointSQL, centroid) asc";
- $sImportanceSQL = '(case when importance = 0 OR importance IS NULL then 0.75-(search_rank::float/40) else importance end)';
+ if ($aSearch['sHouseNumber'])
+ {
+ $sImportanceSQL = '- abs(26 - address_rank) + 3';
+ }
+ else
+ {
+ $sImportanceSQL = '(case when importance = 0 OR importance IS NULL then 0.75-(search_rank::float/40) else importance end)';
+ }
if ($this->sViewboxSmallSQL) $sImportanceSQL .= " * case when ST_Contains($this->sViewboxSmallSQL, centroid) THEN 1 ELSE 0.5 END";
if ($this->sViewboxLargeSQL) $sImportanceSQL .= " * case when ST_Contains($this->sViewboxLargeSQL, centroid) THEN 1 ELSE 0.5 END";
+
$aOrder[] = "$sImportanceSQL DESC";
if (sizeof($aSearch['aFullNameAddress']))
{
$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."'";
+ $sHouseNumberRegex = '\\\\m'.$aSearch['sHouseNumber'].'\\\\M';
+ $sSQL = "select place_id from placex where parent_place_id in (".$sPlaceIDs.") and transliteration(housenumber) ~* E'".$sHouseNumberRegex."'";
if (sizeof($this->aExcludePlaceIDs))
{
$sSQL .= " and place_id not in (".join(',',$this->aExcludePlaceIDs).")";
}
if ($sCountryCodesSQL) $sSQL .= " and lp.calculated_country_code in ($sCountryCodesSQL)";
if ($sOrderBySQL) $sSQL .= "order by ".$sOrderBySQL." asc";
- if ($iOffset) $sSQL .= " offset $iOffset";
+ if ($this->iOffset) $sSQL .= " offset $this->iOffset";
$sSQL .= " limit $this->iLimit";
if (CONST_Debug) var_dump($sSQL);
$aClassPlaceIDs = array_merge($aClassPlaceIDs, $this->oDB->getCol($sSQL));
}
if ($sCountryCodesSQL) $sSQL .= " and l.calculated_country_code in ($sCountryCodesSQL)";
if ($sOrderBy) $sSQL .= "order by ".$OrderBysSQL." asc";
- if ($iOffset) $sSQL .= " offset $iOffset";
+ if ($this->iOffset) $sSQL .= " offset $this->iOffset";
$sSQL .= " limit $this->iLimit";
if (CONST_Debug) var_dump($sSQL);
$aClassPlaceIDs = array_merge($aClassPlaceIDs, $this->oDB->getCol($sSQL));
// No results? Done
if (!sizeof($aSearchResults))
{
+ if ($this->bFallback)
+ {
+ if ($this->fallbackStructuredQuery())
+ {
+ return $this->lookup();
+ }
+ }
+
return array();
}
$aClassType = getClassTypesWithImportance();
- $aRecheckWords = preg_split('/\b/u',$sQuery);
+ $aRecheckWords = preg_split('/\b[\s,\\-]*/u',$sQuery);
foreach($aRecheckWords as $i => $sWord)
{
if (!$sWord) unset($aRecheckWords[$i]);
$aResult['aPolyPoints'][] = array($aPoint[1], $aPoint[2]);
}
}
- $aResult['aBoundingBox'] = array($aPointPolygon['minlat'],$aPointPolygon['maxlat'],$aPointPolygon['minlon'],$aPointPolygon['maxlon']);
+ $aResult['aBoundingBox'] = array((string)$aPointPolygon['minlat'],(string)$aPointPolygon['maxlat'],(string)$aPointPolygon['minlon'],(string)$aPointPolygon['maxlon']);
}
// Is there an icon set for this type of result?
$sAddress = $aResult['langaddress'];
foreach($aRecheckWords as $i => $sWord)
{
- if (stripos($sAddress, $sWord)!==false) $iCountWords++;
+ if (stripos($sAddress, $sWord)!==false)
+ {
+ $iCountWords++;
+ if (preg_match("/(^|,)\s*$sWord\s*(,|$)/", $sAddress)) $iCountWords += 0.1;
+ }
}
$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
} // end class
-
-/*
- if (isset($_GET['route']) && $_GET['route'] && isset($_GET['routewidth']) && $_GET['routewidth'])
- {
- $aPoints = explode(',',$_GET['route']);
- if (sizeof($aPoints) % 2 != 0)
- {
- userError("Uneven number of points");
- exit;
- }
- $sViewboxCentreSQL = "ST_SetSRID('LINESTRING(";
- $fPrevCoord = false;
- }
-*/