loadComponent('RequestHandler'); // configure the request handler to deal with json encoding. $this->RequestHandler->config('inputTypeMap.json', ['json_decode', true]); $this->api_key = SimpleMapsComponent::getGoogleAPIKey(); // allow simple access to location db. $this->loadModel ( 'Locations' ); } /** * Index method * * @return \Cake\Network\Response|null */ public function index() { $categories = $this->paginate($this->Categories); $this->set(compact('categories')); $this->set('_serialize', ['categories']); } /** * View method * * @param string|null $id Category id. * @return \Cake\Network\Response|null * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function view($id = null) { $category = $this->Categories->get($id, [ 'contain' => ['Locations'] ]); $this->set('category', $category); $this->set('_serialize', ['category']); // query all the locations belonging to this category but only retrieve part of the info. $locationBits = $this->Categories->CategoriesLocations->find('list', [ 'conditions' => [ 'category_id' => $id ], 'contain' => ['Locations'], 'valueField' => function ($row) { return $row->location->get('lat') . ',' . $row->location->get('lng') . ' ' . $row->location->get('location'); } ]); $this->set('locationBits', $locationBits); } /** * queries the locations to display on a map given an "id" to lookup in categories table. */ public function map($id = null) { $category = $this->Categories->get($id, [ 'contain' => ['Locations'] ]); $locationsFound = $this->Categories->getLocationsInCategory($id); $this->set('locationsInCategory', $locationsFound); // grab the flags in requests. $parms = $this->request->getQueryParams(); // we will cluster by default, but they can also pass the clustering flag with 0 or 1. $clustering = (array_key_exists('clustering', $parms) && ($parms['clustering'] == '1')) || !array_key_exists('clustering', $parms); //Log::Debug('clustering is: ' . var_export($clustering, true)); // set up the selected items in our config chooser. $selectedList = []; if ($clustering) { array_push($selectedList, 'clustering'); } $this->set('selectedList', $selectedList); // pass the pre-loading function in for the map element. $this->set('dbProcessor', $this); $this->set('preloader', '$this->MapDisplay->addMarkers($dbProcessor, $locationsInCategory, $category );'); if ($this->request->is(['post', 'put'])) { //, 'patch'])) { $postData = $this->request->getData('view_options')['_ids']; $new_clustering = is_array($postData) && in_array('clustering', $postData); if ($clustering != $new_clustering) { $this->redirect(['action' => 'map', '0' => $id, '?' => [ 'clustering' => $new_clustering, ] ]); } } $this->set('category', $category); // $this->set('_serialize', ['category']); } /** * processes a location row by extracting the important information into an array. * separates the db structure from the pieces needed for google maps. */ public function processRow(& $location_row) { return [ 'lat' => $location_row ['location'] ['lat'], 'lng' => $location_row ['location'] ['lng'], 'title' => $location_row ['location'] ['name'], // kludge below adds extra space on content, since someone is not left justifying these. 'content' => h ( $location_row ['location'] ['location'] ) . '      ', ]; } public function extractCategoryImage(& $category) { return $category->image; } /** * attempts to jump to the center of the category, given a particular interpretation of the * category. if possible we will decide that the category is a state name, and then attempt * to jump to the center of that state. * will also show markers in the category. */ public function center($id = null) { $category = $this->Categories->get($id, [ 'contain' => ['Locations'] ]); // grab the flags in requests. $parms = $this->request->getQueryParams(); // we will cluster by default, but they can also pass the clustering flag with 0 or 1. $clustering = (array_key_exists('clustering', $parms) && ($parms['clustering'] == '1')) || !array_key_exists('clustering', $parms); //Log::Debug('clustering is: ' . var_export($clustering, true)); // set up the selected items in our config chooser. $selectedList = []; if ($clustering) { array_push($selectedList, 'clustering'); } $this->set('selectedList', $selectedList); $state = null; // interpret the category name as a state if possible. if (substr($category->name, 0, 3) == 'US-') { // bingo, we have a US prefix on the string, so decide what state they mean. $state = substr($category->name, 3, 2); } // perform the lookup to get the readable name. otherwise a state like indiana (IN) will // not get geocoded properly. probably others as well. $state = SimpleMapsComponent::lookupStateFromAbbreviation($state); $this->set('state', $state); Log::debug('state was found as: ' . $state); if ($state !== null) { $centeredPoint = SimpleMapsComponent::geocode($state . " USA", [ 'key' => $this->api_key ]); $mapCenter = [ 'lat' => $centeredPoint[0], 'lng' => $centeredPoint[1] ] ; $this->set('mapCenter', $mapCenter); Log::Debug('computed a map center of: ' . var_export($mapCenter, true)); } // thoughts on algorithm: is the center of the state according to google what we want to rely on? // we could at least start there, record the lat/longs and then edit them later as we decide. $radius = null; $lat = null; $long = null; if ($mapCenter) { $radius = 40; // miles from center to show markers. $lat = $mapCenter['lat']; $long = $mapCenter['lng']; } Log::Debug('given lat=' . $lat . ' long='. $long . ' radius=' . $radius); Log::Debug('loading locations in category within radius'); // compute the lat/long bounding box for our search. $bounds = SimpleMapsComponent::calculateLatLongBoundingBox ( $lat, $long, $radius ); if (! $bounds) { Log::Debug("failed to calculate the bounding box!"); } else { Log::Debug("bounding box: " . var_export($bounds, true)); } // query all the locations belonging to this category within the box. $locationsInCategory = $this->Categories->getLocationsInCategoryInBox($id, $bounds [0], $bounds [1], $bounds [2], $bounds [3] ); $this->set('locationsInCategory', $locationsInCategory); $this->set('dbProcessor', $this); $this->set('preloader', '$this->MapDisplay->addMarkers($dbProcessor, $locationsInCategory, $category );'); if ($this->request->is(['post', 'put'])) { //, 'patch'])) { $postData = $this->request->getData('view_options')['_ids']; $new_clustering = is_array($postData) && in_array('clustering', $postData); if ($clustering != $new_clustering) { $this->redirect(['action' => 'map', '0' => $id, '?' => [ 'clustering' => $new_clustering, ] ]); } } $this->set('category', $category); // $this->set('_serialize', ['category','selectedList']); } /** * Add method * * @return \Cake\Network\Response|null Redirects on successful add, renders view otherwise. */ public function add() { $category = $this->Categories->newEntity(); if ($this->request->is('post')) { $category = $this->Categories->patchEntity($category, $this->request->getData()); if ($this->Categories->save($category)) { $this->Flash->success(__('The category has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The category could not be saved. Please, try again.')); } $this->set(compact('category')); $this->set('_serialize', ['category']); } /** * Edit method * * @param string|null $id Category id. * @return \Cake\Network\Response|null Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $category = $this->Categories->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $category = $this->Categories->patchEntity($category, $this->request->getData()); if ($this->Categories->save($category)) { $this->Flash->success(__('The category has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The category could not be saved. Please, try again.')); } $this->set(compact('category')); $this->set('_serialize', ['category']); } /** * Delete method * * @param string|null $id Category id. * @return \Cake\Network\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $category = $this->Categories->get($id); if ($this->Categories->delete($category)) { $this->Flash->success(__('The category has been deleted.')); } else { $this->Flash->error(__('The category could not be deleted. Please, try again.')); } return $this->redirect(['action' => 'index']); } /** * provides ajax friendly responses. expects to be passed 'category' in post data. * requires bounding box for lookup be passed as 'sw_lat', 'sw_lng', 'ne_lat', 'ne_lng'. */ public function lookupajax() { $reqData = $this->request->getData(); Log::Debug('got to lookup with data: ' . var_export($reqData, true)); // check that this is a post method, since we don't support anything else. if (! $this->request->is(['post'])) { die('this is a post method'); } if (array_key_exists('action', $reqData)) { $action = $reqData['action']; if (strcasecmp($action, 'lookupBox') == 0) { $this->findLocationsInCategoryWithinBounds($reqData); } else if (strcasecmp($action, 'getInfo') == 0) { $this->getInfoOnLocation($reqData); } else { die('lookupajax call was given unknown action: ' . $action); } } else { die('lookupajax call has no action specified'); } } public function findLocationsInCategoryWithinBounds($reqData) { if (array_key_exists('category', $reqData)) { $id = $reqData['category']; } else { // throw an exception here? $message = 'failed to find category id in request data'; Log::Debug($message); die($message); } $category = $this->Categories->get($id, [ 'contain' => ['Locations'] ]); $this->set('category', $category); if (array_key_exists('sw_lat', $reqData)) { $sw_lat = $reqData['sw_lat']; } if (array_key_exists('sw_lng', $reqData)) { $sw_lng = $reqData['sw_lng']; } if (array_key_exists('ne_lat', $reqData)) { $ne_lat = $reqData['ne_lat']; } if (array_key_exists('ne_lng', $reqData)) { $ne_lng = $reqData['ne_lng']; } //temp! fails over to using whole range. if ($sw_lat === null) { $sw_lat = -90; } if ($sw_lng === null) { $sw_lng = -180; } if ($ne_lat === null) { $ne_lat = 90; } if ($ne_lng === null) { $ne_lng = 180; } $start = null; if (array_key_exists('start', $reqData)) { $start = $reqData['start']; } $end = null; if (array_key_exists('end', $reqData)) { $end = $reqData['end']; } // lookup the locations inside that box and store for view. $locationsToSerialize = $this->Categories->getChewedLocationsInCategoryInBox($id, $sw_lat, $sw_lng, $ne_lat, $ne_lng, $start, $end); //Log::debug('before encoding, php array looks like: ' . var_export($locationsToSerialize, true)); Log::Debug("returning json now..."); $encoded = json_encode($locationsToSerialize); // Log::debug('encoded json is: ' . var_export($encoded, true)); die($encoded); } public function getInfoOnLocation($reqData) { if (array_key_exists('id', $reqData)) { $id = $reqData['id']; } else { // throw an exception here? $message = 'failed to find location id in request data'; Log::Debug($message); die($message); } $location = $this->Locations->get($id, [ ]); return die(json_encode($location)); } }