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