]> git.street.me.uk Git - andy/gpx.git/commitdiff
Add support for GPX wpt type
authorAndy Street <andy@street.me.uk>
Tue, 18 Dec 2018 15:46:01 +0000 (15:46 +0000)
committerAndy Street <andy@street.me.uk>
Tue, 18 Dec 2018 15:46:01 +0000 (15:46 +0000)
docs/samples/full-1.0.gpx
docs/samples/full-1.1.gpx
src/libgpx/gpx.php
src/libgpx/gpxreader.php
src/libgpx/gpxwriter.php
src/libgpx/point.php [new file with mode: 0644]
src/libgpx/xmlreader.php

index 4debbf26fd548daac7d3a5a261c4701d0d09e9ea..e2424da86bef690f0371e66c09e0f73fbdfa9ca7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<gpx version="1.0" creator="libgpx" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd" xmlns="http://www.topografix.com/GPX/1/0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<gpx version="1.0" creator="libgpx" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd" xmlns="http://www.topografix.com/GPX/1/0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:custom="https://www.example.com/schema/custom">
   <name>full-1.0</name>
   <desc>This is a sample file containing all parts of the GPX 1.0 specification supported by libgpx.</desc>
   <author>John Doe</author>
@@ -9,4 +9,26 @@
   <time>2018-11-25T10:07:47Z</time>
   <keywords>Example, GPX</keywords>
   <bounds minlat="-1.234" minlon="-1.234" maxlat="1.234" maxlon="1.234"/>
+  <wpt lat="50.750130" lon="-1.072391">
+    <ele>10.3</ele>
+    <time>2018-11-25T10:07:47Z</time>
+    <magvar>12.3</magvar>
+    <geoidheight>10</geoidheight>
+    <name>Horse Sands Fort</name>
+    <cmt>Fort</cmt>
+    <desc>A fort in the Solent</desc>
+    <src>Guess</src>
+    <url>https://www.example.com/fort</url>
+    <urlname>More Information</urlname>
+    <sym>Flag, Blue</sym>
+    <type>Nautical</type>
+    <fix>3d</fix>
+    <sat>6</sat>
+    <hdop>1</hdop>
+    <vdop>2</vdop>
+    <pdop>3</pdop>
+    <ageofdgpsdata>86400</ageofdgpsdata>
+    <dgpsid>13</dgpsid>
+    <custom:checkedBy>Jane Doe</custom:checkedBy>
+  </wpt>
 </gpx>
index 3d8c9f2e20311ba3f7feef4f6187fadd376e6dfa..40a99c844bb39c0a7f8482a45332b0153d4e3c0c 100644 (file)
       <custom:checkedBy>Jane Doe</custom:checkedBy>\r
     </extensions>\r
   </metadata>\r
+  <wpt lat="50.750130" lon="-1.072391">\r
+    <ele>10.3</ele>\r
+    <time>2018-11-25T10:07:47Z</time>\r
+    <magvar>12.3</magvar>\r
+    <geoidheight>10</geoidheight>\r
+    <name>Horse Sands Fort</name>\r
+    <cmt>Fort</cmt>\r
+    <desc>A fort in the Solent</desc>\r
+    <src>Guess</src>\r
+    <link href="https://www.example.com/fort">\r
+      <text>More Information</text>\r
+      <type>text/html</type>\r
+    </link>\r
+    <sym>Flag, Blue</sym>\r
+    <type>Nautical</type>\r
+    <fix>3d</fix>\r
+    <sat>6</sat>\r
+    <hdop>1</hdop>\r
+    <vdop>2</vdop>\r
+    <pdop>3</pdop>\r
+    <ageofdgpsdata>86400</ageofdgpsdata>\r
+    <dgpsid>13</dgpsid>\r
+    <extensions>\r
+      <custom:checkedBy>Jane Doe</custom:checkedBy>\r
+    </extensions>\r
+  </wpt>\r
 </gpx>\r
