1<?php 2/* 3 * Copyright (c) 2011-2018 Mark C. Prins <mprins@users.sf.net> 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 */ 17if (!defined('DOKU_INC')) { 18 die (); 19} 20 21if (!defined('DOKU_PLUGIN')) { 22 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 23} 24 25require_once (DOKU_PLUGIN . 'syntax.php'); 26/** 27 * DokuWiki Plugin geotag (Syntax Component). 28 * 29 * Handles the rendering part of the geotag plugin. 30 * 31 * @license BSD license 32 * @author Mark C. Prins <mprins@users.sf.net> 33 */ 34class syntax_plugin_geotag_geotag extends DokuWiki_Syntax_Plugin { 35 /** 36 * 37 * @see DokuWiki_Syntax_Plugin::getType() 38 */ 39 public function getType() { 40 return 'substition'; 41 } 42 43 /** 44 * 45 * @see DokuWiki_Syntax_Plugin::getPType() 46 */ 47 public function getPType() { 48 return 'block'; 49 } 50 51 /** 52 * 53 * @see Doku_Parser_Mode::getSort() 54 */ 55 public function getSort() { 56 return 305; 57 } 58 59 /** 60 * 61 * @see Doku_Parser_Mode::connectTo() 62 */ 63 public function connectTo($mode) { 64 $this->Lexer->addSpecialPattern('\{\{geotag>.*?\}\}', $mode, 'plugin_geotag_geotag'); 65 } 66 67 /** 68 * 69 * @see DokuWiki_Syntax_Plugin::handle() 70 */ 71 public function handle($match, $state, $pos, Doku_Handler $handler) { 72 $tags = trim(substr($match, 9, - 2)); 73 // parse geotag content 74 preg_match("(lat[:|=]-?\d*\.\d*)", $tags, $lat); 75 preg_match("(lon[:|=]-?\d*\.\d*)", $tags, $lon); 76 preg_match("(alt[:|=]-?\d*\.?\d*)", $tags, $alt); 77 preg_match("/(region[:|=][\p{L}\s\w'-]*)/u", $tags, $region); 78 preg_match("/(placename[:|=][\p{L}\s\w'-]*)/u", $tags, $placename); 79 preg_match("/(country[:|=][\p{L}\s\w'-]*)/u", $tags, $country); 80 preg_match("(hide|unhide)", $tags, $hide); 81 82 $showlocation = $this->getConf('geotag_location_prefix'); 83 if ($this->getConf('geotag_showlocation')) { 84 $showlocation = trim(substr($placename [0], 10)); 85 if (strlen($showlocation) < 1) { 86 $showlocation = $this->getConf('geotag_location_prefix'); 87 } 88 } 89 // read config for system setting 90 $style = ''; 91 if ($this->getConf('geotag_hide')) { 92 $style = ' style="display: none;"'; 93 } 94 // override config for the current tag 95 if (trim($hide [0]) == 'hide') { 96 $style = ' style="display: none;"'; 97 } elseif (trim($hide [0]) == 'unhide') { 98 $style = ''; 99 } 100 101 $data = array( 102 hsc(trim(substr($lat [0], 4))), 103 hsc(trim(substr($lon [0], 4))), 104 hsc(trim(substr($alt [0], 4))), 105 $this->_geohash(substr($lat [0], 4), substr($lon [0], 4)), 106 hsc(trim(substr($region [0], 7))), 107 hsc(trim(substr($placename [0], 10))), 108 hsc(trim(substr($country [0], 8))), 109 hsc($showlocation), 110 $style 111 ); 112 return $data; 113 } 114 115 /** 116 * 117 * @see DokuWiki_Syntax_Plugin::render() 118 */ 119 public function render($mode, Doku_Renderer $renderer, $data) { 120 if ($data === false) { 121 return false; 122 } 123 list ($lat, $lon, $alt, $geohash, $region, $placename, $country, $showlocation, $style) = $data; 124 $ddlat = $lat; 125 $ddlon = $lon; 126 if ($this->getConf('displayformat') === 'DMS') { 127 $lat = $this->convertLat($lat); 128 $lon = $this->convertLon($lon); 129 } else { 130 $lat .= 'º'; 131 $lon .= 'º'; 132 } 133 134 if ($mode == 'xhtml') { 135 if ($this->getConf('geotag_prevent_microformat_render')) { 136 return true; 137 } 138 $searchPre = ''; 139 $searchPost = ''; 140 if ($this->getConf('geotag_showsearch')) { 141 if ($spHelper = &plugin_load('helper', 'spatialhelper_search')) { 142 $title = $this->getLang('findnearby') . ' ' . $placename; 143 $url = wl(getID(), array( 144 'do' => 'findnearby', 145 'lat' => $ddlat, 146 'lon' => $ddlon 147 )); 148 $searchPre = '<a href="' . $url . '" title="' . $title . '">'; 149 $searchPost = '<span class="a11y">' . $title . '</span></a>'; 150 } 151 } 152 153 // render geotag microformat/schema.org microdata 154 $renderer->doc .= '<span class="geotagPrint">' . $this->getLang('geotag_desc') . '</span>'; 155 $renderer->doc .= '<div class="h-geo geo"' . $style . ' title="' . $this->getLang('geotag_desc') . $placename . '" itemscope itemtype="http://schema.org/Place">'; 156 $renderer->doc .= '<span itemprop="name">' . $showlocation . '</span>: ' . $searchPre; 157 $renderer->doc .= '<span itemprop="geo" itemscope itemtype="http://schema.org/GeoCoordinates">'; 158 $renderer->doc .= '<span class="p-latitude latitude" itemprop="latitude" data-latitude="' . $ddlat . '">' . $lat . '</span>;'; 159 $renderer->doc .= '<span class="p-longitude longitude" itemprop="longitude" data-longitude="' . $ddlon . '">' . $lon . '</span>'; 160 if (!empty ($alt)) { 161 $renderer->doc .= ', <span class="p-altitude altitude" itemprop="elevation" data-altitude="' . $alt . '">' . $alt . 'm</span>'; 162 } 163 $renderer->doc .= '</span>' . $searchPost . '</div>' . DOKU_LF; 164 return true; 165 } elseif ($mode == 'metadata') { 166 // render metadata (our action plugin will put it in the page head) 167 $renderer->meta ['geo'] ['lat'] = $ddlat; 168 $renderer->meta ['geo'] ['lon'] = $ddlon; 169 $renderer->meta ['geo'] ['placename'] = $placename; 170 $renderer->meta ['geo'] ['region'] = $region; 171 $renderer->meta ['geo'] ['country'] = $country; 172 $renderer->meta ['geo'] ['geohash'] = $geohash; 173 $renderer->meta ['geo'] ['alt'] = $alt; 174 return true; 175 } elseif ($mode == 'odt') { 176 if (!empty ($alt)) { 177 $alt = ', ' . $alt . 'm'; 178 } 179 $renderer->p_open(); 180 $renderer->_odtAddImage(DOKU_PLUGIN . 'geotag/images/geotag.png', null, null, 'left', ''); 181 $renderer->cdata($this->getLang('geotag_desc') . ' ' . $placename); 182 $renderer->monospace_open(); 183 $renderer->cdata($lat . ';' . $lon . $alt); 184 $renderer->monospace_close(); 185 $renderer->p_close(); 186 return true; 187 } 188 return false; 189 } 190 191 /** 192 * Calculate the geohash for this lat/lon pair. 193 * 194 * @param float $lat 195 * @param float $lon 196 */ 197 private function _geohash($lat, $lon) { 198 if (!$geophp = plugin_load('helper', 'geophp')) { 199 dbglog($geophp, 'syntax_plugin_geotag_geotag::_geohash: geophp plugin is not available.'); 200 return ""; 201 } 202 $_lat = floatval($lat); 203 $_lon = floatval($lon); 204 $geometry = new Point($_lon, $_lat); 205 return $geometry->out('geohash'); 206 } 207 208 /** 209 * Convert decimal degrees to degrees, minutes, seconds format 210 * 211 * @todo move this into a shared library 212 * @param float $decimaldegrees 213 * @return string dms 214 */ 215 private function _convertDDtoDMS($decimaldegrees) { 216 $dms = floor($decimaldegrees); 217 $secs = ($decimaldegrees - $dms) * 3600; 218 $min = floor($secs / 60); 219 $sec = round($secs - ($min * 60), 3); 220 $dms .= 'º' . $min . '\'' . $sec . '"'; 221 return $dms; 222 } 223 224 /** 225 * convert latitude in decimal degrees to DMS+hemisphere. 226 * 227 * @todo move this into a shared library 228 * @param float $decimaldegrees 229 * @return string 230 */ 231 private function convertLat($decimaldegrees) { 232 if (strpos($decimaldegrees, '-') !== false) { 233 $latPos = "S"; 234 } else { 235 $latPos = "N"; 236 } 237 $dms = $this->_convertDDtoDMS(abs(floatval($decimaldegrees))); 238 return hsc($dms . $latPos); 239 } 240 241 /** 242 * convert longitude in decimal degrees to DMS+hemisphere. 243 * 244 * @todo move this into a shared library 245 * @param float $decimaldegrees 246 * @return string 247 */ 248 private function convertLon($decimaldegrees) { 249 if (strpos($decimaldegrees, '-') !== false) { 250 $lonPos = "W"; 251 } else { 252 $lonPos = "E"; 253 } 254 $dms = $this->_convertDDtoDMS(abs(floatval($decimaldegrees))); 255 return hsc($dms . $lonPos); 256 } 257} 258