source: extensions/rv_gmaps/trunk/qsearch.inc.php @ 31846

Last change on this file since 31846 was 29920, checked in by rvelices, 10 years ago

rv_gmaps added near: quick search scope (experimental so far)

File size: 7.2 KB
Line 
1<?php
2include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
3
4define('LAT_PATT',
5"(?<lsig>[+-]?)(?<ldeg>\\d{1,3}\\.?\\d*)\\s*(?:\\xC2\\xB0)?\\s*(?:(?<lmin>\\d+)\\s*'?)?\\s*(?:(?<lsec>\\d+\\.?\\d*)\\s*\"?)?\\s*(?<lhs>[NS])?"
6);
7define('LON_PATT',
8"(?<Lsig>[+-]?)(?<Ldeg>\\d{1,3}\\.?\\d*)\\s*(?:\\xC2\\xB0)?\\s*(?:(?<Lmin>\\d+)\\s*'?)?\\s*(?:(?<Lsec>\\d+\\.?\\d*)\\s*\"?)?\\s*(?<Lhs>[EW])?"
9);
10define('RAD_PATT',
11"(?:,\\s*(?<r>[+-]?\\d+\\.?\\d*)\\s*(?:(?<ru>mi|km|m)[a-z]*)?)?"
12);
13define('POS_PATT',
14LAT_PATT.'\\s*,\\s*'.LON_PATT.'\\s*'.RAD_PATT
15);
16
17class QNearScope extends QSearchScope
18{
19  function __construct($id, $aliases, $nullable=false)
20  {
21    parent::__construct($id, $aliases, $nullable, true);
22  }
23
24        static function tollr($matches)
25        {
26                foreach( array('l','L') as $l)
27                {
28                        $val = 0 + $matches["${l}deg"];
29                        $val += $matches["${l}min"] /60;
30                        $val += $matches["${l}sec"] /3600;
31                        if ($matches["${l}sig"] == '-')
32                                $val =-$val;
33                        if (strpbrk($matches["${l}hs"], 'sSwW')!==false)
34                                $val =-$val;
35                        $vals[] = $val;
36                }
37                if (abs($vals[0]) > 90 || abs($vals[1]) > 180)
38                        return false;
39                $ret =  array(
40                        'lat' => $vals[0],
41                        'lon' => $vals[1],
42                        );
43                if (!empty($matches['r']))
44                {
45                        $r = 0 + $matches['r'];
46                        $mult = 1000;
47                        if (!empty($matches['ru']))
48                        {
49                                if (0===stripos($matches['ru'], 'mi'))
50                                        $mult = 1609.344;
51                                elseif ('m'==$matches['ru'][0])
52                                        $mult=1;
53                        }
54                        $r *= $mult;
55                        if ($r<=0)
56                                return false;
57                        $ret['r'] = $r;
58                }
59                return $ret;
60        }
61
62  function parse($token)
63  {
64                $pat = '#'.POS_PATT.'#i';
65                if (preg_match($pat, $token->term, $matches))
66                {
67                        $ll = self::tollr($matches);
68                        if ($ll)
69                        {
70                                $r = @$ll['r'];
71                                if (!$r) $r = 10000;
72
73  $cos_lat = max( 1e-2, cos($ll['lat']*M_PI/180) );
74  $dlat = $r/113000; // 1 degree is approx between 111 and 116 km
75  $dlon = min( $r/(113000*$cos_lat), 180 );
76  $bounds = array(
77      's' => $ll['lat'] - $dlat,
78      'w' => $ll['lon'] - $dlon,
79      'n' => $ll['lat'] + $dlat,
80      'e' => $ll['lon'] + $dlon,
81    );
82  if ($bounds['s']<-90) $bounds['s']=-90;
83  if ($bounds['w']<-180) $bounds['w']+=360;
84  if ($bounds['n']>90) $bounds['n']=90;
85  if ($bounds['e']>180) $bounds['e']-=360;
86
87                                $token->scope_data = $bounds;
88                                return true;
89                        }
90                }
91                elseif (strlen($token->term) > 2)
92                {
93                        $key = 'geo_'.urlencode(transliterate($token->term));
94                        global $persistent_cache;
95                        if (!$persistent_cache->get($key, $geo))
96                        {
97                                include_once( PHPWG_ROOT_PATH .'admin/include/functions.php' );
98                                $url = 'http://maps.googleapis.com/maps/api/geocode/json?address='.urlencode($token->term);
99                                $ret = fetchRemote($url, $resp);
100
101                                $persistent_cache->set($key.'_json', $resp);
102                                $geo = null;
103                                if ($ret)
104                                {
105                                        $geo = json_decode($resp, true);
106                                        $persistent_cache->set($key, $geo);
107                                }
108                        }
109
110                        if (count(@$geo['results'])) {
111                                        $gb = $geo['results'][0]['geometry']['viewport'];
112                                $bounds = array(
113                        's' => $gb['southwest']['lat'],
114                        'w' => $gb['southwest']['lng'],
115                        'n' => $gb['northeast']['lat'],
116                        'e' => $gb['northeast']['lng'],
117                        );
118                                        $add = @$geo['results'][0]['formatted_address'];
119                                        if (strlen($add))
120                                        {
121                                                if (strpbrk($add, ' ,')!==false)
122                                                        $token->modifier |= QST_QUOTED;
123                                                $token->term = $add;
124                                        }
125                                        $token->scope_data = $bounds;
126                                        $token->geo = $geo['results'];
127                                        add_event_handler('qsearch_results', 'rvm_qsearch_results');
128                                        return true;
129                        }
130
131                }
132    return false;
133  }
134}
135
136function rvm_qsearch_pre($q)
137{
138        if (preg_match('#^'.POS_PATT.'$#i', $q))
139                $q = 'near:"'.str_replace('"','',$q).'"';
140
141        $q = preg_replace_callback('#(?<pre>^|[^a-z0-9\x80-\xFF])near:(?<pos>'.POS_PATT.')#i', function ($matches) {
142        $pre = $matches['pre'];
143        $pos = $matches['pos'];
144        $post = ' ';
145        $pos = str_replace('"','', rtrim($pos));
146        return "${pre}near:\"${pos}\"${post}";
147}, $q);
148
149        $off = 0;
150        while (preg_match("#(?:^|(?<=[ \\(-]))near[: ](?<loc>[a-z0-9 ,-]+)#i", $q, $matches, PREG_OFFSET_CAPTURE, $off))
151        {
152                $loc = $matches['loc'][0];
153//todo check space-char
154
155                //check or and not
156                if (preg_match("# (and|or|not)($| )#i", $loc, $m2, PREG_OFFSET_CAPTURE))
157                {
158                        $loc = substr($loc, 0, $m2[0][1]);
159                }
160
161                //check following : after match
162                if (':' == @$q[ $matches[0][1] + strlen($matches[0][0])])
163                        $loc = substr($loc, 0, strrpos($loc, ' '));
164                $off += 5;
165
166                $quote='';
167                if (strpbrk($loc, ' ,')!==false)
168                        $quote='"';
169
170                $q = substr_replace($q,
171                        'near:'.$quote.$loc.$quote.' ', $matches[0][1],
172                        5+strlen($loc));
173                $q = rtrim($q);
174        }
175
176        return $q;
177}
178
179function rvm_qsearch_get_scopes($scopes)
180{
181        $scopes[] = new QNearScope('near', array(), true);
182        add_event_handler('qsearch_get_images_sql_scopes', 'rvm_qsearch_get_images_sql_scopes');
183        return $scopes;
184}
185
186function rvm_qsearch_get_images_sql_scopes($clauses, $token)
187{
188        switch ($token->scope->id)
189        {
190                case 'near':
191                include_once( dirname(__FILE__) .'/include/functions_map.php');
192
193                $bounds = $token->scope_data;
194                $clauses[] = rvm_bounds_to_sql( $bounds );
195                        break;
196        }
197        return $clauses;
198}
199
200function rvm_build_addr($comps, $remove)
201{
202        $keep = array(
203                'neighborhood',
204                'sublocality',
205                'locality',
206                'administrative_area_level_2',
207                'administrative_area_level_1',
208                'country',
209        );
210        $res = array();
211
212        foreach($comps as $comp) {
213                $common = array_intersect($keep, $comp['types']);
214                if (empty($common)) {
215                        $remove--;
216                        continue;
217                }
218                if (!in_array($comp['long_name'],$res))
219                        $res[current($common)] = $comp['long_name'];
220        }
221
222        if ($remove>0)
223                array_shift($res);
224        return $res;
225}
226
227function rvm_qsearch_results($sr, $exp, $qsr)
228{
229//echo __FUNCTION__.'<br>';
230        for ($i=0; $i<count($exp->stokens); $i++)
231        {
232                $token = $exp->stokens[$i];
233                if ( !($geo=@$token->geo))
234                        continue;
235                $aacomp = array();
236                $acomp = rvm_build_addr($geo[0]['address_components'],1);
237                if (!empty($acomp))
238                        $aacomp[] = $acomp;
239                for ($j=1; $j<count($geo); $j++)
240                        $aacomp[] = array($geo[$j]['formatted_address']);
241
242                if (!empty($aacomp)) {
243                        $sr['qs']['geo'][] = array(
244                                'needle' => (string)$token,
245                                'haystack' => (string)$exp,
246                                'alt' => $aacomp);
247                }
248        }
249        return $sr;
250}
251
252function rvm_qsearch_show_alt($geo)
253{
254        global $page, $template;
255//echo __FUNCTION__.'<br>';
256        $base_url = get_root_url().'qsearch.php?q=';
257
258        foreach( $geo as $pseudo_token)
259        {
260                $haystack = $pseudo_token['haystack'];
261                $needle = $pseudo_token['needle'];
262                foreach($pseudo_token['alt'] as $acomp)
263                {
264                        $line = array();
265                        $acomp = array_values($acomp);
266                        for ($i=0; $i<count($acomp); $i++)
267                        {
268                                $text = $link = $acomp[$i];
269                                for ($j=$i+1; $j<count($acomp); $j++)
270                                        $link .= ','.$acomp[$j];
271                                $line[] = '<a href="'.$base_url.urlencode( str_replace($needle, 'near:"'.$link.'"',$haystack) ).'">'.htmlspecialchars($text).'</a>';
272                        }
273                        $lines[] = implode(', ', $line);
274                }
275        }
276        $template->assign('PLUGIN_INDEX_CONTENT_BEGIN', '<p class=search_results>See also: '.implode(" &mdash; \n", $lines).'</p>');
277}
278
279function rvm_get_popup_help($content, $page)
280{
281        $needle = '</table>';
282        if ('quick_search'==$page &&
283                ($pos=strpos($content,$needle)) &&
284                ($pos=strpos($content,$needle,$pos+1)) )
285        {
286                $e = '';
287
288                $e .= "<tr>
289<td>
290<q>near:</q>
291</td>
292<td>
293<q>near:lat,lon,distance</q>; Distance is optional (default: 10km). If specified it should be a number followed by an optional unit (m, km, mi).
294<br><q>near:location</q>
295</td>
296</tr>
297";
298                $content = substr_replace($content, $e, $pos, 0);
299        }
300        return $content;
301}
302
303?>
Note: See TracBrowser for help on using the repository browser.