xref: /plugin/geotag/syntax/geotag.php (revision 2293e8926da1f776e39a7343588710dcd7db9b69) !
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' ) . '&nbsp;' . $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>:&nbsp;' . $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