3 * SPDX-License-Identifier: GPL-2.0-only
5 * This file is part of Nominatim. (https://nominatim.org)
7 * Copyright (C) 2022 by the Nominatim developer community.
8 * For a full list of authors see the git log.
13 require_once(CONST_LibDir.'/lib.php');
17 * Collection of search constraints that are independent of the
18 * actual interpretation of the search query.
20 * The search context is shared between all SearchDescriptions. This
21 * object mainly serves as context provider for the database queries.
22 * Therefore most data is directly cached as SQL statements.
26 /// Search radius around a given Near reference point.
27 private $fNearRadius = false;
28 /// True if search must be restricted to viewbox only.
29 public $bViewboxBounded = false;
31 /// Reference point for search (as SQL).
33 /// Viewbox selected for search (as SQL).
34 public $sqlViewboxSmall = '';
35 /// Viewbox with a larger buffer around (as SQL).
36 public $sqlViewboxLarge = '';
37 /// Reference along a route (as SQL).
38 public $sqlViewboxCentre = '';
39 /// List of countries to restrict search to (as array).
40 public $aCountryList = null;
41 /// List of countries to restrict search to (as SQL).
42 public $sqlCountryList = '';
43 /// List of place IDs to exclude (as SQL).
44 private $sqlExcludeList = '';
45 /// Subset of word ids of full words in the query.
46 private $aFullNameWords = array();
48 public function setFullNameWords($aWordList)
50 $this->aFullNameWords = $aWordList;
53 public function getFullNameTerms()
55 return $this->aFullNameWords;
59 * Check if a reference point is defined.
61 * @return bool True if a reference point is defined.
63 public function hasNearPoint()
65 return $this->fNearRadius !== false;
69 * Get radius around reference point.
71 * @return float Search radius around reference point.
73 public function nearRadius()
75 return $this->fNearRadius;
79 * Set search reference point in WGS84.
81 * If set, then only places around this point will be taken into account.
83 * @param float $fLat Latitude of point.
84 * @param float $fLon Longitude of point.
85 * @param float $fRadius Search radius around point.
89 public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
91 $this->fNearRadius = $fRadius;
92 $this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
96 * Check if the search is geographically restricted.
98 * Searches are restricted if a reference point is given or if
99 * a bounded viewbox is set.
101 * @return bool True, if the search is geographically bounded.
103 public function isBoundedSearch()
105 return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded);
109 * Set rectangular viewbox.
111 * The viewbox may be bounded which means that no search results
112 * must be outside the viewbox.
114 * @param float[4] $aViewBox Coordinates of the viewbox.
115 * @param bool $bBounded True if the viewbox is bounded.
119 public function setViewboxFromBox(&$aViewBox, $bBounded)
121 $this->bViewboxBounded = $bBounded;
122 $this->sqlViewboxCentre = '';
124 $this->sqlViewboxSmall = sprintf(
125 'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
132 $fHeight = abs($aViewBox[0] - $aViewBox[2]);
133 $fWidth = abs($aViewBox[1] - $aViewBox[3]);
135 $this->sqlViewboxLarge = sprintf(
136 'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
137 max($aViewBox[0], $aViewBox[2]) + $fHeight,
138 max($aViewBox[1], $aViewBox[3]) + $fWidth,
139 min($aViewBox[0], $aViewBox[2]) - $fHeight,
140 min($aViewBox[1], $aViewBox[3]) - $fWidth
145 * Set viewbox along a route.
147 * The viewbox may be bounded which means that no search results
148 * must be outside the viewbox.
150 * @param object $oDB Nominatim::DB instance to use for computing the box.
151 * @param string[] $aRoutePoints List of x,y coordinates along a route.
152 * @param float $fRouteWidth Buffer around the route to use.
153 * @param bool $bBounded True if the viewbox bounded.
157 public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded)
159 $this->bViewboxBounded = $bBounded;
160 $this->sqlViewboxCentre = "ST_SetSRID('LINESTRING(";
162 foreach ($aRoutePoints as $aPoint) {
163 $fPoint = (float)$aPoint;
164 $this->sqlViewboxCentre .= $sSep.$fPoint;
165 $sSep = ($sSep == ' ') ? ',' : ' ';
167 $this->sqlViewboxCentre .= ")'::geometry,4326)";
169 $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
170 $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox');
171 $this->sqlViewboxSmall = "'".$sGeom."'::geometry";
173 $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
174 $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox');
175 $this->sqlViewboxLarge = "'".$sGeom."'::geometry";
179 * Set list of excluded place IDs.
181 * @param integer[] $aExcluded List of IDs.
185 public function setExcludeList($aExcluded)
187 $this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')';
191 * Set list of countries to restrict search to.
193 * @param string[] $aCountries List of two-letter lower-case country codes.
197 public function setCountryList($aCountries)
199 $this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')';
200 $this->aCountryList = $aCountries;
204 * Extract a reference point from a query string.
206 * @param string $sQuery Query to scan.
208 * @return string The remaining query string.
210 public function setNearPointFromQuery($sQuery)
212 $aResult = parseLatLon($sQuery);
214 if ($aResult !== false
215 && $aResult[1] <= 90.1
216 && $aResult[1] >= -90.1
217 && $aResult[2] <= 180.1
218 && $aResult[2] >= -180.1
220 $this->setNearPoint($aResult[1], $aResult[2]);
221 $sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
228 * Get an SQL snippet for computing the distance from the reference point.
230 * @param string $sObj SQL variable name to compute the distance from.
232 * @return string An SQL string.
234 public function distanceSQL($sObj)
236 return 'ST_Distance('.$this->sqlNear.", $sObj)";
240 * Get an SQL snippet for checking if something is within range of the
243 * @param string $sObj SQL variable name to compute if it is within range.
245 * @return string An SQL string.
247 public function withinSQL($sObj)
249 return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
253 * Get an SQL snippet of the importance factor of the viewbox.
255 * The importance factor is computed by checking if an object is within
256 * the viewbox and/or the extended version of the viewbox.
258 * @param string $sObj SQL variable name of object to weight the importance
260 * @return string SQL snippet of the factor with a leading multiply sign.
262 public function viewboxImportanceSQL($sObj)
266 if ($this->sqlViewboxSmall) {
267 $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END";
269 if ($this->sqlViewboxLarge) {
270 $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END";
277 * SQL snippet checking if a place ID should be excluded.
279 * @param string $sVariable SQL variable name of place ID to check,
280 * potentially prefixed with more SQL.
282 * @return string SQL snippet.
284 public function excludeSQL($sVariable)
286 if ($this->sqlExcludeList) {
287 return $sVariable.$this->sqlExcludeList;
294 * Check if the given country is covered by the search context.
296 * @param string $sCountryCode Country code of the country to check.
298 * @return True, if no country code restrictions are set or the
299 * country is included in the country list.
301 public function isCountryApplicable($sCountryCode)
303 return $this->aCountryList === null || in_array($sCountryCode, $this->aCountryList);
306 public function debugInfo()
309 'Near radius' => $this->fNearRadius,
310 'Near point (SQL)' => $this->sqlNear,
311 'Bounded viewbox' => $this->bViewboxBounded,
312 'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
313 'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
314 'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
315 'Countries (SQL)' => $this->sqlCountryList,
316 'Excluded IDs (SQL)' => $this->sqlExcludeList