Skip to content

Commit 7c45299

Browse files
committed
- use geoPHP for indexing and search
- search for lat/lon as well as geohash - add admin page for index purge - authorisation check for ID's in search Issue number 25
1 parent adc8974 commit 7c45299

File tree

10 files changed

+450
-251
lines changed

10 files changed

+450
-251
lines changed

action.php

Lines changed: 133 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
die ();
1919
if (! defined ( 'DOKU_PLUGIN' ))
2020
define ( 'DOKU_PLUGIN', DOKU_INC . 'lib/plugins/' );
21+
if (! defined ( 'DOKU_LF' ))
22+
define ( 'DOKU_LF', "\n" );
2123
require_once (DOKU_PLUGIN . 'action.php');
2224

2325
/**
@@ -35,18 +37,22 @@ class action_plugin_spatialhelper extends DokuWiki_Action_Plugin {
3537
* DokuWiki's event controller object. Also available as global $EVENT_HANDLER
3638
*/
3739
public function register(Doku_Event_Handler &$controller) {
38-
// listen for page add / delete
40+
// listen for page add / delete events
3941
// http://www.dokuwiki.org/devel:event:indexer_page_add
4042
$controller->register_hook ( 'INDEXER_PAGE_ADD', 'BEFORE', $this, '_updateSpatialIndex' );
4143
$controller->register_hook ( 'IO_WIKIPAGE_WRITE', 'BEFORE', $this, '_removeFromIndex' );
4244

4345
// http://www.dokuwiki.org/devel:event:sitemap_generate
44-
$controller->register_hook ( 'SITEMAP_GENERATE', 'BEFORE', $this, '_createspatialsitemap' );
46+
// using after will only trigger us if a sitemap was actually created
47+
$controller->register_hook ( 'SITEMAP_GENERATE', 'AFTER', $this, '_createSpatialSitemap' );
48+
4549
// http://www.dokuwiki.org/devel:event:sitemap_ping
4650
// $controller->register_hook('SITEMAP_PING', 'AFTER', $this, '_ping');
51+
4752
// handle actions we know of
4853
$controller->register_hook ( 'ACTION_ACT_PREPROCESS', 'BEFORE', $this, '_trap_action', array () );
4954
$controller->register_hook ( 'TPL_ACT_UNKNOWN', 'BEFORE', $this, '_findnearby', array () );
55+
5056
// listen for media uploads and deletes
5157
$controller->register_hook ( 'MEDIA_UPLOAD_FINISH', 'BEFORE', $this, '_handle_media_uploaded', array () );
5258
$controller->register_hook ( 'MEDIA_DELETE_FILE', 'BEFORE', $this, '_handle_media_deleted', array () );
@@ -61,28 +67,13 @@ public function register(Doku_Event_Handler &$controller) {
6167
* the parameters passed to register_hook when this handler was registered
6268
*/
6369
function _updateSpatialIndex(Doku_Event &$event, $param) {
64-
// $version = getVersionData();
65-
// dbglog($version, "dokuwiki version data");
66-
$id = "";
67-
// if ($version['date'] < '2011-03-06') {
68-
// /*
69-
// Anteater and previous
70-
// $event→data[0] – the page id
71-
// $event→data[1] – empty, can be filled by additional content to index by your plugin
72-
// */
73-
// $id = $event->data[0];
74-
// } else {
75-
/*
76-
* As of 2011-03-06 the data structure has been changed to:
77-
* $event→data['page'] – the page id
78-
* $event→data['body'] – empty, can be filled by additional content to index by your plugin
79-
* $event→data['metadata'] – the metadata that shall be indexed. This is an array where the keys are the metadata indexes and the value a string or an array of strings with the values. title and relation_references will already be set.
80-
*/
70+
// $event→data['page'] – the page id
71+
// $event→data['body'] – empty, can be filled by additional content to index by your plugin
72+
// $event→data['metadata'] – the metadata that shall be indexed. This is an array where the keys are the metadata indexes and the value a string or an array of strings with the values. title and relation_references will already be set.
8173
$id = $event->data ['page'];
82-
// }
8374
dbg ( "start update spatial index for page: $id", '--- action_plugin_spatialhelper::_updateSpatialIndex ---' );
84-
$indexer = plugin_load ( 'helper', 'spatialhelper_index' );
8575

76+
$indexer = plugin_load ( 'helper', 'spatialhelper_index' );
8677
if ($indexer) {
8778
$entries = $indexer->updateSpatialIndex ( $id );
8879
dbglog ( "Done indexing, entries: $entries", '--- action_plugin_spatialhelper::_updateSpatialIndex ---' );
@@ -100,16 +91,15 @@ function _updateSpatialIndex(Doku_Event &$event, $param) {
10091
* the parameters passed to register_hook when this handler was registered
10192
*/
10293
function _removeFromIndex(Doku_Event &$event, $param) {
103-
/*
104-
* event data:
105-
* $data[0] – The raw arguments for io_saveFile as an array. Do not change file path.
106-
* $data[0][0] – the file path.
107-
* $data[0][1] – the content to be saved, and may be modified.
108-
* $data[1] – ns: The colon separated namespace path minus the trailing page name. (false if root ns)
109-
* $data[2] – page_name: The wiki page name.
110-
* $data[3] – rev: The page revision, false for current wiki pages.
111-
*/
112-
// dbglog($event->data,"Event data in _removeFromIndex.");
94+
// event data:
95+
// $data[0] – The raw arguments for io_saveFile as an array. Do not change file path.
96+
// $data[0][0] – the file path.
97+
// $data[0][1] – the content to be saved, and may be modified.
98+
// $data[1] – ns: The colon separated namespace path minus the trailing page name. (false if root ns)
99+
// $data[2] – page_name: The wiki page name.
100+
// $data[3] – rev: The page revision, false for current wiki pages.
101+
dbglog ( $event->data, "Event data in _removeFromIndex." );
102+
113103
if (@file_exists ( $event->data [0] [0] )) {
114104
// file not new
115105
if (! $event->data [0] [1]) {
@@ -122,7 +112,6 @@ function _removeFromIndex(Doku_Event &$event, $param) {
122112
}
123113
$indexer = plugin_load ( 'helper', 'spatialhelper_index' );
124114
if ($indexer) {
125-
dbglog ( "loaded helper spatialhelper_index. Deleting $id from index" );
126115
$indexer->deleteFromIndex ( $id );
127116
}
128117
}
@@ -137,24 +126,29 @@ function _removeFromIndex(Doku_Event &$event, $param) {
137126
* @param mixed $param
138127
* not used
139128
*/
140-
private function _createspatialsitemap(Doku_Event &$event, $param) {
141-
/*
142-
* $event→data['items']: Array of SitemapItem instances, the array of sitemap items that already contains all public pages of the wiki
143-
* $event→data['sitemap']: The path of the file the sitemap will be saved to.
144-
*/
145-
dbglog ( $event->data ['items'], "Array of SitemapItem instances, the array of sitemap items that already contains all public pages of the wiki" );
146-
dbglog ( $event->data ['sitemap'], "The path of the file the sitemap will be saved to." );
129+
function _createSpatialSitemap(Doku_Event &$event, $param) {
130+
// $event→data['items']: Array of SitemapItem instances, the array of sitemap items that already contains all public pages of the wiki
131+
// $event→data['sitemap']: The path of the file the sitemap will be saved to.
132+
dbglog ( $event->data, "_createSpatialSitemap" );
133+
dbglog ( $param, "_createSpatialSitemap" );
134+
// TODO add a new SitemapItem object that point to the KML of public geocoded pages
135+
// global $conf;
136+
// $url = $conf['baseurl'].$conf['cachedir'].'/sitemap.kml';
137+
// $lastmod = @filemtime($conf['cachedir'].'/sitemap.kml);
138+
// $event->data['items'][] = new SitemapItem($url,$lastmod);
147139
}
148140

149141
/**
150142
* trap findnearby action.
143+
* This addional handler is
144+
* required as described at: https://www.dokuwiki.org/devel:event:tpl_act_unknown
151145
*
152146
* @param Doku_Event $event
153147
* event object by reference
154148
* @param mixed $param
155149
* not used
156150
*/
157-
function _trap_action(&$event, $param) {
151+
function _trap_action(Doku_Event &$event, $param) {
158152
if ($event->data != 'findnearby')
159153
return;
160154
$event->preventDefault ();
@@ -169,97 +163,139 @@ function _trap_action(&$event, $param) {
169163
* not used
170164
*/
171165
function _findnearby(Doku_Event &$event, $param) {
172-
global $lang;
173166
if ($event->data != 'findnearby')
174167
return;
175168
$event->preventDefault ();
176169

177-
$tagns = $this->getConf ( 'namespace' );
178-
$flags = explode ( ',', trim ( $this->getConf ( 'pagelist_flags' ) ) );
179-
180-
// TODO findNearbyLatLon()
181-
$geohash = trim ( str_replace ( $this->getConf ( 'namespace' ) . ':', '', $_REQUEST ['geohash'] ) );
182-
170+
global $INPUT;
171+
$dist_prefix = '';
183172
if ($helper = &plugin_load ( 'helper', 'spatialhelper_search' )) {
184-
$results = $helper->findNearby ( $geohash );
185-
$ids = ( array ) ($results [0]);
186-
$location = ( string ) ($results [1]);
187-
foreach ( $ids as $id ) {
188-
$pages [] = array (
189-
'id' => $id
190-
);
173+
if ($INPUT->has ( 'geohash' )) {
174+
$results = $helper->findNearby ( $INPUT->str ( 'geohash' ) );
175+
$dist_prefix = hsc($this->getLang ('result_distance_prefix')).' ';
176+
} elseif ($INPUT->has ( 'lat' ) && $INPUT->has ( 'lon' )) {
177+
$results = $helper->findNearbyLatLon ( $INPUT->param ( 'lat' ), $INPUT->param ( 'lon' ) );
178+
} else {
179+
print '<div class="level1"><p>' . hsc ( $this->getLang ('invalidinput') ) . '</p></div>';
191180
}
181+
$pages = ( array ) ($results [0]);
182+
$media = ( array ) $results [1];
183+
$location = ( string ) ($results [2]);
184+
$geohash = ( string ) ($results [3]);
185+
$precision = $results [4];
192186
}
193-
// use html_buildlist() instead for media...
194187

188+
print '<h1>' . $this->getLang ('results_header') . '</h1>' . DOKU_LF;
189+
print '<div class="level1">' . DOKU_LF;
195190
if (! empty ( $pages )) {
196-
// let Pagelist Plugin do the work for us
197-
if (plugin_isdisabled ( 'pagelist' ) || (! $pagelist = plugin_load ( 'helper', 'pagelist' ))) {
198-
msg ( $this->getLang ( 'missing_pagelistplugin' ), - 1 );
199-
return false;
200-
}
201-
202-
$pagelist->setFlags ( $flags );
203-
$pagelist->startList ();
191+
$pagelist = '<ol>' . DOKU_LF;
204192
foreach ( $pages as $page ) {
205-
$pagelist->addPage ( $page );
193+
$pagelist .= '<li>' . html_wikilink ( ':' . $page ['id'], useHeading ( 'navigation' ) ? null : noNS ( $page ['id'] ) ) . ' (' . $dist_prefix . $page ['distance'] . 'm) ' . $page ['description'] . '</li>' . DOKU_LF;
206194
}
207-
// TODO convert geohash to lat/lon
208-
print '<h1>Geohash: ' . str_replace ( '_', ' ', $_REQUEST ['geohash'] ) . ' (lat,lon: ' . $location . ')</h1>' . DOKU_LF;
209-
print '<div class="level1">' . DOKU_LF;
210-
print $pagelist->finishList ();
195+
$pagelist .= '</ol>' . DOKU_LF;
196+
197+
print '<h2>' . $this->getLang ('results_pages') . hsc ( ' lat,lon: ' . $location . ' (geohash: ' . $geohash . ')' ) . '</h2>';
198+
print '<div class="level2">' . DOKU_LF;
199+
print $pagelist;
200+
print "<p>Precision: $precision m</p>\n";
211201
print '</div>' . DOKU_LF;
212202
} else {
213-
print '<div class="level1"><p>' . $lang ['nothingfound'] . '</p></div>';
203+
print '<p>' . hsc ( $this->getLang ('nothingfound') ) . '</p>';
204+
}
205+
if (! empty ( $media )) {
206+
$pagelist = '<ol>' . DOKU_LF;
207+
foreach ( $media as $m ) {
208+
$opts = array();
209+
$link = ml ( $m ['id'], $opts, false, '&amp;', false );
210+
$opts ['w'] = '100px';
211+
$src = ml ( $m ['id'], $opts );
212+
$pagelist .= '<li><a href="' . $link . '"><img src="' . $src . '"></a> (' . $dist_prefix . $page ['distance'] . 'm) </li>'. DOKU_LF;
213+
}
214+
$pagelist .= '</ol>' . DOKU_LF;
215+
216+
print '<h2>' . $this->getLang ('results_media') . hsc ( ' lat,lon: ' . $location . ' (geohash: ' . $geohash . ')' ) . '</h2>' . DOKU_LF;
217+
print '<div class="level2">' . DOKU_LF;
218+
print $pagelist;
219+
print "<p>Precision: $precision m</p>\n";
220+
print '</div>' . DOKU_LF;
214221
}
222+
print '</div>' . DOKU_LF;
215223
}
224+
/**
225+
* add media to spatial index.
226+
*
227+
* @param Doku_Event $event
228+
* event object by reference
229+
* @param unknown $param
230+
*/
216231
function _handle_media_uploaded(Doku_Event &$event, $param) {
217-
/*
218-
* data[0] temporary file name (read from $_FILES)
219-
* data[1] file name of the file being uploaded
220-
* data[2] future directory id of the file being uploaded
221-
* data[3] the mime type of the file being uploaded
222-
* data[4] true if the uploaded file exists already
223-
* data[5] (since 2011-02-06) the PHP function used to move the file to the correct location
224-
*/
232+
// data[0] temporary file name (read from $_FILES)
233+
// data[1] file name of the file being uploaded
234+
// data[2] future directory id of the file being uploaded
235+
// data[3] the mime type of the file being uploaded
236+
// data[4] true if the uploaded file exists already
237+
// data[5] (since 2011-02-06) the PHP function used to move the file to the correct location
238+
dbglog ( $event->data, "_handle_media_uploaded::event data" );
225239

226240
// check the list of mimetypes
227241
// if it's a supported type call appropriate index function
228-
dbglog ( "checking uploaded media with mimetype " . $event->data [3] );
229-
dbglog ( $event->data, "_handle_media_uploaded::event data" );
230-
// if(stristr('image/',$event->data[3])){
231-
if (substr_compare ( $event->data [3], 'image/jpeg', 0 ))
232-
// TODO add image/tiff
233-
{
234-
242+
if (substr_compare ( $event->data [3], 'image/jpeg', 0 )) {
235243
$indexer = plugin_load ( 'helper', 'spatialhelper_index' );
236244
if ($indexer) {
237-
dbglog ( "Loaded helper spatialhelper_index." );
238245
$indexer->indexImage ( $event->data [2], $event->data [1] );
239246
}
240247
}
248+
// TODO add image/tiff
241249
// TODO kml, gpx, geojson...
242250
}
243251

244252
/**
245253
* removes the media from the index.
246254
*/
247255
function _handle_media_deleted(Doku_Event &$event, $param) {
248-
/*
249-
* data['id'] ID
250-
* data['unl'] unlink return code
251-
* data['del'] Namespace directory unlink return code
252-
* data['name'] file name
253-
* data['path'] full path to the file
254-
* data['size'] file size
255-
*/
256+
// data['id'] ID data['unl'] unlink return code
257+
// data['del'] Namespace directory unlink return code
258+
// data['name'] file name data['path'] full path to the file
259+
// data['size'] file size
256260
dbglog ( $event->data, "_handle_media_deleted::event data" );
257-
$id = $event->data ['id'];
258-
// remove the id from the index
261+
262+
// remove the media id from the index
259263
$indexer = plugin_load ( 'helper', 'spatialhelper_index' );
260264
if ($indexer) {
261-
dbglog ( "Loaded helper spatialhelper_index. Deleting media $id from index" );
262-
$indexer->deleteFromIndex ( $id );
265+
$indexer->deleteFromIndex ( 'media__' . $event->data ['id'] );
263266
}
264267
}
268+
269+
/**
270+
* Calculate a new coordinate based on start, distance and bearing
271+
*
272+
* @param $start array
273+
* - start coordinate as decimal lat/lon pair
274+
* @param $dist float
275+
* - distance in kilometers
276+
* @param $brng float
277+
* - bearing in degrees (compass direction)
278+
*/
279+
private function _geo_destination($start, $dist, $brng) {
280+
$lat1 = _toRad ( $start [0] );
281+
$lon1 = _toRad ( $start [1] );
282+
// http://en.wikipedia.org/wiki/Earth_radius
283+
// average earth radius in km
284+
$dist = $dist / 6371.01;
285+
$brng = _toRad ( $brng );
286+
287+
$lon2 = $lon1 + atan2 ( sin ( $brng ) * sin ( $dist ) * cos ( $lat1 ), cos ( $dist ) - sin ( $lat1 ) * sin ( $lat2 ) );
288+
$lon2 = fmod ( ($lon2 + 3 * pi ()), (2 * pi ()) ) - pi ();
289+
290+
return array (
291+
_toDeg ( $lat2 ),
292+
_toDeg ( $lon2 )
293+
);
294+
}
295+
private function _toRad($deg) {
296+
return $deg * pi () / 180;
297+
}
298+
private function _toDeg($rad) {
299+
return $rad * 180 / pi ();
300+
}
265301
}

0 commit comments

Comments
 (0)