PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/app/modules/map/lib/MapSearch.php

http://github.com/modolabs/Kurogo-Mobile-Web
PHP | 217 lines | 165 code | 35 blank | 17 comment | 22 complexity | 5726531b52815e3ad49dbc196ea7585d MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1
  1. <?php
  2. /*
  3. * Copyright © 2010 - 2013 Modo Labs Inc. All rights reserved.
  4. *
  5. * The license governing the contents of this file is located in the LICENSE
  6. * file located at the root directory of this distribution. If the LICENSE file
  7. * is missing, please contact sales@modolabs.com.
  8. *
  9. */
  10. class MapSearch extends DataRetriever {
  11. protected $searchResults;
  12. protected $resultCount;
  13. protected $feeds;
  14. protected $feedGroup;
  15. protected $searchParams;
  16. protected $searchMode;
  17. const SEARCH_MODE_TEXT = 0;
  18. const SEARCH_MODE_NEARBY = 1;
  19. public function __construct($feeds) {
  20. $this->setFeedData($feeds);
  21. }
  22. public function setFeedData($feeds) {
  23. $this->feeds = $feeds;
  24. }
  25. public function init($args) {
  26. parent::init($args);
  27. $this->setCacheGroup(get_class($this));
  28. }
  29. public function setFeedGroup($feedGroup) {
  30. $this->feedGroup = $feedGroup;
  31. }
  32. public function getSearchResults() {
  33. return $this->searchResults;
  34. }
  35. public function getResultCount() {
  36. return $this->resultCount;
  37. }
  38. public function retrieveResponse() {
  39. $response = $this->initResponse();
  40. switch ($this->searchMode) {
  41. case self::SEARCH_MODE_NEARBY:
  42. if (is_array($this->searchParams)) {
  43. list($center, $tolerance, $maxItems, $dataSource) = $this->searchParams;
  44. $response->setResponse(
  45. $this->doSearchByProximity($center, $tolerance, $maxItems, $dataSource));
  46. }
  47. break;
  48. case self::SEARCH_MODE_TEXT:
  49. default:
  50. if (is_string($this->searchParams)) {
  51. $response->setResponse(
  52. $this->doSearchByText($this->searchParams));
  53. }
  54. break;
  55. }
  56. return $response;
  57. }
  58. // tolerance specified in meters
  59. public function searchByProximity($center, $tolerance=1000, $maxItems=0, $dataSource=null) {
  60. $this->searchMode = self::SEARCH_MODE_NEARBY;
  61. $this->setContext('mode', 'nearby');
  62. $cacheKey = "c={$center['lat']},{$center['lon']}&t={$tolerance}&m={$maxItems}";
  63. if (isset($this->feedGroup) && strlen($this->feedGroup)) {
  64. $cacheKey .= "&g={$this->feedGroup}";
  65. }
  66. if (isset($dataSource)) {
  67. $id = $dataSource->getId();
  68. $cacheKey .= "&d={$id}";
  69. }
  70. $this->setCacheKey($cacheKey);
  71. $this->searchParams = array($center, $tolerance, $maxItems, $dataSource);
  72. $this->searchResults = $this->getData();
  73. if ($this->searchResults === null) {
  74. $this->searchResults = array();
  75. }
  76. $this->resultCount = count($this->searchResults);
  77. return $this->searchResults;
  78. }
  79. public function searchCampusMap($query) {
  80. $this->searchMode = self::SEARCH_MODE_TEXT;
  81. $this->setContext('mode', 'text');
  82. $cacheKey = "q={$query}";
  83. if (isset($this->feedGroup) && strlen($this->feedGroup)) {
  84. $cacheKey .= "&g={$this->feedGroup}";
  85. }
  86. $this->setCacheKey($cacheKey);
  87. $this->searchParams = $query;
  88. $this->searchResults = $this->getData();
  89. if ($this->searchResults === null) {
  90. $this->searchResults = array();
  91. }
  92. $this->resultCount = count($this->searchResults);
  93. return $this->searchResults;
  94. }
  95. protected function doSearchByProximity($center, $tolerance=1000, $maxItems=0, $controller=null) {
  96. $resultsByDistance = array();
  97. $controllers = array();
  98. if ($controller !== null) {
  99. $controllers[] = $controller;
  100. } else {
  101. foreach ($this->feeds as $categoryID => $feedData) {
  102. $feedData['group'] = $this->feedGroup;
  103. $controller = mapModelFromFeedData($feedData);
  104. if ($controller->canSearch()) { // respect config settings
  105. $controllers[] = $controller;
  106. }
  107. }
  108. }
  109. // keep track of duplicate placemarks
  110. $unique = array();
  111. foreach ($controllers as $controller) {
  112. try {
  113. $results = $controller->searchByProximity($center, $tolerance, $maxItems);
  114. // merge arrays manually since keys are numeric
  115. foreach($results as $placemark) {
  116. $toCenter = $placemark->getGeometry()->getCenterCoordinate();
  117. // assume if placemarks have the same lat/lon
  118. // and title then they are the same place
  119. $testString = $toCenter['lat'].$toCenter['lon'].$placemark->getTitle();
  120. if (in_array($testString, $unique)) {
  121. continue;
  122. }
  123. $unique[] = $testString;
  124. $distance = greatCircleDistance($center['lat'], $center['lon'], $toCenter['lat'], $toCenter['lon']);
  125. $placemark->setField('distance', $distance);
  126. // avoid distance collisions
  127. if (isset($resultsByDistance[$distance])) {
  128. $distance++;
  129. while(isset($resultsByDistance[$distance])) {
  130. $distance++;
  131. }
  132. }
  133. $resultsByDistance[$distance] = $placemark;
  134. }
  135. } catch (KurogoDataServerException $e) {
  136. Kurogo::log(LOG_WARNING, 'encountered KurogoDataServerException for feed config: ' . print_r($feedData, true) . $e->getMessage(), 'maps');
  137. }
  138. }
  139. ksort($resultsByDistance);
  140. if ($maxItems && count($resultsByDistance) > $maxItems) {
  141. array_splice($resultsByDistance, $maxItems + 1);
  142. }
  143. return array_values($resultsByDistance);
  144. }
  145. // sort by length of alias so the most specific (longer) ones are first
  146. protected function sortAliases($a, $b) {
  147. $lenA = strlen($a);
  148. $lenB = strlen($b);
  149. if ($lenA == $lenB) {
  150. return 0;
  151. }
  152. return ($lenA > $lenB) ? -1 : 1;
  153. }
  154. protected function doSearchByText($query) {
  155. $allResults = array();
  156. foreach ($this->feeds as $id => $feedData) {
  157. //use aliases to remap search query
  158. $aliases = Kurogo::arrayVal($feedData, 'ALIASES', array());
  159. uksort($aliases, array($this,'sortAliases'));
  160. foreach ($aliases as $alias=>$map) {
  161. if (stripos($query, $alias)!== false) {
  162. //use the alias's value for searching
  163. $query = $map;
  164. break;
  165. }
  166. }
  167. if ($this->feedGroup) {
  168. $feedData['group'] = $this->feedGroup;
  169. }
  170. $controller = mapModelFromFeedData($feedData);
  171. if ($controller->canSearch()) {
  172. try {
  173. $results = $controller->search($query);
  174. $allResults = array_merge($allResults, $results);
  175. } catch (KurogoDataServerException $e) {
  176. Kurogo::log(LOG_WARNING,'encountered KurogoDataServerException for feed config: ' . print_r($feedData, true) . $e->getMessage(), 'maps');
  177. }
  178. }
  179. }
  180. return $allResults;
  181. }
  182. }