index 4404d942bd5b9bc07a70dac080d3977dab0518d0..4a6e1ffe878394a99719f4ac49d83f69e88e34b7 100644 (file)
@@ -99,6 +99,13 @@ class GPX implements Geographic
    */
   protected $metadataExtensions;
 
+  /**
+   * A list of waypoints.
+   *
+   * @var TypedDoublyLinkedList (Point)
+   */
+  protected $waypoints;
+
   /**
    * Create a new instance.
    */
@@ -107,6 +114,7 @@ class GPX implements Geographic
     $this->links = new TypedDoublyLinkedList('libgpx\Link');
     $this->keywords = new TypedDoublyLinkedList('string');
     $this->metadataExtensions = new TypedDoublyLinkedList('string');
+    $this->waypoints = new TypedDoublyLinkedList('libgpx\Point');
   }
 
 
@@ -265,6 +273,16 @@ class GPX implements Geographic
     return $this->metadataExtensions;
   }
 
+  /**
+   * Fetch a list of waypoints.
+   *
+   * @return TypedDoublyLinkedList A list of waypoints.
+   */
+  public function getWaypoints()
+  {
+    return $this->waypoints;
+  }
+
   /**
    * Fetch a bounding box that covers the feature.
    *
@@ -273,9 +291,12 @@ class GPX implements Geographic
    */
   public function getBounds()
   {
-    //TODO: Implement this properly when waypoints, routes and tracks have
-    //      been implemented.
-    return null;
+    $result = null;
+    foreach ($this->waypoints as $waypoint) {
+      $bounds = $waypoint->getBounds();
+      $result = ($result === null ? $bounds : $result->extend($bounds));
+    }
+    return $result;
   }
 
 }
index 0168f243707d7402fb50bdb573bd260f7378cf31..f243513f4a4cf3b8f5ba9af258339f8a6e0ace4c 100644 (file)
@@ -26,6 +26,7 @@ namespace libgpx;
 
 use \DateTime;
 use \DateTimeZone;
+use \DomainException;
 use \Exception;
 use \InvalidArgumentException;
 use \RuntimeException;
