From: Andy Street Date: Wed, 19 Dec 2018 18:35:33 +0000 (+0000) Subject: Add ability to read track types X-Git-Url: https://git.street.me.uk/andy/gpx.git/commitdiff_plain/35c309ccc829e02fc197c9fbaa97094966ebd2c1 Add ability to read track types --- diff --git a/docs/samples/full-1.0.gpx b/docs/samples/full-1.0.gpx index cdcf366..ab9c5f6 100644 --- a/docs/samples/full-1.0.gpx +++ b/docs/samples/full-1.0.gpx @@ -43,4 +43,22 @@ + + Sample Track + track + A sample track + Imagination + https://www.example.com/track + Sample Route + 1 + Jane Doe + + + + + + + + + diff --git a/docs/samples/full-1.1.gpx b/docs/samples/full-1.1.gpx index d913c74..414a45c 100644 --- a/docs/samples/full-1.1.gpx +++ b/docs/samples/full-1.1.gpx @@ -69,4 +69,30 @@ + + Sample Track + track + A sample track + Imagination + + Sample Route + text/html + + 1 + A type + + Jane Doe + + + + Jane Doe + + + + + + + + + diff --git a/src/libgpx/gpx.php b/src/libgpx/gpx.php index 3d45b6f..f71f7f1 100644 --- a/src/libgpx/gpx.php +++ b/src/libgpx/gpx.php @@ -113,6 +113,13 @@ class GPX implements Geographic */ protected $routes; + /** + * A list of tracks. + * + * @var TypedDoublyLinkedList (Track) + */ + protected $tracks; + /** * Fetch the name of the program that created the GPX file. * @@ -303,6 +310,19 @@ class GPX implements Geographic return $this->routes; } + /** + * Fetch a list of tracks. + * + * @param boolean $create Create the list if it does not already exist. + * @return TypedDoublyLinkedList A list of tracks. + */ + public function getTracks(bool $create = true) + { + if ($create && $this->tracks === null) + $this->tracks = new TypedDoublyLinkedList('libgpx\Track'); + return $this->tracks; + } + /** * Fetch a bounding box that covers the feature. * @@ -314,7 +334,8 @@ class GPX implements Geographic $result = null; $lists = [ $this->waypoints, - $this->routes + $this->routes, + $this->tracks ]; foreach ($lists as $list) { if ($list === null) continue; diff --git a/src/libgpx/gpxreader.php b/src/libgpx/gpxreader.php index b98d2ef..4df8d1d 100644 --- a/src/libgpx/gpxreader.php +++ b/src/libgpx/gpxreader.php @@ -183,6 +183,9 @@ class GPXReader }, 'rte' => function ($gpx) { $gpx->getRoutes()[] = $this->readRte(); + }, + 'trk' => function ($gpx) { + $gpx->getTracks()[] = $this->readTrk(); } ] ]; @@ -514,6 +517,60 @@ class GPXReader return $result; } + /** + * Read a trk type. + * + * @see https://www.topografix.com/GPX/1/1/#type_trkType + * + * @return Track + * @throws MalformedGPXException If the GPX file was invalid or not supported. + */ + protected function readTrk() + { + try { + $result = new Track(); + $struct = $this->getDataTypeStruct(); + $struct['elements']['number'] = function ($track) { + $track->setNumber($this->string2int($this->readXSDString())); + }; + $struct['elements']['trkseg'] = function ($track) { + $track->getSegments()[] = $this->readTrkseg(); + }; + $this->readStruct($struct, $result); + } catch (DomainException $e) { + throw new MalformedGPXException( + $e->getMessage(), $e->getCode(), $e + ); + } + return $result; + } + + /** + * Read a trkseg type. + * + * @see https://www.topografix.com/GPX/1/1/#type_trksegType + * + * @return TrackSegment + * @throws MalformedGPXException If the GPX file was invalid or not supported. + */ + protected function readTrkseg() + { + $result = new TrackSegment(); + $struct = [ + 'elements' => [ + 'trkpt' => function ($segment) { + $segment->getPoints()[] = $this->readWpt(); + } + ] + ]; + if ($this->version == '1.1') + $struct['elements']['extensions'] = function ($segment) { + $this->readExtension($segment->getExtensions()); + }; + $this->readStruct($struct, $result); + return $result; + } + /** * Read a data structure from XML. * diff --git a/src/libgpx/gpxwriter.php b/src/libgpx/gpxwriter.php index 342f89e..1e7878c 100644 --- a/src/libgpx/gpxwriter.php +++ b/src/libgpx/gpxwriter.php @@ -245,6 +245,12 @@ class GPXWriter $this->writeRoute($route, $xml); } } + $tracks = $gpx->getTracks(false); + if ($tracks !== null) { + foreach ($tracks as $track) { + $this->writeTrack($track, $xml); + } + } $xml->endElement(); $xml->endDocument(); $xml->flush(); @@ -518,6 +524,7 @@ class GPXWriter * * @param Route $route The route to write. * @param XMLWriter $xml Where to write. + * @return void */ protected function writeRoute(Route $route, XMLWriter $xml) { @@ -535,4 +542,56 @@ class GPXWriter $xml->endElement(); } + /** + * Write XML for a track element. + * + * @param Track $track The track to write. + * @param XMLWriter $xml Where to write. + * @return void + */ + protected function writeTrack(Track $track, XMLWriter $xml) + { + $xml->startElement('trk'); + $var = $track->getNumber(); + if ($var !== null) + $xml->writeElement('number', $var); + $this->writeDataType($track, $xml); + $var = $track->getSegments(false); + if ($var !== null) { + foreach ($var as $segment) { + $this->writeTrackSegment($segment, $xml); + } + } + $xml->endElement(); + } + + /** + * Write XML for a track segment element. + * + * @param TrackSegment $segment The segment to write. + * @param XMLWriter $xml Where to write. + * @return void + */ + protected function writeTrackSegment(TrackSegment $segment, XMLWriter $xml) + { + $xml->startElement('trkseg'); + $var = $segment->getPoints(false); + if ($var !== null) { + foreach ($var as $point) { + $this->writePoint($point, 'trkpt', $xml); + } + } + if ($this->format == '1.1') { + $var = $segment->getExtensions(false); + if ($var !== null) { + $xml->startElement('extensions'); + foreach ($var as $extension) { + $xml->writeRaw($extension); + } + $xml->endElement(); + } + } + $xml->endElement(); + } + } diff --git a/src/libgpx/track.php b/src/libgpx/track.php new file mode 100644 index 0000000..266d32b --- /dev/null +++ b/src/libgpx/track.php @@ -0,0 +1,109 @@ + + * + * 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; + +/** + * A GPX trk type. + * + * @author Andy Street + */ +class Track extends DataType implements Geographic +{ + + /** + * GPS track number. + * + * @var int + */ + protected $number; + + /** + * A list of track segments. + * + * @var DoublyLinkedList (TrackSegment) + * + */ + protected $segments; + + /** + * Fetch the track number. + * + * @return int|null The route number or null if not set. + */ + public function getNumber() + { + return $this->number; + } + + /** + * Set the number of the track. + * + * @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 an ordered list of track segments. + * + * @param boolean $create Create the list if it does not already exist. + * @return TypedDoublyLinkedList|null A list of track segments. + */ + public function getSegments(bool $create = true) + { + if ($create && $this->segments === null) + $this->segments = new TypedDoublyLinkedList('libgpx\TrackSegment'); + return $this->segments; + } + + /** + * 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->segments !== null) { + foreach ($this->segments as $segment) { + $bounds = $segment->getBounds(); + $result = ($result === null ? $bounds : $result->extend($bounds)); + } + } + return $result; + } + +} diff --git a/src/libgpx/tracksegment.php b/src/libgpx/tracksegment.php new file mode 100644 index 0000000..ccb3078 --- /dev/null +++ b/src/libgpx/tracksegment.php @@ -0,0 +1,93 @@ + + * + * 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; + +/** + * A GPX trkseg type. + * + * @author Andy Street + */ +class TrackSegment implements Geographic +{ + + /** + * A list of track points. + * + * @var TypedDoublyLinkedList (Point) + */ + protected $points; + + /** + * A list of XML snippets describing unsupported data. + * + * @var TypedDoublyLinkedList (string) + */ + protected $extensions; + + /** + * Fetch an ordered list of points that defines this track segment. + * + * @param boolean $create Create the list if it does not already exist. + * @return TypedDoublyLinkedList|null A list points. + */ + public function getPoints(bool $create = true) + { + if ($create && $this->points === null) + $this->points = new TypedDoublyLinkedList('libgpx\Point'); + return $this->points; + } + + /** + * 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 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; + } + +}