mirror of
https://github.com/osm-search/Nominatim.git
synced 2024-12-27 23:15:12 +03:00
320 lines
9.7 KiB
PHP
320 lines
9.7 KiB
PHP
<?php
|
|
/**
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* This file is part of Nominatim. (https://nominatim.org)
|
|
*
|
|
* Copyright (C) 2022 by the Nominatim developer community.
|
|
* For a full list of authors see the git log.
|
|
*/
|
|
|
|
namespace Nominatim;
|
|
|
|
require_once(CONST_LibDir.'/lib.php');
|
|
|
|
|
|
/**
|
|
* Collection of search constraints that are independent of the
|
|
* actual interpretation of the search query.
|
|
*
|
|
* The search context is shared between all SearchDescriptions. This
|
|
* object mainly serves as context provider for the database queries.
|
|
* Therefore most data is directly cached as SQL statements.
|
|
*/
|
|
class SearchContext
|
|
{
|
|
/// Search radius around a given Near reference point.
|
|
private $fNearRadius = false;
|
|
/// True if search must be restricted to viewbox only.
|
|
public $bViewboxBounded = false;
|
|
|
|
/// Reference point for search (as SQL).
|
|
public $sqlNear = '';
|
|
/// Viewbox selected for search (as SQL).
|
|
public $sqlViewboxSmall = '';
|
|
/// Viewbox with a larger buffer around (as SQL).
|
|
public $sqlViewboxLarge = '';
|
|
/// Reference along a route (as SQL).
|
|
public $sqlViewboxCentre = '';
|
|
/// List of countries to restrict search to (as array).
|
|
public $aCountryList = null;
|
|
/// List of countries to restrict search to (as SQL).
|
|
public $sqlCountryList = '';
|
|
/// List of place IDs to exclude (as SQL).
|
|
private $sqlExcludeList = '';
|
|
/// Subset of word ids of full words in the query.
|
|
private $aFullNameWords = array();
|
|
|
|
public function setFullNameWords($aWordList)
|
|
{
|
|
$this->aFullNameWords = $aWordList;
|
|
}
|
|
|
|
public function getFullNameTerms()
|
|
{
|
|
return $this->aFullNameWords;
|
|
}
|
|
|
|
/**
|
|
* Check if a reference point is defined.
|
|
*
|
|
* @return bool True if a reference point is defined.
|
|
*/
|
|
public function hasNearPoint()
|
|
{
|
|
return $this->fNearRadius !== false;
|
|
}
|
|
|
|
/**
|
|
* Get radius around reference point.
|
|
*
|
|
* @return float Search radius around reference point.
|
|
*/
|
|
public function nearRadius()
|
|
{
|
|
return $this->fNearRadius;
|
|
}
|
|
|
|
/**
|
|
* Set search reference point in WGS84.
|
|
*
|
|
* If set, then only places around this point will be taken into account.
|
|
*
|
|
* @param float $fLat Latitude of point.
|
|
* @param float $fLon Longitude of point.
|
|
* @param float $fRadius Search radius around point.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
|
|
{
|
|
$this->fNearRadius = $fRadius;
|
|
$this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
|
|
}
|
|
|
|
/**
|
|
* Check if the search is geographically restricted.
|
|
*
|
|
* Searches are restricted if a reference point is given or if
|
|
* a bounded viewbox is set.
|
|
*
|
|
* @return bool True, if the search is geographically bounded.
|
|
*/
|
|
public function isBoundedSearch()
|
|
{
|
|
return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded);
|
|
}
|
|
|
|
/**
|
|
* Set rectangular viewbox.
|
|
*
|
|
* The viewbox may be bounded which means that no search results
|
|
* must be outside the viewbox.
|
|
*
|
|
* @param float[4] $aViewBox Coordinates of the viewbox.
|
|
* @param bool $bBounded True if the viewbox is bounded.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setViewboxFromBox(&$aViewBox, $bBounded)
|
|
{
|
|
$this->bViewboxBounded = $bBounded;
|
|
$this->sqlViewboxCentre = '';
|
|
|
|
$this->sqlViewboxSmall = sprintf(
|
|
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
|
$aViewBox[0],
|
|
$aViewBox[1],
|
|
$aViewBox[2],
|
|
$aViewBox[3]
|
|
);
|
|
|
|
$fHeight = abs($aViewBox[0] - $aViewBox[2]);
|
|
$fWidth = abs($aViewBox[1] - $aViewBox[3]);
|
|
|
|
$this->sqlViewboxLarge = sprintf(
|
|
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
|
max($aViewBox[0], $aViewBox[2]) + $fHeight,
|
|
max($aViewBox[1], $aViewBox[3]) + $fWidth,
|
|
min($aViewBox[0], $aViewBox[2]) - $fHeight,
|
|
min($aViewBox[1], $aViewBox[3]) - $fWidth
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Set viewbox along a route.
|
|
*
|
|
* The viewbox may be bounded which means that no search results
|
|
* must be outside the viewbox.
|
|
*
|
|
* @param object $oDB Nominatim::DB instance to use for computing the box.
|
|
* @param string[] $aRoutePoints List of x,y coordinates along a route.
|
|
* @param float $fRouteWidth Buffer around the route to use.
|
|
* @param bool $bBounded True if the viewbox bounded.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded)
|
|
{
|
|
$this->bViewboxBounded = $bBounded;
|
|
$this->sqlViewboxCentre = "ST_SetSRID('LINESTRING(";
|
|
$sSep = '';
|
|
foreach ($aRoutePoints as $aPoint) {
|
|
$fPoint = (float)$aPoint;
|
|
$this->sqlViewboxCentre .= $sSep.$fPoint;
|
|
$sSep = ($sSep == ' ') ? ',' : ' ';
|
|
}
|
|
$this->sqlViewboxCentre .= ")'::geometry,4326)";
|
|
|
|
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
|
|
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox');
|
|
$this->sqlViewboxSmall = "'".$sGeom."'::geometry";
|
|
|
|
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
|
|
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox');
|
|
$this->sqlViewboxLarge = "'".$sGeom."'::geometry";
|
|
}
|
|
|
|
/**
|
|
* Set list of excluded place IDs.
|
|
*
|
|
* @param integer[] $aExcluded List of IDs.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setExcludeList($aExcluded)
|
|
{
|
|
$this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')';
|
|
}
|
|
|
|
/**
|
|
* Set list of countries to restrict search to.
|
|
*
|
|
* @param string[] $aCountries List of two-letter lower-case country codes.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setCountryList($aCountries)
|
|
{
|
|
$this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')';
|
|
$this->aCountryList = $aCountries;
|
|
}
|
|
|
|
/**
|
|
* Extract a reference point from a query string.
|
|
*
|
|
* @param string $sQuery Query to scan.
|
|
*
|
|
* @return string The remaining query string.
|
|
*/
|
|
public function setNearPointFromQuery($sQuery)
|
|
{
|
|
$aResult = parseLatLon($sQuery);
|
|
|
|
if ($aResult !== false
|
|
&& $aResult[1] <= 90.1
|
|
&& $aResult[1] >= -90.1
|
|
&& $aResult[2] <= 180.1
|
|
&& $aResult[2] >= -180.1
|
|
) {
|
|
$this->setNearPoint($aResult[1], $aResult[2]);
|
|
$sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
|
|
}
|
|
|
|
return $sQuery;
|
|
}
|
|
|
|
/**
|
|
* Get an SQL snippet for computing the distance from the reference point.
|
|
*
|
|
* @param string $sObj SQL variable name to compute the distance from.
|
|
*
|
|
* @return string An SQL string.
|
|
*/
|
|
public function distanceSQL($sObj)
|
|
{
|
|
return 'ST_Distance('.$this->sqlNear.", $sObj)";
|
|
}
|
|
|
|
/**
|
|
* Get an SQL snippet for checking if something is within range of the
|
|
* reference point.
|
|
*
|
|
* @param string $sObj SQL variable name to compute if it is within range.
|
|
*
|
|
* @return string An SQL string.
|
|
*/
|
|
public function withinSQL($sObj)
|
|
{
|
|
return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
|
|
}
|
|
|
|
/**
|
|
* Get an SQL snippet of the importance factor of the viewbox.
|
|
*
|
|
* The importance factor is computed by checking if an object is within
|
|
* the viewbox and/or the extended version of the viewbox.
|
|
*
|
|
* @param string $sObj SQL variable name of object to weight the importance
|
|
*
|
|
* @return string SQL snippet of the factor with a leading multiply sign.
|
|
*/
|
|
public function viewboxImportanceSQL($sObj)
|
|
{
|
|
$sSQL = '';
|
|
|
|
if ($this->sqlViewboxSmall) {
|
|
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END";
|
|
}
|
|
if ($this->sqlViewboxLarge) {
|
|
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END";
|
|
}
|
|
|
|
return $sSQL;
|
|
}
|
|
|
|
/**
|
|
* SQL snippet checking if a place ID should be excluded.
|
|
*
|
|
* @param string $sVariable SQL variable name of place ID to check,
|
|
* potentially prefixed with more SQL.
|
|
*
|
|
* @return string SQL snippet.
|
|
*/
|
|
public function excludeSQL($sVariable)
|
|
{
|
|
if ($this->sqlExcludeList) {
|
|
return $sVariable.$this->sqlExcludeList;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Check if the given country is covered by the search context.
|
|
*
|
|
* @param string $sCountryCode Country code of the country to check.
|
|
*
|
|
* @return True, if no country code restrictions are set or the
|
|
* country is included in the country list.
|
|
*/
|
|
public function isCountryApplicable($sCountryCode)
|
|
{
|
|
return $this->aCountryList === null || in_array($sCountryCode, $this->aCountryList);
|
|
}
|
|
|
|
public function debugInfo()
|
|
{
|
|
return array(
|
|
'Near radius' => $this->fNearRadius,
|
|
'Near point (SQL)' => $this->sqlNear,
|
|
'Bounded viewbox' => $this->bViewboxBounded,
|
|
'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
|
|
'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
|
|
'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
|
|
'Countries (SQL)' => $this->sqlCountryList,
|
|
'Excluded IDs (SQL)' => $this->sqlExcludeList
|
|
);
|
|
}
|
|
}
|