@@ -176,7 +177,11 @@ class GPXReader
     $result->setCreator($this->xml->getAttribute('creator'));
 
     $struct = [
-      'elements' => []
+      'elements' => [
+        'wpt' => function ($gpx) {
+          $gpx->getWaypoints()[] = $this->readWpt();
+        }
+      ]
     ];
     if ($this->version == '1.1') {
       $struct['elements']['metadata'] = function ($gpx) {
@@ -208,7 +213,7 @@ class GPXReader
       $struct['elements']['url'] = function ($gpx, &$state) {
         $href = $this->readXSDString();
         $links = $gpx->getLinks();
-        if ($links->isEmpty) {
+        if ($links->isEmpty()) {
           $link = new Link($href);
           if (isset($state['urlname'])) {
             $link->setText($state['urlname']);
@@ -222,7 +227,7 @@ class GPXReader
       $struct['elements']['urlname'] = function ($gpx, &$state) {
         $text = $this->readXSDString();
         $links = $gpx->getLinks();
-        if ($links->isEmpty) {
+        if ($links->isEmpty()) {
           $state['urlname'] = $text;
         } else {
           $links->bottom()->setText($text);
@@ -412,6 +417,121 @@ class GPXReader
     $this->readStruct($struct, $list);
   }
 
+  /**
+   * Read a wpt type.
+   *
+   * @see https://www.topografix.com/GPX/1/1/#type_wptType
+   *
+   * @return Point
+   * @throws MalformedGPXException If the GPX file was invalid or not supported.
+   */
+  protected function readWpt()
+  {
+    try {
+      $result = new Point(
+        $this->string2float($this->xml->getAttribute('lat')),
+        $this->string2float($this->xml->getAttribute('lon'))
+      );
+      $struct = [
+        'elements' => [
+          'ele' => function ($point) {
+            $point->setEle($this->string2float($this->readXSDString()));
+          },
+          'time' => function ($point) {
+            $point->setTime($this->string2DateTime($this->readXSDString()));
+          },
+          'magvar' => function ($point) {
+            $point->setMagvar($this->string2float($this->readXSDString()));
+          },
+          'geoidheight' => function ($point) {
+            $point->setGeoidHeight($this->string2float($this->readXSDString()));
+          },
+          'name' => function ($point) {
+            $point->setName($this->readXSDString());
+          },
+          'cmt' => function ($point) {
+            $point->setComment($this->readXSDString());
+          },
+          'desc' => function ($point) {
+            $point->setDescription($this->readXSDString());
+          },
+          'src' => function ($point) {
+            $point->setSource($this->readXSDString());
+          },
+          'sym' => function ($point) {
+            $point->setSymbol($this->readXSDString());
+          },
+          'type' => function ($point) {
+            $point->setType($this->readXSDString());
+          },
+          'fix' => function ($point) {
+            $point->setFix($this->readXSDString());
+          },
+          'sat' => function ($point) {
+            $point->setSatellites($this->string2int($this->readXSDString()));
+          },
+          'hdop' => function ($point) {
+            $point->setHdop($this->string2float($this->readXSDString()));
+          },
+          'vdop' => function ($point) {
+            $point->setVdop($this->string2float($this->readXSDString()));
+          },
+          'pdop' => function ($point) {
+            $point->setPdop($this->string2float($this->readXSDString()));
+          },
+          'ageofdgpsdata' => function ($point) {
+            $point->setAgeOfDGPSData($this->string2float($this->readXSDString()));
+          },
+          'dgpsid' => function ($point) {
+            $point->setDGPSId($this->string2int($this->readXSDString()));
+          }
+        ]
+      ];
+      if ($this->version == '1.1') {
+        $struct['elements']['link'] = function ($point) {
+          $point->getLinks()[] = $this->readLink();
+        };
+        $struct['elements']['extensions'] = function ($point) {
+          $this->readExtension($point->getExtensions());
+        };
+      } else {
+        $struct['elements']['url'] = function ($point, &$state) {
+          $href = $this->readXSDString();
+          $links = $point->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 ($point, &$state) {
+          $text = $this->readXSDString();
+          $links = $point->getLinks();
+          if ($links->isEmpty()) {
+            $state['urlname'] = $text;
+          } else {
+            $links->bottom()->setText($text);
+          }
+        };
+        $struct['extensions'] = function ($point) {
+          $point->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.
    *
@@ -548,4 +668,36 @@ class GPXReader
     return $result;
   }
 
+  /**
+   * Convert a string to a float.
+   *
+   * @param string $value The string value to convert.
+   * @return float
+   * @throws MalformedGPXException If the value is not numeric.
+   */
+  protected function string2float(string $value)
+  {
+    if (!is_numeric($value))
+      throw new MalformedGPXException(
+        sprintf('Expected decimal value but got "%s"', $value)
+      );
+    return floatval($value);
+  }
+
+  /**
+   * Convert a string to a int.
+   *
+   * @param string $value The string value to convert.
+   * @return int
+   * @throws MalformedGPXException If the value is not numeric.
+   */
+  protected function string2int(string $value)
+  {
+    if (!is_numeric($value))
+      throw new MalformedGPXException(
+        sprintf('Expected decimal value but got "%s"', $value)
+      );
+    return intval($value);
+  }
+
 }
index b4fd51f484a7a838a6e5ce2cb582e99ccc5ffac3..7622fdb9586e62ae5332e47a193fadfdb6726fe4 100644 (file)
@@ -233,6 +233,9 @@ class GPXWriter
       $namespace . ' ' . $schema
     );
     $this->writeMetadata($gpx, $xml);
+    foreach ($gpx->getWaypoints() as $wpt) {
+      $this->writePoint($wpt, 'wpt', $xml);
+    }
     $xml->endElement();
     $xml->endDocument();
     $xml->flush();
@@ -398,4 +401,89 @@ class GPXWriter
     return $timestamp->format($format);
   }
 
+  /**
+   * Generate XML for GPX wpt type.
+   *
+   * @param Point $point The point to write.
+   * @param string $element The XML element name (e.g. <wpt>)
+   * @param XMLWriter $xml Where to write the point.
+   * @return void
+   */
+  protected function writePoint(Point $point, string $element, XMLWriter $xml)
+  {
+    $xml->startElement($element);
+    $xml->writeAttribute('lat', $point->getLatitude());
+    $xml->writeAttribute('lon', $point->getLongitude());
+    $val = $point->getEle();
+    if ($val !== null)
+      $xml->writeElement('ele', $val);
+    $val = $point->getTime();
+    if ($val !== null)
+      $xml->writeElement('time', $this->createTimestamp($val));
+    $val = $point->getMagvar();
+    if ($val !== null)
+      $xml->writeElement('magvar', $val);
+    $val = $point->getGeoidHeight();
+    if ($val !== null)
+      $xml->writeElement('geoidheight', $val);
+    $var = $point->getName();
+    if ($var !== null)
+      $xml->writeElement('name', $var);
+    $var = $point->getComment();
+    if ($var !== null)
+      $xml->writeElement('cmt', $var);
+    $var = $point->getDescription();
+    if ($var !== null)
+      $xml->writeElement('desc', $var);
+    $var = $point->getSource();
+    if ($var !== null)
+      $xml->writeElement('src', $var);
+    $var = $point->getLinks(false);
+    if ($var !== null) {
+      foreach($var as $link) {
+        $this->writeLink($link, $xml);
+        if ($this->format == '1.0')
+          break;
+      }
+    }
+    $var = $point->getSymbol();
+    if ($var !== null)
+      $xml->writeElement('sym', $var);
+    $var = $point->getType();
+    if ($var !== null)
+      $xml->writeElement('type', $var);
+    $var = $point->getFix();
+    if ($var !== null)
+      $xml->writeElement('fix', $var);
+    $var = $point->getSatellites();
+    if ($var !== null)
+      $xml->writeElement('sat', $var);
+    $var = $point->getHdop();
+    if ($var !== null)
+      $xml->writeElement('hdop', $var);
+    $var = $point->getVdop();
+    if ($var !== null)
+      $xml->writeElement('vdop', $var);
+    $var = $point->getPdop();
+    if ($var !== null)
+      $xml->writeElement('pdop', $var);
+    $var = $point->getAgeOfDGPSData();
+    if ($var !== null)
+      $xml->writeElement('ageofdgpsdata', $var);
+    $var = $point->getDGPSId();
+    if ($var !== null)
+      $xml->writeElement('dgpsid', $var);
+    $var = $point->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();
+    }
+    $xml->endElement();
+  }
+
 }
diff --git a/src/libgpx/point.php b/src/libgpx/point.php
new file mode 100644 (file)
index 0000000..7568821
--- /dev/null
@@ -0,0 +1,688 @@
+<?php
+/**
+ * point.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;
+use \DateTimeInterface;
+
+/**
+ * A geographic point.
+ *
+ * @see https://www.topografix.com/GPX/1/1/#type_wptType
+ *
+ * @author Andy Street <andy@street.me.uk>
+ */
+class Point implements Geographic
+{
+
+  /**
+   * The latitude of the point.
+   *
+   * @var float
+   */
+  protected $lat;
+
+  /**
+   * The longitude of the point.
+   *
+   * @var float
+   */
+  protected $lon;
+
+  /**
+   * Elevation (in meters) of the point.
+   *
+   * @var float
+   */
+  protected $ele;
+
+  /**
+   * Creation/modification timestamp for element.
+   *
+   * Date and time in are in Univeral Coordinated Time (UTC), not local time!
+   * Conforms to ISO 8601 specification for date/time representation.
+   * Fractional seconds are allowed for millisecond timing in tracklogs.
+   *
+   * @var DateTimeInterface
+   */
+  protected $time;
+
+  /**
+   * Magnetic variation (in degrees) at the point.
+   *
+   * @var float
+   */
+  protected $magvar;
+
+  /**
+   * Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid.
+   * As defined in NMEA GGA message.
+   *
+   * @var float
+   */
+  protected $geoidheight;
+
+  /**
+   * The name of the point.
+   *
+   * @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 point.
+   *
+   * @var TypedDoublyLinkedList (Link)
+   */
+  protected $links;
+
+  /**
+   * Text of GPS symbol name.
+   *
+   * For interchange with other programs, use the exact spelling of the symbol
+   * as displayed on the GPS. If the GPS abbreviates words, spell them out.
+   *
+   * @var string
+   */
+  protected $symbol;
+
+  /**
+   * Type (classification) of the point.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * Type of GPS fix.
+   *
+   * "none", "2d", "3d", "dgps" or "pps".
+   *
+   * None means GPS had no fix. To signify "the fix info is unknown, leave out
+   * fixType entirely. pps = military signal used.
+   *
+   * @var string
+   */
+  protected $fix;
+
+  /**
+   * Number of satellites used to calculate the GPX fix.
+   *
+   * @var int
+   */
+  protected $satellites;
+
+  /**
+   * Horizontal dilution of precision.
+   *
+   * @var float
+   */
+  protected $hdop;
+
+  /**
+   * Vertical dilution of precision.
+   *
+   * @var float
+   */
+  protected $vdop;
+
+  /**
+   * Position dilution of precision.
+   *
+   * @var float
+   */
+  protected $pdop;
+
+  /**
+   * Number of seconds since last DGPS update.
+   *
+   * @var float
+   */
+  protected $ageofdgpsdata;
+
+  /**
+   * ID of DGPS station used in differential correction.
+   *
+   * @var int
+   */
+  protected $dgpsid;
+
+  /**
+   * A list of XML snippets describing unsupported data.
+   *
+   * @var TypedDoublyLinkedList (string)
+   */
+  protected $extensions;
+
+  /**
+   * Create a new point.
+   *
+   * @param float $lat Latitude
+   * @param float $lon Longitude
+   * @throws DomainException If not a valid latitude or longitude.
+   */
+  public function __construct(float $lat, float $lon)
+  {
+    $this->setLatitude($lat);
+    $this->setLongitude($lon);
+  }
+
+  /**
+   * Fetch the latitude of the point.
+   *
+   * @return float
+   */
+  public function getLatitude()
+  {
+    return $this->lat;
+  }
+
+  /**
+   * Set the latitude of the point.
+   *
+   * @param float $lat Latitude
+   * @return void
+   * @throws DomainException If not a valid latitude.
+   */
+  public function setLatitude(float $lat)
+  {
+    if ($lat < -90 || $lat > 90)
+      throw new DomainException(
+        sprintf('Value %s is not in the range -90-+90.', $lat)
+      );
+    $this->lat = $lat;
+  }
+
+  /**
+   * Fetch the longitude of the point.
+   *
+   * @return float
+   */
+  public function getLongitude()
+  {
+    return $this->lon;
+  }
+
+  /**
+   * Set the longitude of the point.
+   *
+   * @param float $lon Longitude
+   * @return void
+   * @throws DomainException If not a valid longitude.
+   */
+  public function setLongitude(float $lon)
+  {
+    if ($lon < -180 || $lon > 180)
+      throw new DomainException(
+        sprintf('Value %s is not in the range -180-+180.', $lon)
+      );
+    $this->lon = $lon;
+  }
+
+  /**
+   * Fetch the elevation of the point.
+   *
+   * @return float|null The elevation in meters or null if not set.
+   */
+  public function getEle()
+  {
+    return $this->ele;
+  }
+
+  /**
+   * Set the elevation of the point.
+   *
+   * @param float $ele The elevation in meters or null to delete.
+   * @return void
+   */
+  public function setEle(float $ele = null)
+  {
+    $this->ele = $ele;
+  }
+
+  /**
+   * Fetch the creation time of the point.
+   *
+   * @return DateTimeInterface|null The time or null if not set.
+   */
+  public function getTime()
+  {
+    return $this->time;
+  }
+
+  /**
+   * Set the creation time of the point.
+   *
+   * @param DateTimeInterface $time The creation time or null to delete.
+   * @return void
+   */
+  public function setTime(DateTimeInterface $time = null)
+  {
+    $this->time = $time;
+  }
+
+  /**
+   * Fetch the magnetic variation.
+   *
+   * @return float|null The magnetic variation in degrees or null if not set.
+   */
+  public function getMagvar()
+  {
+    return $this->magvar;
+  }
+
+  /**
+   * Set the magnetic variation.
+   *
+   * @param float $magvar The magnetic variation in degrees or null to delete.
+   * @return void
+   * @throws DomainException if not in range 0 <= value < 360
+   */
+  public function setMagvar(float $magvar = null)
+  {
+    if (
+      $magvar !== null
+      && ($magvar < 0 || $magvar >= 360)
+    ) {
+      throw new DomainException(
+        sprintf('Value %s is not in the range 0 <= value < 360.', $lon)
+      );
+    }
+    $this->magvar = $magvar;
+  }
+
+  /**
+   * Fetch the geoid height at the point.
+   *
+   * @return float The geoid height in meters.
+   */
+  public function getGeoidHeight()
+  {
+    return $this->geoidheight;
+  }
+
+  /**
+   * Set the geoid height at the point.
+   *
+   * @param float $geoidheight The geoid height in meters.
+   * @return void
+   */
+  public function setGeoidHeight(float $geoidheight = null)
+  {
+    $this->geoidheight = $geoidheight;
+  }
+
+  /**
+   * Fetch the name of the point.
+   *
+   * @return string|null The name or null if not set.
+   */
+  public function getName()
+  {
+    return $this->name;
+  }
+
+  /**
+   * Set the name of the point.
+   *
+   * @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 point.
+   *
+   * @return string The comment or null if not set.
+   */
+  public function getComment()
+  {
+    return $this->comment;
+  }
+
+  /**
+   * Set a comment for the point.
+   *
+   * @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 point.
+   *
+   * @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 point.
+   *
+   * @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 waypoint symbol.
+   *
+   * @return string|null The symbol or null if not set.
+   */
+  public function getSymbol()
+  {
+    return $this->symbol;
+  }
+
+  /**
+   * Set the symbol representing this point.
+   *
+   * @param string $symbol The name of the symbol or null to delete.
+   * @return void
+   */
+  public function setSymbol(string $symbol = null)
+  {
+    $this->symbol = $symbol;
+  }
+
+  /**
+   * Fetch the type of the point.
+   *
+   * @return string|null The type or null if not set.
+   */
+  public function getType()
+  {
+    return $this->type;
+  }
+
+  /**
+   * Set the type of the point.
+   *
+   * @param string $type The type or null to delete.
+   * @return void
+   */
+  public function setType(string $type = null)
+  {
+    $this->type = $type;
+  }
+
+  /**
+   * Fetch the fix type for this point.
+   *
+   * @return string|null The fix type or null if not set.
+   */
+  public function getFix()
+  {
+    return $this->fix;
+  }
+
+  /**
+   * Set the fix type.
+   *
+   * @param string $fix "none", "2d", "3d", "dgps", "pps" or null to delete.
+   * @return void
+   * @throws DomainException If an invalid fix type is passed.
+   */
+  public function setFix(string $fix = null)
+  {
+    if ($fix !== null && !in_array($fix, ['none', '2d', '3d', 'dgps', 'pps']))
+      throw new DomainException(sprintf('Unknown fix type "%s"', $fix));
+    $this->fix = $fix;
+  }
+
+  /**
+   * Fetch the number of satellites used to calculate the fix.
+   *
+   * @return int The number of satellites or null if not set.
+   */
+  public function getSatellites()
+  {
+    return $this->satellites;
+  }
+
+  /**
+   * Set the number of satellites used to calculate the fix.
+   *
+   * @param int $satellites The number if satellites or null to delete.
+   * @return void
+   * @throws DomainException If $satellites < 0
+   */
+  public function setSatellites(int $satellites = null)
+  {
+    if ($satellites !== null && $satellites < 0)
+      throw new DomainException(
+        sprintf('Satellites must be >= 0 but got %s', $satellites)
+      );
+    $this->satellites = $satellites;
+  }
+
+  /**
+   * Fetch the hdop of the point.
+   *
+   * @return float|null The hdop or null if not set.
+   */
+  public function getHdop()
+  {
+    return $this->hdop;
+  }
+
+  /**
+   * Set the hdop of the point.
+   *
+   * @param float $hdop The hdop or null to delete.
+   * @return void
+   */
+  public function setHdop(float $hdop = null)
+  {
+    $this->hdop = $hdop;
+  }
+
+  /**
+   * Fetch the vdop of the point.
+   *
+   * @return float|null The vdop or null if not set.
+   */
+  public function getVdop()
+  {
+    return $this->vdop;
+  }
+
+  /**
+   * Set the vdop of the point.
+   *
+   * @param float $vdop The vdop or null to delete.
+   * @return void
+   */
+  public function setVdop(float $vdop = null)
+  {
+    $this->vdop = $vdop;
+  }
+
+  /**
+   * Fetch the pdop of the point.
+   *
+   * @return float|null The pdop or null if not set.
+   */
+  public function getPdop()
+  {
+    return $this->pdop;
+  }
+
+  /**
+   * Set the pdop of the point.
+   *
+   * @param float $pdop The pdop or null to delete.
+   * @return void
+   */
+  public function setPdop(float $pdop = null)
+  {
+    $this->pdop = $pdop;
+  }
+
+  /**
+   * Fetch the age of the DGPS data.
+   *
+   * @return float The age of DGPS data in seconds or null if not set.
+   */
+  public function getAgeOfDGPSData()
+  {
+    return $this->ageofdgpsdata;
+  }
+
+  /**
+   * Set the age of the DGPS data.
+   *
+   * @param float $ageofdgpsdata The age in seconds or null to delete.
+   * @return void
+   */
+  public function setAgeOfDGPSData(float $ageofdgpsdata = null)
+  {
+    $this->ageofdgpsdata = $ageofdgpsdata;
+  }
+
+  /**
+   * Fetch the id of the DGPS station.
+   *
+   * @return int The DGPS id or null if not set.
+   */
+  public function getDGPSId()
+  {
+    return $this->dgpsid;
+  }
+
+  /**
+   * Set the id of the DGPS station.
+   *
+   * @param int $dgpsid The station id.
+   * @return void
+   * @throws DomainException If the station id < 0 or > 1023.
+   */
+  public function setDGPSId(int $dgpsid = null)
+  {
+    if ($dgpsid !== null && ($dgpsid < 0 || $dgpsid > 1023))
+      throw new DomainException(
+        sprintf('DGPS id must be >= 0 and <= 1023 but got %s', $dgpsid)
+      );
+    return $this->dgpsid = $dgpsid;
+  }
+
+  /**
+   * 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()
+  {
+    return new Bounds($this->lat, $this->lon, $this->lat, $this->lon);
+  }
+}
index f3f282945e14e8d2cfbc8d074d6ed2e6cad7180d..2b578beb8dd10d187d66e07a0f4b460179694009 100644 (file)
@@ -98,9 +98,9 @@ class XMLReader extends \XMLReader
    * @return boolean Returns true on success or false on failure.
    * @throws LibXMLException If an error of sufficient magnitude is detected.
    */
-  public function next() {
+  public function next($localname = null) {
     libxml_clear_errors();
-    $result = parent::next();
+    $result = ($localname === null ? parent::next() : parent::next($localname));
     $error = libxml_get_last_error();
     if ($error instanceof LibXMLError && $error->level >= $this->errorLevel)
       throw new LibXMLException($error);