diff --git a/src/module/Phpug/config/module.config.php b/src/module/Phpug/config/module.config.php index 3d4526e..de699a4 100644 --- a/src/module/Phpug/config/module.config.php +++ b/src/module/Phpug/config/module.config.php @@ -444,6 +444,7 @@ 'Phpug\Api\Rest\Twitter' => 'Phpug\Api\Rest\TwitterController', 'Phpug\Api\v1\Usergroup' => 'Phpug\Api\v1\UsergroupController', 'Phpug\Api\v1\Calendar' => 'Phpug\Api\v1\CalendarController', + 'Phpug\Api\v1\Location' => 'Phpug\Api\v1\LocationController', 'Phpug\Controller\MentoringController' => 'Phpug\Controller\MentoringController', 'Phpug\Controller\EventCacheController' => 'Phpug\Controller\EventCacheController', 'Phpug\Controller\TwitterController' => 'Phpug\Controller\TwitterController', diff --git a/src/module/Phpug/src/Phpug/Api/v1/CalendarController.php b/src/module/Phpug/src/Phpug/Api/v1/CalendarController.php index 03aab45..9478d0c 100644 --- a/src/module/Phpug/src/Phpug/Api/v1/CalendarController.php +++ b/src/module/Phpug/src/Phpug/Api/v1/CalendarController.php @@ -31,6 +31,7 @@ namespace Phpug\Api\v1; +use Phpug\ORM\Query\AST\Functions\DistanceFrom; use Zend\Mvc\Controller\AbstractActionController; use Sabre\VObject; use Zend\Json\Json; @@ -64,10 +65,16 @@ public function listAction() $em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); $result = $em->getRepository('Phpug\Entity\Cache')->findBy(array('type' => 'event')); $calendar = new VObject\Component\VCalendar(); + $affectedUGs = $this->findGroupsWithinRangeAndDistance(); foreach ($result as $cal) { if (! $cal->getGRoup()) { continue; } + + if ($affectedUGs && ! in_array($cal->getGroup()->getShortname(), $affectedUGs)) { + continue; + } + try { $ical = VObject\Reader::read($cal->getCache()); foreach ($ical->children as $event) { @@ -81,7 +88,7 @@ public function listAction() } catch(\Exception $e){} } - + $viewModel = $this->getViewModel(); return $viewModel->setVariable('calendar', new \Phpug\Wrapper\SabreVCalendarWrapper($calendar)); @@ -114,4 +121,43 @@ protected function setAcceptHeaderAccordingToParameters() return $this; } + + protected function findGroupsWithinRangeAndDistance() + { + $lat = $this->params()->fromQuery('latitude', null); + $lon = $this->params()->fromQuery('longitude', null); + $distance = $this->params()->fromQuery('distance', null); + $number = $this->params()->fromQuery('count', null); + + if (! $lat || ! $lon) { + return array(); + } + /** @var \Doctrine\ORM\EntityManager $em */ + $em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + DistanceFrom::setLatitudeField('latitude'); + DistanceFrom::setLongitudeField('longitude'); + DistanceFrom::setRadius(6367); + $em->getConfiguration()->addCustomNumericFunction('DISTANCEFROM', 'Phpug\ORM\Query\AST\Functions\DistanceFrom'); + + $qs = 'SELECT p, DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') AS distance FROM \Phpug\Entity\Usergroup p WHERE p.state = 1 '; + + + if ($distance) { + $qs .= ' AND DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') <= ' . (float) $distance; + } + + $qs .= ' ORDER BY distance'; + + $query = $em->createQuery($qs); + if ($number) { + $query->setMaxResults($number); + } + + $res = array(); + foreach ($query->getResult() as $result) { + $res[] = $result[0]->getShortname(); + } + + return $res; + } } \ No newline at end of file diff --git a/src/module/Phpug/src/Phpug/Api/v1/LocationController.php b/src/module/Phpug/src/Phpug/Api/v1/LocationController.php new file mode 100644 index 0000000..59b1b32 --- /dev/null +++ b/src/module/Phpug/src/Phpug/Api/v1/LocationController.php @@ -0,0 +1,168 @@ + + * @copyright ©2014-2014 Andreas Heigl + * @license http://www.opesource.org/licenses/mit-license.php MIT-License + * @version 0.0 + * @since 04.02.14 + * @link https://github.com/heiglandreas/ + */ + +namespace Phpug\Api\v1; + +use Phpug\ORM\Query\AST\Functions\DistanceFrom; +use Zend\Mvc\Controller\AbstractActionController; +use Sabre\VObject; +use Zend\Json\Json; + +class LocationController extends AbstractActionController +{ + private $acceptCriteria = array( + 'Zend\View\Model\JsonModel' => array( + 'application/json', + 'text/html', + ), + 'Zend\View\Model\FeedModel' => array('application/rss+xml'), + ); + + /** + * Get a list of groups that are nearest to the given coordinates. + * + * Coordinates are given via the parameters latlon, + * the maximum distance from the current location is given via distance + * and the maximum number of entries is given via the parameter max + * + * This method will then return the maximum number of entries within the given + * range. If less than the maximum number of entries is found within the distance + * only that number of entries will be returned. When no distance is given or the + * distance is given as "0" the maximum number of entries will be retrieved. + * + * @return mixed|\Zend\View\Model\ModelInterface + * @throws \UnexpectedValueException + */ + public function nextGroupsAction() + { + $adapter = $this->getAdapter(); + $response = $this->getResponse(); + $viewModel = $this->getViewModel(); + + Json::$useBuiltinEncoderDecoder = true; + + // Get Latitude, Longitude, distance and/or number of groups to retrieve + $latitude = $this->params()->fromQuery('latitude'); + $longitude = $this->params()->fromQuery('longitude'); + $distance = $this->params()->fromQuery('distance', null); + $number = $this->params()->fromQuery('count', null); + + + //$this->sorter = $this->getServiceManager('Phpug\Sorter\Usergroup\Distance'); + $groups = $this->findGroupsWithinRangeAndDistance($latitude, $longitude, $distance, $number); + $return = array( + 'currentLocation' => array( + 'latitude' => $latitude, + 'longitude' => $longitude, + ), + 'groups' => array(), + ); + // $hydrator = $this->getServiceManager('Phpug\Hydrator\Usergroup'); + foreach ($groups as $group) { + $grp = array( + 'name' => $group[0]->getName(), + 'latitude' => $group[0]->getLatitude(), + 'longitude' => $group[0]->getLongitude(), + 'shortname' => $group[0]->getShortname(), + 'distance' => $group['distance'], + 'icalendar_url' => $group[0]->getIcalendar_url(), + 'url' => $group[0]->getUrl(), + 'contacts' => array(), + 'uri' => '', + ); + + foreach ($group[0]->getContacts() as $contact) { + $grp['contacts'][] = array( + 'service' => $contact->getServiceName(), + 'name' => $contact->getName(), + 'uri' => $contact->getUrl(), + ); + } + + $return['groups'][] = $grp; + } + $viewModel->setVariable('groups', $return); + + return $viewModel; + } + + protected function getAdapter() + { + $format = $this->params()->fromQuery('format', null); + switch ($format) { + case 'sphp': + $contentType = 'text/plain'; + $adapter = '\Zend\Serializer\Adapter\PhpSerialize'; + break; + case 'json': + default: + $contentType = 'application/json'; + $adapter = '\Zend\Serializer\Adapter\Json'; + break; + } + $this->getResponse()->getHeaders()->addHeaderLine('Content-Type', $contentType); + + return new $adapter; + } + + protected function getViewModel() + { + return $this->acceptableViewModelSelector($this->acceptCriteria); + } + + protected function findGroupsWithinRangeAndDistance($lat, $lon, $distance = null, $number = null) + { + /** @var \Doctrine\ORM\EntityManager $em */ + $em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + DistanceFrom::setLatitudeField('latitude'); + DistanceFrom::setLongitudeField('longitude'); + DistanceFrom::setRadius(6367); + $em->getConfiguration()->addCustomNumericFunction('DISTANCEFROM', 'Phpug\ORM\Query\AST\Functions\DistanceFrom'); + + $qs = 'SELECT p, DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') AS distance FROM \Phpug\Entity\Usergroup p WHERE p.state = 1 '; + + + if ($distance) { + $qs .= ' AND DISTANCEFROM(' . (float) $lat . ',' . (float) $lon . ') <= ' . (float) $distance; + } + + $qs .= ' ORDER BY distance'; + + $query = $em->createQuery($qs); + if ($number) { + $query->setMaxResults($number); + } + + return $query->getResult(); + + + } +} \ No newline at end of file diff --git a/src/module/Phpug/src/Phpug/ORM/Query/AST/Functions/DistanceFrom.php b/src/module/Phpug/src/Phpug/ORM/Query/AST/Functions/DistanceFrom.php new file mode 100644 index 0000000..e9a1b5c --- /dev/null +++ b/src/module/Phpug/src/Phpug/ORM/Query/AST/Functions/DistanceFrom.php @@ -0,0 +1,129 @@ + + * @copyright ©2015-2015 Andreas Heigl + * @license http://www.opesource.org/licenses/mit-license.php MIT-License + * @version 0.0 + * @since 25.03.15 + * @link https://github.com/heiglandreas/ + */ + +namespace Phpug\ORM\Query\AST\Functions; + + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\Lexer; + +/** + * DistanceFromFunction ::= "DISTANCEFROM" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * This distance is calculated acording to http://www.movable-type.co.uk/scripts/gis-faq-5.1.html + * + * @see http://www.movable-type.co.uk/scripts/gis-faq-5.1.html + */ +class DistanceFrom extends FunctionNode +{ + protected static $latitudeField = 'latitude'; + protected static $longitudeField = 'longitude'; + + /** + * This is the radius of the sphere. + * + * For the earth use 6367 for distance results in kilometers or + * ?? for results in miles. + * + * @var float + */ + protected static $radius = 6367; + + protected $latitude = null; + + protected $longitude = null; + + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return sprintf( + '(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)', + self::getRadius(), + self::getLatitudeField(), + self::getLongitudeField(), + $this->latitude->dispatch($sqlWalker), + $this->longitude->dispatch($sqlWalker) + ); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $this->latitude = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->longitude = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + + } + + public static function getRadius() + { + return self::$radius; + } + + public static function getLatitudeField() + { + return self::$latitudeField; + } + + public static function getLongitudeField() + { + return self::$longitudeField; + } + + public static function setLongitudeField($longitude) + { + self::$longitudeField = (string) $longitude; + } + + public static function setLatitudeField($latitude) + { + self::$latitudeField = (string) $latitude; + } + + public static function setRadius($radius) + { + self::$radius = (float) $radius; + } + + +} \ No newline at end of file diff --git a/src/module/Phpug/src/Phpug/Wrapper/SabreVCalendarWrapper.php b/src/module/Phpug/src/Phpug/Wrapper/SabreVCalendarWrapper.php index 8083a9b..b9c4e92 100644 --- a/src/module/Phpug/src/Phpug/Wrapper/SabreVCalendarWrapper.php +++ b/src/module/Phpug/src/Phpug/Wrapper/SabreVCalendarWrapper.php @@ -62,6 +62,7 @@ public function getEvents(\DateInterval $interval) $now = new \DateTime(); $then = (new \DateTime())->add($interval); $this->object->expand($now, $then); + $return = array(); foreach ($this->object->children as $item) { if (! $item instanceof VEvent) { continue;