]> git.street.me.uk Git - andy/gpx.git/blame - src/libgpx/gpxwriter.php
Ignore phpdoc
[andy/gpx.git] / src / libgpx / gpxwriter.php
CommitLineData
88564339
AS
1<?php
2/**
3 * gpxwriter.php
4 *
5 * Copyright 2018 Andy Street <andy@street.me.uk>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 *
22 *
23 */
24
25namespace libgpx;
26
27use \DateTime;
28use \DateTimeInterface;
29use \DateTimeZone;
30use \RuntimeException;
31use \UnexpectedValueException;
32use \XMLWriter;
33
34/**
35 * Write GPX files.
36 *
37 * @author Andy Street <andy@street.me.uk>
38 */
39class GPXWriter
40{
41
42 /**
43 * Who to credit as the document creator in the GPX root element.
44 *
45 * @var string
46 * @see http://www.topografix.com/GPX/1/1/#type_gpxType
47 */
48 protected $creator = 'libgpx';
49
50 /**
51 * The version of the GPX standard to generate.
52 *
53 * @var string
54 */
55 protected $format = '1.1';
56
57 /**
58 * Whether to indent the XML output for readability.
59 *
60 * @var boolean
61 */
62 protected $indent = true;
63
64 /**
65 * Whether to include milliseconds in timestamps.
66 *
67 * @var boolean
68 */
69 protected $milliseconds = true;
70
71 /**
72 * Fetch the name credited as the creator of the XML files.
73 *
74 * @return string The creator.
75 */
76 public function getCreator()
77 {
78 return $this->creator;
79 }
80
81 /**
82 * Set the name to be credited as the XML document creator.
83 *
84 * @param string $creator The name to credit as the creator.
85 * @return void
86 */
87 public function setCreator(string $creator)
88 {
89 $this->creator = $creator;
90 }
91
92 /**
93 * Fetch the GPX file format that this class generates.
94 *
95 * @return string The GPX file format.
96 */
97 public function getFormat()
98 {
99 return $this->format;
100 }
101
102 /**
103 * Set the GPX file format that this class generates.
104 *
105 * @param string $format Accepted values are "1.0" or "1.1".
106 * @return void
107 * @throws UnexpectedValueException If $format is not a known GPX version.
108 */
109 public function setFormat(string $format)
110 {
111 switch ($format) {
112 case '1.0':
113 case '1.1':
114 $this->format = $format;
115 break;
116 default:
117 throw new UnexpectedValueException(
118 sprintf('Unknown GPX version "%s"', $format)
119 );
120 }
121 }
122
123 /**
124 * Whether this class will indent output for readability.
125 *
126 * @return boolean If the output will be indented.
127 */
128 public function getIndent()
129 {
130 return $this->indent;
131 }
132
133 /**
134 * Set whether to indent output for readability.
135 *
136 * @param boolean $indent Whether to indent.
137 * @return void
138 */
139 public function setIndent(bool $indent)
140 {
141 $this->indent = $indent;
142 }
143
144 /**
145 * Whether timestamps are written to include milliseconds.
146 *
147 * @return boolean
148 */
149 public function getTimestampUseMilliseconds()
150 {
151 return $this->milliseconds;
152 }
153
154 /**
155 * Set whether to output milliseconds in timestamps.
156 *
157 * @param boolean $milliseconds Whether to use milliseconds.
158 * @return void
159 */
160 public function setTimestampUseMilliseconds(bool $milliseconds)
161 {
162 $this->milliseconds = $milliseconds;
163 }
164
165 /**
166 * Fetch an XML representation as a string.
167 *
168 * @param GPX $gpx The source for the XML.
169 * @return string An XML representation of the GPX object.
170 */
171 public function fetch(GPX $gpx)
172 {
173 $xml = new XMLWriter();
174 $xml->openMemory();
175 $this->write($gpx, $xml);
176 return $xml->outputMemory();
177 }
178
179 /**
180 * Write XML to standard out.
181 *
182 * @param GPX $gpx The source for the XML.
183 * @return void
184 */
185 public function output(GPX $gpx)
186 {
187 $xml = new XMLWriter();
188 $xml->openURI('php://output');
189 $this->write($gpx, $xml);
190 }
191
192 /**
193 * Write XML to a file.
194 *
195 * @param GPX $gpx The source for the XML.
196 * @param string $filename Where to write the XML.
197 * @return void
198 * @throws RuntimeException If the file cannot be written to.
199 */
200 public function save(GPX $gpx, string $filename)
201 {
202 $xml = new XMLWriter();
203 if (!$xml->openURI('php://output'))
204 throw RuntimeException(sprintf('Unable to write to "%s"', $filename));
205 $this->write($gpx, $xml);
206 }
207
208 /**
209 * Generate the XML.
210 *
211 * @param GPX $gpx The source for the XML.
212 * @param XMLWriter $xml The instance to use to generate the XML.
213 * @return void
214 */
215 protected function write(GPX $gpx, XMLWriter $xml)
216 {
217 $xml->setIndent($this->indent);
218 $xml->startDocument('1.0', 'UTF-8');
219 if ($this->format == '1.1') {
220 $namespace = libgpx::NAMESPACE_GPX_1_1;
221 $schema = libgpx::SCHEMA_GPX_1_1;
222 } else {
223 $namespace = libgpx::NAMESPACE_GPX_1_0;
224 $schema = libgpx::SCHEMA_GPX_1_0;
225 }
226 $xml->startElementNs(null, 'gpx', $namespace);
227 $xml->writeAttribute('version', $this->format);
228 $xml->writeAttribute('creator', $this->creator);
229 $xml->writeAttributeNS(
230 'xsi',
231 'schemaLocation',
232 libgpx::NAMESPACE_XMLSCHEMA,
233 $namespace . ' ' . $schema
234 );
235 $this->writeMetadata($gpx, $xml);
236 $xml->endElement();
237 $xml->endDocument();
238 $xml->flush();
239 }
240
241 /**
242 * Generate XML for Metadata elements.
243 *
244 * @param GPX $gpx The object to source the metadata from.
245 * @param XMLWriter $xml Where to write the metadata.
246 * @return void
247 */
248 protected function writeMetadata(GPX $gpx, XMLWriter $xml)
249 {
250 if ($this->format == '1.1')
251 $xml->startElement('metadata');
252 $name = $gpx->getName();
253 if (!empty($name))
254 $xml->writeElement('name', $name);
255 $desc = $gpx->getDesc();
256 if (!empty($desc))
257 $xml->writeElement('desc', $desc);
258 $author = $gpx->getAuthor();
259 if ($author instanceof Person)
260 $this->writePerson($author, $xml);
261 if ($this->format == '1.1') {
262 $copyright = $gpx->getCopyright();
263 if ($copyright instanceof Copyright)
264 $this->writeCopyright($copyright, $xml);
265 }
266 foreach($gpx->getLinks() as $link) {
267 $this->writeLink($link, $xml);
268 if ($this->format == '1.0')
269 break;
270 }
271 $xml->writeElement('time', $this->createTimestamp(new DateTime()));
272 if (!empty($gpx->getKeywords()))
7c465a82
AS
273 $xml->writeElement(
274 'keywords',
275 implode(', ', $gpx->getKeywords()->toArray())
276 );
88564339
AS
277 $bounds = $gpx->getBounds();
278 if ($bounds instanceof Bounds) {
279 $xml->startElement('bounds');
280 $xml->writeAttribute('minlat', $bounds->getMinLat());
281 $xml->writeAttribute('minlon', $bounds->getMinLon());
282 $xml->writeAttribute('maxlat', $bounds->getMaxLat());
283 $xml->writeAttribute('maxlon', $bounds->getMaxLon());
284 $xml->endElement();
285 }
7c465a82
AS
286 if ($this->format == '1.1') {
287 $extensions = $gpx->getMetadataExtensions();
288 if (!$extensions->isEmpty()) {
289 $xml->startElement('extensions');
290 foreach ($extensions as $extension) {
291 $xml->writeRaw($extension);
292 }
293 $xml->endElement();
294 }
295 $xml->endElement(); // </metadata>
296 }
88564339
AS
297 }
298
299 /**
300 * Generate XML for GPX Person type.
301 *
302 * @param Person $person The person object to source data from.
303 * @param XMLWriter $xml Where to write the data.
304 * @return void
305 */
306 protected function writePerson(Person $person, XMLWriter $xml)
307 {
308 if ($this->format == '1.1') {
309 $xml->startElement('author');
310 $name = $person->getName();
311 if (!empty($name))
312 $xml->writeElement('name', $name);
313 $email = $person->getEmail();
314 if (!empty($email)) {
315 $email = explode('@', $email, 2);
316 $xml->startElement('email');
317 $xml->writeAttribute('id', $email[0]);
318 $xml->writeAttribute('domain', $email[1]);
319 $xml->endElement();
320 }
321 $link = $person->getLink();
322 if (!empty($link))
323 $this->writeLink($link, $xml);
324 $xml->endElement();
325 } else {
326 $name = $person->getName();
327 if (!empty($name))
328 $xml->writeElement('author', $name);
329 $email = $person->getEmail();
330 if (!empty($email))
331 $xml->writeElement('email', $email);
332 }
333 }
334
335 /**
336 * Generate XML for a GPX Link type.
337 *
338 * @param Link $link The link object to source data from.
339 * @param XMLWriter $xml Where to write the data.
340 * @return void
341 */
342 protected function writeLink(Link $link, XMLWriter $xml)
343 {
344 if ($this->format == '1.1') {
345 $xml->startElement('link');
346 $xml->writeAttribute('href', $link->getHref());
347 $text = $link->getText();
348 if (!empty($text))
349 $xml->writeElement('text', $text);
350 $type = $link->getType();
351 if (!empty($type))
352 $xml->writeElement('type', $type);
353 $xml->endElement();
354 } else {
355 $xml->writeElement('url', $link->getHref());
356 $text = $link->getText();
357 if (!empty($text))
358 $xml->writeElement('urlname', $text);
359 }
360 }
361
362 /**
363 * Generate XML for GPX Copyright type.
364 *
365 * @param Copyright $copyright Object to source copyright data from.
366 * @param XMLWriter $xml Where to write the data.
367 * @return void
368 */
369 protected function writeCopyright(Copyright $copyright, XMLWriter $xml)
370 {
371 $xml->startElement('copyright');
372 $xml->writeAttribute('author', $copyright->getAuthor());
373 $year = $copyright->getYear();
374 if (!empty($year))
375 $xml->writeElement('year', $year);
376 $license = $copyright->getLicense();
377 if (!empty($license))
378 $xml->writeElement('license', $license);
379 $xml->endElement();
380 }
381
382 /**
383 * Create an ISO 8601 timestamp.
384 *
385 * @param DateTimeInterface $timestamp The timestamp to format.
386 * @param boolean $millis Include milliseconds.
387 * @return string A timestamp.
388 */
389 protected function createTimestamp(
390 DateTimeInterface $timestamp,
391 bool $millis = null
392 ) {
393 if ($millis === null)
394 $millis = $this->milliseconds;
395 if (!$timestamp->getTimezone()->getOffset($timestamp))
396 $timestamp = $timestamp->setTimezone(new DateTimeZone('UTC'));
397 $format = 'Y-m-d\TH:i:s' . ($millis ? '.v\Z' : '\Z');
398 return $timestamp->format($format);
399 }
400
401}