xref: /plugin/spatialhelper/helper/index.php (revision 9af1cdd4adca160d449eeb70ed71b06973b0b8a0)
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}