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