]> git.openstreetmap.org Git - nominatim.git/blob - lib-php/SearchContext.php
extend API unit tests
[nominatim.git] / lib-php / SearchContext.php
1 <?php
2
3 namespace Nominatim;
4
5 require_once(CONST_LibDir.'/lib.php');
6
7
8 /**
9  * Collection of search constraints that are independent of the
10  * actual interpretation of the search query.
11  *
12  * The search context is shared between all SearchDescriptions. This
13  * object mainly serves as context provider for the database queries.
14  * Therefore most data is directly cached as SQL statements.
15  */
16 class SearchContext
17 {
18     /// Search radius around a given Near reference point.
19     private $fNearRadius = false;
20     /// True if search must be restricted to viewbox only.
21     public $bViewboxBounded = false;
22
23     /// Reference point for search (as SQL).
24     public $sqlNear = '';
25     /// Viewbox selected for search (as SQL).
26     public $sqlViewboxSmall = '';
27     /// Viewbox with a larger buffer around (as SQL).
28     public $sqlViewboxLarge = '';
29     /// Reference along a route (as SQL).
30     public $sqlViewboxCentre = '';
31     /// List of countries to restrict search to (as array).
32     public $aCountryList = null;
33     /// List of countries to restrict search to (as SQL).
34     public $sqlCountryList = '';
35     /// List of place IDs to exclude (as SQL).
36     private $sqlExcludeList = '';
37     /// Subset of word ids of full words in the query.
38     private $aFullNameWords = array();
39
40     public function setFullNameWords($aWordList)
41     {
42         $this->aFullNameWords = $aWordList;
43     }
44
45     public function getFullNameTerms()
46     {
47         return $this->aFullNameWords;
48     }
49
50     /**
51      * Check if a reference point is defined.
52      *
53      * @return bool True if a reference point is defined.
54      */
55     public function hasNearPoint()
56     {
57         return $this->fNearRadius !== false;
58     }
59
60     /**
61      * Get radius around reference point.
62      *
63      * @return float Search radius around reference point.
64      */
65     public function nearRadius()
66     {
67         return $this->fNearRadius;
68     }
69
70     /**
71      * Set search reference point in WGS84.
72      *
73      * If set, then only places around this point will be taken into account.
74      *
75      * @param float $fLat    Latitude of point.
76      * @param float $fLon    Longitude of point.
77      * @param float $fRadius Search radius around point.
78      *
79      * @return void
80      */
81     public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
82     {
83         $this->fNearRadius = $fRadius;
84         $this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
85     }
86
87     /**
88      * Check if the search is geographically restricted.
89      *
90      * Searches are restricted if a reference point is given or if
91      * a bounded viewbox is set.
92      *
93      * @return bool True, if the search is geographically bounded.
94      */
95     public function isBoundedSearch()
96     {
97         return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded);
98     }
99
100     /**
101      * Set rectangular viewbox.
102      *
103      * The viewbox may be bounded which means that no search results
104      * must be outside the viewbox.
105      *
106      * @param float[4] $aViewBox Coordinates of the viewbox.
107      * @param bool     $bBounded True if the viewbox is bounded.
108      *
109      * @return void
110      */
111     public function setViewboxFromBox(&$aViewBox, $bBounded)
112     {
113         $this->bViewboxBounded = $bBounded;
114         $this->sqlViewboxCentre = '';
115
116         $this->sqlViewboxSmall = sprintf(
117             'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
118             $aViewBox[0],
119             $aViewBox[1],
120             $aViewBox[2],
121             $aViewBox[3]
122         );
123
124         $fHeight = abs($aViewBox[0] - $aViewBox[2]);
125         $fWidth = abs($aViewBox[1] - $aViewBox[3]);
126
127         $this->sqlViewboxLarge = sprintf(
128             'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
129             max($aViewBox[0], $aViewBox[2]) + $fHeight,
130             max($aViewBox[1], $aViewBox[3]) + $fWidth,
131             min($aViewBox[0], $aViewBox[2]) - $fHeight,
132             min($aViewBox[1], $aViewBox[3]) - $fWidth
133         );
134     }
135
136     /**
137      * Set viewbox along a route.
138      *
139      * The viewbox may be bounded which means that no search results
140      * must be outside the viewbox.
141      *
142      * @param object   $oDB          Nominatim::DB instance to use for computing the box.
143      * @param string[] $aRoutePoints List of x,y coordinates along a route.
144      * @param float    $fRouteWidth  Buffer around the route to use.
145      * @param bool     $bBounded     True if the viewbox bounded.
146      *
147      * @return void
148      */
149     public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded)
150     {
151         $this->bViewboxBounded = $bBounded;
152         $this->sqlViewboxCentre = "ST_SetSRID('LINESTRING(";
153         $sSep = '';
154         foreach ($aRoutePoints as $aPoint) {
155             $fPoint = (float)$aPoint;
156             $this->sqlViewboxCentre .= $sSep.$fPoint;
157             $sSep = ($sSep == ' ') ? ',' : ' ';
158         }
159         $this->sqlViewboxCentre .= ")'::geometry,4326)";
160
161         $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
162         $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox');
163         $this->sqlViewboxSmall = "'".$sGeom."'::geometry";
164
165         $sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
166         $sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox');
167         $this->sqlViewboxLarge = "'".$sGeom."'::geometry";
168     }
169
170     /**
171      * Set list of excluded place IDs.
172      *
173      * @param integer[] $aExcluded List of IDs.
174      *
175      * @return void
176      */
177     public function setExcludeList($aExcluded)
178     {
179         $this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')';
180     }
181
182     /**
183      * Set list of countries to restrict search to.
184      *
185      * @param string[] $aCountries List of two-letter lower-case country codes.
186      *
187      * @return void
188      */
189     public function setCountryList($aCountries)
190     {
191         $this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')';
192         $this->aCountryList = $aCountries;
193     }
194
195     /**
196      * Extract a reference point from a query string.
197      *
198      * @param string $sQuery Query to scan.
199      *
200      * @return string The remaining query string.
201      */
202     public function setNearPointFromQuery($sQuery)
203     {
204         $aResult = parseLatLon($sQuery);
205
206         if ($aResult !== false
207             && $aResult[1] <= 90.1
208             && $aResult[1] >= -90.1
209             && $aResult[2] <= 180.1
210             && $aResult[2] >= -180.1
211         ) {
212             $this->setNearPoint($aResult[1], $aResult[2]);
213             $sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
214         }
215
216         return $sQuery;
217     }
218
219     /**
220      * Get an SQL snippet for computing the distance from the reference point.
221      *
222      * @param string $sObj SQL variable name to compute the distance from.
223      *
224      * @return string An SQL string.
225      */
226     public function distanceSQL($sObj)
227     {
228         return 'ST_Distance('.$this->sqlNear.", $sObj)";
229     }
230
231     /**
232      * Get an SQL snippet for checking if something is within range of the
233      * reference point.
234      *
235      * @param string $sObj SQL variable name to compute if it is within range.
236      *
237      * @return string An SQL string.
238      */
239     public function withinSQL($sObj)
240     {
241         return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
242     }
243
244     /**
245      * Get an SQL snippet of the importance factor of the viewbox.
246      *
247      * The importance factor is computed by checking if an object is within
248      * the viewbox and/or the extended version of the viewbox.
249      *
250      * @param string $sObj SQL variable name of object to weight the importance
251      *
252      * @return string SQL snippet of the factor with a leading multiply sign.
253      */
254     public function viewboxImportanceSQL($sObj)
255     {
256         $sSQL = '';
257
258         if ($this->sqlViewboxSmall) {
259             $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END";
260         }
261         if ($this->sqlViewboxLarge) {
262             $sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END";
263         }
264
265         return $sSQL;
266     }
267
268     /**
269      * SQL snippet checking if a place ID should be excluded.
270      *
271      * @param string $sVariable SQL variable name of place ID to check,
272      *                          potentially prefixed with more SQL.
273      *
274      * @return string SQL snippet.
275      */
276     public function excludeSQL($sVariable)
277     {
278         if ($this->sqlExcludeList) {
279             return $sVariable.$this->sqlExcludeList;
280         }
281
282         return '';
283     }
284
285     /**
286      * Check if the given country is covered by the search context.
287      *
288      * @param string $sCountryCode  Country code of the country to check.
289      *
290      * @return True, if no country code restrictions are set or the
291      *         country is included in the country list.
292      */
293     public function isCountryApplicable($sCountryCode)
294     {
295         return $this->aCountryList === null || in_array($sCountryCode, $this->aCountryList);
296     }
297
298     public function debugInfo()
299     {
300         return array(
301                 'Near radius' => $this->fNearRadius,
302                 'Near point (SQL)' => $this->sqlNear,
303                 'Bounded viewbox' => $this->bViewboxBounded,
304                 'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
305                 'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
306                 'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
307                 'Countries (SQL)' => $this->sqlCountryList,
308                 'Excluded IDs (SQL)' => $this->sqlExcludeList
309                );
310     }
311 }