Skip to content

Commit b372e0a

Browse files
committed
Adds location based search of usergroups
1 parent 86ec192 commit b372e0a

File tree

3 files changed

+298
-0
lines changed

3 files changed

+298
-0
lines changed

src/module/Phpug/config/module.config.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@
444444
'Phpug\Api\Rest\Twitter' => 'Phpug\Api\Rest\TwitterController',
445445
'Phpug\Api\v1\Usergroup' => 'Phpug\Api\v1\UsergroupController',
446446
'Phpug\Api\v1\Calendar' => 'Phpug\Api\v1\CalendarController',
447+
'Phpug\Api\v1\Location' => 'Phpug\Api\v1\LocationController',
447448
'Phpug\Controller\MentoringController' => 'Phpug\Controller\MentoringController',
448449
'Phpug\Controller\EventCacheController' => 'Phpug\Controller\EventCacheController',
449450
'Phpug\Controller\TwitterController' => 'Phpug\Controller\TwitterController',
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
/**
3+
* Copyright (c)2014-2014 heiglandreas
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIBILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*
23+
* @category
24+
* @author Andreas Heigl<[email protected]>
25+
* @copyright ©2014-2014 Andreas Heigl
26+
* @license http://www.opesource.org/licenses/mit-license.php MIT-License
27+
* @version 0.0
28+
* @since 04.02.14
29+
* @link https://github.com/heiglandreas/
30+
*/
31+
32+
namespace Phpug\Api\v1;
33+
34+
use Phpug\ORM\Query\AST\Functions\DistanceFrom;
35+
use Zend\Mvc\Controller\AbstractActionController;
36+
use Sabre\VObject;
37+
use Zend\Json\Json;
38+
39+
class LocationController extends AbstractActionController
40+
{
41+
private $acceptCriteria = array(
42+
'Zend\View\Model\JsonModel' => array(
43+
'application/json',
44+
'text/html',
45+
),
46+
'Zend\View\Model\FeedModel' => array('application/rss+xml'),
47+
);
48+
49+
/**
50+
* Get a list of groups that are nearest to the given coordinates.
51+
*
52+
* Coordinates are given via the parameters <var>lat</var and <var>lon</var>,
53+
* the maximum distance from the current location is given via <var>distance</var>
54+
* and the maximum number of entries is given via the parameter <var>max</var>
55+
*
56+
* This method will then return the maximum number of entries within the given
57+
* range. If less than the maximum number of entries is found within the distance
58+
* only that number of entries will be returned. When no distance is given or the
59+
* distance is given as "0" the maximum number of entries will be retrieved.
60+
*
61+
* @return mixed|\Zend\View\Model\ModelInterface
62+
* @throws \UnexpectedValueException
63+
*/
64+
public function nextGroupsAction()
65+
{
66+
$adapter = $this->getAdapter();
67+
$response = $this->getResponse();
68+
$viewModel = $this->getViewModel();
69+
70+
Json::$useBuiltinEncoderDecoder = true;
71+
72+
// Get Latitude, Longitude, distance and/or number of groups to retrieve
73+
$latitude = $this->params()->fromQuery('latitude');
74+
$longitude = $this->params()->fromQuery('longitude');
75+
$distance = $this->params()->fromQuery('distance', null);
76+
$number = $this->params()->fromQuery('count', null);
77+
78+
79+
//$this->sorter = $this->getServiceManager('Phpug\Sorter\Usergroup\Distance');
80+
$groups = $this->findGroupsWithinRangeAndDistance($latitude, $longitude, $distance, $number);
81+
$return = array(
82+
'currentLocation' => array(
83+
'latitude' => $latitude,
84+
'longitude' => $longitude,
85+
),
86+
'groups' => array(),
87+
);
88+
// $hydrator = $this->getServiceManager('Phpug\Hydrator\Usergroup');
89+
foreach ($groups as $group) {
90+
$grp = array(
91+
'name' => $group[0]->getName(),
92+
'latitude' => $group[0]->getLatitude(),
93+
'longitude' => $group[0]->getLongitude(),
94+
'shortname' => $group[0]->getShortname(),
95+
'distance' => $group['distance'],
96+
'icalendar_url' => $group[0]->getIcalendar_url(),
97+
'url' => $group[0]->getUrl(),
98+
'contacts' => array(),
99+
'uri' => '',
100+
);
101+
102+
foreach ($group[0]->getContacts() as $contact) {
103+
$grp['contacts'][] = array(
104+
'service' => $contact->getServiceName(),
105+
'name' => $contact->getName(),
106+
'uri' => $contact->getUrl(),
107+
);
108+
}
109+
110+
$return['groups'][] = $grp;
111+
}
112+
$viewModel->setVariable('groups', $return);
113+
114+
return $viewModel;
115+
}
116+
117+
protected function getAdapter()
118+
{
119+
$format = $this->params()->fromQuery('format', null);
120+
switch ($format) {
121+
case 'sphp':
122+
$contentType = 'text/plain';
123+
$adapter = '\Zend\Serializer\Adapter\PhpSerialize';
124+
break;
125+
case 'json':
126+
default:
127+
$contentType = 'application/json';
128+
$adapter = '\Zend\Serializer\Adapter\Json';
129+
break;
130+
}
131+
$this->getResponse()->getHeaders()->addHeaderLine('Content-Type', $contentType);
132+
133+
return new $adapter;
134+
}
135+
136+
protected function getViewModel()
137+
{
138+
return $this->acceptableViewModelSelector($this->acceptCriteria);
139+
}
140+
141+
protected function findGroupsWithinRangeAndDistance($lat, $lon, $distance = null, $number = null)
142+
{
143+
/** @var \Doctrine\ORM\EntityManager $em */
144+
$em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
145+
DistanceFrom::setLatitudeField('latitude');
146+
DistanceFrom::setLongitudeField('longitude');
147+
DistanceFrom::setRadius(6367);
148+
$em->getConfiguration()->addCustomNumericFunction('DISTANCEFROM', 'Phpug\ORM\Query\AST\Functions\DistanceFrom');
149+
150+
$qs = 'SELECT p, DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') AS distance FROM \Phpug\Entity\Usergroup p WHERE p.state = 1 ';
151+
152+
153+
if ($distance) {
154+
$qs .= ' AND DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') <= ' . (float) $distance;
155+
}
156+
157+
$qs .= ' ORDER BY distance';
158+
159+
$query = $em->createQuery($qs);
160+
if ($number) {
161+
$query->setMaxResults($number);
162+
}
163+
164+
return $query->getResult();
165+
166+
167+
}
168+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
/**
3+
* Copyright (c)2015-2015 heiglandreas
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIBILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*
23+
* @category
24+
* @author Andreas Heigl<[email protected]>
25+
* @copyright ©2015-2015 Andreas Heigl
26+
* @license http://www.opesource.org/licenses/mit-license.php MIT-License
27+
* @version 0.0
28+
* @since 25.03.15
29+
* @link https://github.com/heiglandreas/
30+
*/
31+
32+
namespace Phpug\ORM\Query\AST\Functions;
33+
34+
35+
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
36+
use Doctrine\ORM\Query\Lexer;
37+
38+
/**
39+
* DistanceFromFunction ::= "DISTANCEFROM" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
40+
*
41+
* This distance is calculated acording to http://www.movable-type.co.uk/scripts/gis-faq-5.1.html
42+
*
43+
* @see http://www.movable-type.co.uk/scripts/gis-faq-5.1.html
44+
*/
45+
class DistanceFrom extends FunctionNode
46+
{
47+
protected static $latitudeField = 'latitude';
48+
protected static $longitudeField = 'longitude';
49+
50+
/**
51+
* This is the radius of the sphere.
52+
*
53+
* For the earth use 6367 for distance results in kilometers or
54+
* ?? for results in miles.
55+
*
56+
* @var float
57+
*/
58+
protected static $radius = 6367;
59+
60+
protected $latitude = null;
61+
62+
protected $longitude = null;
63+
64+
65+
/**
66+
* @param \Doctrine\ORM\Query\SqlWalker $sqlWalker
67+
*
68+
* @return string
69+
*/
70+
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
71+
{
72+
return sprintf(
73+
'(asin(sqrt(pow(sin((%2$s*0.017453293-%4$f*0.017453293)/2),2) + cos(%2$s*0.017453293) * cos(%4$f*0.017453293) * pow(sin((%3$s*0.017453293-%5$f*0.017453293)/2),2))) * %1$f)',
74+
self::getRadius(),
75+
self::getLatitudeField(),
76+
self::getLongitudeField(),
77+
$this->latitude->dispatch($sqlWalker),
78+
$this->longitude->dispatch($sqlWalker)
79+
);
80+
}
81+
82+
/**
83+
* @param \Doctrine\ORM\Query\Parser $parser
84+
*
85+
* @return void
86+
*/
87+
public function parse(\Doctrine\ORM\Query\Parser $parser)
88+
{
89+
$parser->match(Lexer::T_IDENTIFIER);
90+
$parser->match(Lexer::T_OPEN_PARENTHESIS);
91+
$this->latitude = $parser->ArithmeticPrimary();
92+
$parser->match(Lexer::T_COMMA);
93+
$this->longitude = $parser->ArithmeticPrimary();
94+
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
95+
96+
}
97+
98+
public static function getRadius()
99+
{
100+
return self::$radius;
101+
}
102+
103+
public static function getLatitudeField()
104+
{
105+
return self::$latitudeField;
106+
}
107+
108+
public static function getLongitudeField()
109+
{
110+
return self::$longitudeField;
111+
}
112+
113+
public static function setLongitudeField($longitude)
114+
{
115+
self::$longitudeField = (string) $longitude;
116+
}
117+
118+
public static function setLatitudeField($latitude)
119+
{
120+
self::$latitudeField = (string) $latitude;
121+
}
122+
123+
public static function setRadius($radius)
124+
{
125+
self::$radius = (float) $radius;
126+
}
127+
128+
129+
}

0 commit comments

Comments
 (0)