<dgpsid>13</dgpsid>
<custom:checkedBy>Jane Doe</custom:checkedBy>
</wpt>
+ <rte>
+ <name>Sample Route</name>
+ <cmt>Sample</cmt>
+ <desc>A Sample route</desc>
+ <src>Imagination</src>
+ <url>https://www.example.com/route</url>
+ <urlname>Sample Route</urlname>
+ <number>1</number>
+ <custom:checkedBy>Jane Doe</custom:checkedBy>
+ <rtept lat="0.0" lon="0.0"/>
+ <rtept lat="1.0" lon="1.0"/>
+ </rte>
</gpx>
<custom:checkedBy>Jane Doe</custom:checkedBy>\r
</extensions>\r
</wpt>\r
+ <rte>\r
+ <name>Sample Route</name>\r
+ <cmt>Sample</cmt>\r
+ <desc>A Sample route</desc>\r
+ <src>Imagination</src>\r
+ <link href="https://www.example.com/route">\r
+ <text>Sample Route</text>\r
+ <type>text/html</type>\r
+ </link>\r
+ <number>1</number>\r
+ <type>A type</type>\r
+ <extensions>\r
+ <custom:checkedBy>Jane Doe</custom:checkedBy>\r
+ </extensions>\r
+ <rtept lat="0.0" lon="0.0"/>\r
+ <rtept lat="1.0" lon="1.0"/>\r
+ </rte>\r
</gpx>\r
*/
protected $waypoints;
+ /**
+ * A list of routes.
+ *
+ * @var TypedDoublyLinkedList (Route)
+ */
+ protected $routes;
+
/**
* Fetch the name of the program that created the GPX file.
*
return $this->waypoints;
}
+ /**
+ * Fetch a list of routes.
+ *
+ * @param boolean $create Create the list if it does not already exist.
+ * @return TypedDoublyLinkedList A list of routes.
+ */
+ public function getRoutes(bool $create = true)
+ {
+ if ($create && $this->routes === null)
+ $this->routes = new TypedDoublyLinkedList('libgpx\Route');
+ return $this->routes;
+ }
+
/**
* Fetch a bounding box that covers the feature.
*
public function getBounds()
{
$result = null;
- foreach ($this->waypoints as $waypoint) {
- $bounds = $waypoint->getBounds();
- $result = ($result === null ? $bounds : $result->extend($bounds));
+ $lists = [
+ $this->waypoints,
+ $this->routes
+ ];
+ foreach ($lists as $list) {
+ if ($list === null) continue;
+ foreach ($list as $geo) {
+ $bounds = $geo->getBounds();
+ if ($bounds === null) continue;
+ $result = ($result === null ? $bounds : $result->extend($bounds));
+ }
}
return $result;
}
'elements' => [
'wpt' => function ($gpx) {
$gpx->getWaypoints()[] = $this->readWpt();
+ },
+ 'rte' => function ($gpx) {
+ $gpx->getRoutes()[] = $this->readRte();
}
]
];
return $result;
}
+ /**
+ * Read a rte type.
+ *
+ * @see https://www.topografix.com/GPX/1/1/#type_rteType
+ *
+ * @return Route
+ * @throws MalformedGPXException If the GPX file was invalid or not supported.
+ */
+ protected function readRte()
+ {
+ try {
+ $result = new Route();
+ $struct = [
+ 'elements' => [
+ 'name' => function ($route) {
+ $route->setName($this->readXSDString());
+ },
+ 'cmt' => function ($route) {
+ $route->setComment($this->readXSDString());
+ },
+ 'desc' => function ($route) {
+ $route->setDescription($this->readXSDString());
+ },
+ 'src' => function ($route) {
+ $route->setSource($this->readXSDString());
+ },
+ 'number' => function ($route) {
+ $route->setNumber($this->string2int($this->readXSDString()));
+ },
+ 'rtept' => function ($route) {
+ $route->getPoints()[] = $this->readWpt();
+ }
+ ]
+ ];
+ if ($this->version == '1.1') {
+ $struct['elements']['link'] = function ($route) {
+ $route->getLinks()[] = $this->readLink();
+ };
+ $struct['elements']['type'] = function ($route) {
+ $route->setType($this->readXSDString());
+ };
+ $struct['elements']['extensions'] = function ($route) {
+ $this->readExtension($route->getExtensions());
+ };
+ } else {
+ $struct['elements']['url'] = function ($route, &$state) {
+ $href = $this->readXSDString();
+ $links = $route->getLinks();
+ if ($links->isEmpty()) {
+ $link = new Link($href);
+ if (isset($state['urlname'])) {
+ $link->setText($state['urlname']);
+ unset($state['urlname']);
+ }
+ $links[] = $link;
+ } else {
+ $links->bottom()->setHref($href);
+ }
+ };
+ $struct['elements']['urlname'] = function ($route, &$state) {
+ $text = $this->readXSDString();
+ $links = $route->getLinks();
+ if ($links->isEmpty()) {
+ $state['urlname'] = $text;
+ } else {
+ $links->bottom()->setText($text);
+ }
+ };
+ $struct['extensions'] = function ($route) {
+ $route->getExtensions()[] = $this->xml->readOuterXML();
+ $this->xml->next();
+ };
+ }
+ $this->readStruct($struct, $result);
+ } catch (DomainException $e) {
+ throw new MalformedGPXException(
+ $e->getMessage(), $e->getCode(), $e
+ );
+ }
+ return $result;
+ }
+
/**
* Read a data structure from XML.
*
$this->writePoint($wpt, 'wpt', $xml);
}
}
+ $routes = $gpx->getRoutes(false);
+ if ($routes !== null) {
+ foreach ($routes as $route) {
+ $this->writeRoute($route, $xml);
+ }
+ }
$xml->endElement();
$xml->endDocument();
$xml->flush();
$xml->endElement();
}
+ /**
+ * Write XML for a route element.
+ *
+ * @param Route $route The route to write.
+ * @param XMLWriter $xml Where to write.
+ */
+ protected function writeRoute(Route $route, XMLWriter $xml)
+ {
+ $xml->startElement('rte');
+ $var = $route->getName();
+ if ($var !== null)
+ $xml->writeElement('name', $var);
+ $var = $route->getComment();
+ if ($var !== null)
+ $xml->writeElement('cmt', $var);
+ $var = $route->getDescription();
+ if ($var !== null)
+ $xml->writeElement('desc', $var);
+ $var = $route->getSource();
+ if ($var !== null)
+ $xml->writeElement('src', $var);
+ $var = $route->getLinks(false);
+ if ($var !== null) {
+ foreach($var as $link) {
+ $this->writeLink($link, $xml);
+ if ($this->format == '1.0')
+ break;
+ }
+ }
+ $var = $route->getNumber();
+ if ($var !== null)
+ $xml->writeElement('number', $var);
+ if ($this->format == '1.1') {
+ $var = $route->getType();
+ if ($var !== null)
+ $xml->writeElement('type', $var);
+ }
+ $var = $route->getExtensions(false);
+ if ($var !== null && !$var->isEmpty()) {
+ if ($this->format == '1.1')
+ $xml->startElement('extensions');
+ foreach ($var as $extension) {
+ $xml->writeRaw($extension);
+ }
+ if ($this->format == '1.1')
+ $xml->endElement();
+ }
+ $var = $route->getPoints(false);
+ if ($var !== null) {
+ foreach ($var as $point) {
+ $this->writePoint($point, 'rtept', $xml);
+ }
+ }
+ $xml->endElement();
+ }
+
}
--- /dev/null
+<?php
+/**
+ * route.php
+ *
+ * Copyright 2018 Andy Street <andy@street.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+namespace libgpx;
+
+use \DomainException;
+
+/**
+ * The GPX route type.
+ *
+ * @see https://www.topografix.com/GPX/1/1/#type_rteType
+ *
+ * @author Andy Street <andy@street.me.uk>
+ */
+class Route implements Geographic
+{
+
+ /**
+ * The name of the route.
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * GPS waypoint comment.
+ *
+ * Sent to GPS as comment.
+ *
+ * @var string
+ */
+ protected $comment;
+
+ /**
+ * A text description of the element.
+ *
+ * Holds additional information about the element intended for the user,
+ * not the GPS.
+ *
+ * @var string
+ */
+ protected $description;
+
+ /**
+ * Source of data.
+ *
+ * Included to give user some idea of reliability and accuracy of data.
+ * E.g. "Garmin eTrex", "USGS quad Boston North".
+ *
+ * @var string
+ */
+ protected $source;
+
+ /**
+ * A list of links associated with this route.
+ *
+ * @var TypedDoublyLinkedList (Link)
+ */
+ protected $links;
+
+ /**
+ * GPS route number.
+ *
+ * @var int
+ */
+ protected $number;
+
+ /**
+ * Type (classification) of route.
+ *
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * A list of XML snippets describing unsupported data.
+ *
+ * @var TypedDoublyLinkedList (string)
+ */
+ protected $extensions;
+
+ /**
+ * A list of route points.
+ *
+ * @var DoublyLinkedList (Point)
+ */
+ protected $points;
+
+ /**
+ * Fetch the name of the route.
+ *
+ * @return string|null The name or null if not set.
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the route.
+ *
+ * @param string $name The name or null to delete.
+ * @return void
+ */
+ public function setName(string $name = null)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Fetch a comment for the route.
+ *
+ * @return string The comment or null if not set.
+ */
+ public function getComment()
+ {
+ return $this->comment;
+ }
+
+ /**
+ * Set a comment for the route.
+ *
+ * @param string $comment The comment or null to delete.
+ * @return void
+ */
+ public function setComment(string $comment = null)
+ {
+ $this->comment = $comment;
+ }
+ /**
+ * Fetch the description for the route.
+ *
+ * @return string The description or null if not set.
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set the description of the point.
+ *
+ * @param string $description The description or null to delete.
+ * @return void
+ */
+ public function setDescription(string $description = null)
+ {
+ $this->description = $description;
+ }
+
+ /**
+ * Fetch the source of the data.
+ *
+ * @return string|null The source or null if not set.
+ */
+ public function getSource()
+ {
+ return $this->source;
+ }
+
+ /**
+ * Set the source of the data.
+ *
+ * @param string $source The source of the data or null to delete.
+ * @return void
+ */
+ public function setSource(string $source = null)
+ {
+ $this->source = $source;
+ }
+
+ /**
+ * Fetch a list of links associated with this route.
+ *
+ * @param boolean $create Create the list if it does not already exist.
+ * @return TypedDoublyLinkedList|null A list of Link objects.
+ */
+ public function getLinks(bool $create = true)
+ {
+ if ($create && $this->links === null)
+ $this->links = new TypedDoublyLinkedList('libgpx\Link');
+ return $this->links;
+ }
+
+ /**
+ * Fetch the route number.
+ *
+ * @return int|null The route number or null if not set.
+ */
+ public function getNumber()
+ {
+ return $this->number;
+ }
+
+ /**
+ * Set the number of the route.
+ *
+ * @param int $number The route number or null to delete.
+ * @return void
+ * @throws DomainException If the number is < 0
+ */
+ public function setNumber(int $number = null)
+ {
+ if ($number !== null && $number < 0)
+ throw new DomainException(
+ sprintf('Number must be >= 0 but got "%s"', $number)
+ );
+ $this->number = $number;
+ }
+
+ /**
+ * Fetch the type of the route.
+ *
+ * @return string|null The type or null if not set.
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Set the type of the route.
+ *
+ * @param string $type The type or null to delete.
+ * @return void
+ */
+ public function setType(string $type = null)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * Fetch a list of XML strings that describe unsupported elements.
+ *
+ * @param boolean $create Create the list if it does not already exist.
+ * @return TypedDoublyLinkedList|null A list of XML strings.
+ */
+ public function getExtensions(bool $create = true)
+ {
+ if ($create && $this->extensions === null)
+ $this->extensions = new TypedDoublyLinkedList('string');
+ return $this->extensions;
+ }
+
+ /**
+ * Fetch an ordered list of route points.
+ *
+ * @param boolean $create Create the list if it does not already exist.
+ * @return TypedDoublyLinkedList|null A list of route points.
+ */
+ public function getPoints(bool $create = true)
+ {
+ if ($create && $this->points === null)
+ $this->points = new TypedDoublyLinkedList('libgpx\Point');
+ return $this->points;
+ }
+
+ /**
+ * Fetch a bounding box that covers the feature.
+ *
+ * @return Bounds|null A bounding box covering the extent of the feature or
+ * null if not applicable.
+ */
+ public function getBounds()
+ {
+ $result = null;
+ if ($this->points !== null) {
+ foreach ($this->points as $point) {
+ $bounds = $point->getBounds();
+ $result = ($result === null ? $bounds : $result->extend($bounds));
+ }
+ }
+ return $result;
+ }
+
+}