1<?php 2/* 3 * Copyright (c) 2008-2011 Mark C. Prins <mc.prins@gmail.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/** 19 * DokuWiki Plugin spatialhelper (index Helper Component) 20 * 21 * @license BSD 22 * @author Mark Prins 23 */ 24 25// must be run within Dokuwiki 26if (!defined('DOKU_INC')) die(); 27 28if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 29require_once DOKU_PLUGIN.'spatialhelper/Geohash.php'; 30 31class helper_plugin_spatialhelper_index extends DokuWiki_Plugin { 32 /** 33 * directory for index files 34 * @var string 35 */ 36 var $idx_dir = ''; 37 38 /** 39 * spatial index, well lookup list/array so we can do spatial queries. 40 * entries should be: array("geohash" => {"id1","id3",}) 41 * @var array 42 */ 43 var $spatial_idx = array(); 44 45 /** 46 * Constructor, initialises the spatial index. 47 */ 48 function helper_plugin_spatialhelper_index() { 49 dbglog('initialize','--- spatialhelper_index::helper_plugin_spatialhelper_index ---'); 50 global $conf; 51 $this->idx_dir = $conf['indexdir']; 52 // test if there is an index, if not build one for the wiki 53 if (!@file_exists($this->idx_dir.'/spatial.idx')){ 54 dbglog('Creating spatial index'); 55 // creates and stores the index 56 $this->generateSpatialIndex(); 57 dbglog('Done creating spatial index'); 58 } else { 59 dbglog('loading spatial index'); 60 $this->spatial_idx = unserialize(io_readFile($this->idx_dir.'/spatial.idx', false)); 61 dbglog($this->spatial_idx,'done loading spatial index'); 62 } 63 } 64 65 /** 66 * Update the spatial index for the page. 67 * @param string $id the document ID 68 */ 69 function updateSpatialIndex($id) { 70 dbglog('reading getoags','--- spatialhelper_index::updateSpatialIndex ---'); 71 $geotags = p_get_metadata($id, 'geo'); 72 //TODO we need just lat/lon, so check for those 73 // (and maybe validate the values -90/90 and -180/180) 74 if(empty($geotags)) { 75 dbglog("No geotags found for page $id, done"); 76 return true; 77 } 78 dbglog($geotags,"Geotags found for page $id"); 79 $geohash = Geohash::encode($geotags['lat'],$geotags['lon']); 80 dbglog('Update index for geohash: '.$geohash); 81 82 $pageIds = array(); 83 // check index for key/geohash 84 if (!array_key_exists($geohash, $this->spatial_idx )) { 85 dbglog('Geohash not in index, just add.'); 86 $pageIds[] = $id; 87 } else { 88 dbglog('Geohash for document is in index, find it.'); 89 // check the index for document 90 $knownHashes = $this->findHashesForId($id, $this->spatial_idx); 91 if(empty($knownHashes)){ 92 dbglog("No index record found for document $id, just add"); 93 $pageIds=$this->spatial_idx[$geohash]; 94 $pageIds[] = $id; 95 } 96 // TODO shortcut, need to make sure there is only one element, if not the index is corrupt 97 $knownHash=$knownHashes[0]; 98 99 if ($knownHash == $geohash){ 100 dbglog("Document $id was found in index and has the same geohash, nothing to do."); 101 return true; 102 } 103 104 if (!empty($knownHash)) { 105 dbglog("Document $id was found in index but has different geohash (it moved)."); 106 // need to move document to the correct index 107 $knownIds = $this->spatial_idx[$knownHash]; 108 dbglog($knownIds,"Known page id's for this hash:"); 109 // remove it from the old geohash element 110 $i = array_search($id,$knownIds); 111 dbglog('Unsetting:'.$knownIds[$i]); 112 unset($knownIds[$i]); 113 $this->spatial_idx[$knownHash]=$knownIds; 114 // set on new geohash element 115 $pageIds=$this->spatial_idx[$geohash]; 116 $pageIds[] = $id; 117 dbglog($pageIds,"page id's for this hash:"); 118 } 119 } 120 // store and save 121 $this->spatial_idx[$geohash] = $pageIds; 122 return $this->_saveIndex(); 123 } 124 125 /** 126 * Looks up the geohash(es) for the document in the index. 127 * @param String $id document ID 128 * @param array $index spatial index 129 */ 130 function findHashesForId($id,$index) { 131 $hashes=array(); 132 foreach ($index as $hash => $docIds){ 133 dbglog($docIds, "Inspecting element $hash for $id"); 134 if(in_array($id, $docIds, false)){ 135 dbglog("Adding $hash to the list of results for $id"); 136 $hashes[]=$hash; 137 } 138 } 139 dbglog($hashes,"Found the following hashes for $id (should only be 1)"); 140 return $hashes; 141 } 142 /** 143 * Deletes the page from the index. 144 * @param String $id document ID 145 */ 146 function deleteFromIndex($id){ 147 // check the index for document 148 $knownHashes = $this->findHashesForId($id, $this->spatial_idx); 149 // TODO shortcut, need to make sure there is only one element, if not the index is corrupt 150 $knownHash=$knownHashes[0]; 151 $knownIds = $this->spatial_idx[$knownHash]; 152 $i = array_search($id,$knownIds); 153 dbglog("removing: $knownIds[$i] from the index"); 154 unset($knownIds[$i]); 155 $this->spatial_idx[$knownHash]=$knownIds; 156 $this->_saveIndex(); 157 } 158 /** 159 * Save spatial index 160 */ 161 private function _saveIndex() { 162 dbglog($this->spatial_idx,'--- spatialhelper_index::_saveIndex ---'); 163 return io_saveFile($this->idx_dir.'/spatial.idx', serialize($this->spatial_idx)); 164 } 165 166 /** 167 * (re-)Generates the spatial index by running through all the pages in the wiki. 168 * @todo add an option to erase the old index 169 */ 170 function generateSpatialIndex() { 171 global $conf; 172 require_once (DOKU_INC.'inc/search.php'); 173 $pages = array(); 174 search($pages, $conf['datadir'], 'search_allpages', array()); 175 foreach ($pages as $page) { 176 $this->updateSpatialIndex($page['id']); 177 } 178 return true; 179 } 180}