Passed
Push — master ( 58539d...96b0d3 )
by Mark
01:31
created

GPX   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 36
eloc 89
dl 0
loc 165
rs 9.52
c 0
b 0
f 0
1
<?php
2
/*
3
 * Copyright (c) Patrick Hayes
4
 *
5
 * This code is open-source and licenced under the Modified BSD License.
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
/**
11
 * PHP Geometry/GPX encoder/decoder
12
 */
13
class GPX extends GeoAdapter
14
{
15
  private $namespace = FALSE;
16
  private $nss = ''; // Name-space string. eg 'georss:'
17
18
  /**
19
   * Read GPX string into geometry objects
20
   *
21
   * @param string $gpx A GPX string
22
   *
23
   * @return Geometry|GeometryCollection
24
   */
25
  public function read($gpx) {
26
    return $this->geomFromText($gpx);
27
  }
28
29
  /**
30
   * Serialize geometries into a GPX string.
31
   *
32
   * @param Geometry $geometry
33
   *
34
   * @return string The GPX string representation of the input geometries
35
   */
36
  public function write(Geometry $geometry, $namespace = FALSE) {
37
    if ($geometry->isEmpty()) return NULL;
38
    if ($namespace) {
39
      $this->namespace = $namespace;
40
      $this->nss = $namespace.':';    
41
    }
42
    return '<'.$this->nss.'gpx creator="geoPHP" version="1.0">'.$this->geometryToGPX($geometry).'</'.$this->nss.'gpx>';
43
  }
44
  
45
  public function geomFromText($text) {
46
    // Change to lower-case and strip all CDATA
47
    $text = strtolower($text);
48
    $text = preg_replace('/<!\[cdata\[(.*?)\]\]>/s','',$text);
49
    
50
    // Load into DOMDocument
51
    $xmlobj = new DOMDocument();
52
    @$xmlobj->loadXML($text);
53
    if ($xmlobj === false) {
54
      throw new Exception("Invalid GPX: ". $text);
55
    }
56
    
57
    $this->xmlobj = $xmlobj;
58
    try {
59
      $geom = $this->geomFromXML();
60
    } catch(InvalidText $e) {
61
        throw new Exception("Cannot Read Geometry From GPX: ". $text);
62
    } catch(Exception $e) {
63
        throw $e;
64
    }
65
66
    return $geom;
67
  }
68
  
69
  protected function geomFromXML() {
70
    $geometries = array();
71
    $geometries = array_merge($geometries, $this->parseWaypoints());
72
    $geometries = array_merge($geometries, $this->parseTracks());
73
    $geometries = array_merge($geometries, $this->parseRoutes());
74
    
75
    if (empty($geometries)) {
76
      throw new Exception("Invalid / Empty GPX");
77
    }
78
    
79
    return geoPHP::geometryReduce($geometries); 
80
  }
81
  
82
  protected function childElements($xml, $nodename = '') {
83
    $children = array();
84
    foreach ($xml->childNodes as $child) {
85
      if ($child->nodeName == $nodename) {
86
        $children[] = $child;
87
      }
88
    }
89
    return $children;
90
  }
91
  
92
  protected function parseWaypoints() {
93
    $points = array();
94
    $wpt_elements = $this->xmlobj->getElementsByTagName('wpt');
95
    foreach ($wpt_elements as $wpt) {
96
      $lat = $wpt->attributes->getNamedItem("lat")->nodeValue;
97
      $lon = $wpt->attributes->getNamedItem("lon")->nodeValue;
98
      $points[] = new Point($lon, $lat);
99
    }
100
    return $points;
101
  }
102
  
103
  protected function parseTracks() {
104
    $lines = array();
105
    $trk_elements = $this->xmlobj->getElementsByTagName('trk');
106
    foreach ($trk_elements as $trk) {
107
      $components = array();
108
      foreach ($this->childElements($trk, 'trkseg') as $trkseg) {
109
        foreach ($this->childElements($trkseg, 'trkpt') as $trkpt) {
110
          $lat = $trkpt->attributes->getNamedItem("lat")->nodeValue;
111
          $lon = $trkpt->attributes->getNamedItem("lon")->nodeValue;
112
          $components[] = new Point($lon, $lat);
113
        }
114
      }
115
      if ($components) {$lines[] = new LineString($components);}
116
    }
117
    return $lines;
118
  }
119
  
120
  protected function parseRoutes() {
121
    $lines = array();
122
    $rte_elements = $this->xmlobj->getElementsByTagName('rte');
123
    foreach ($rte_elements as $rte) {
124
      $components = array();
125
      foreach ($this->childElements($rte, 'rtept') as $rtept) {
126
        $lat = $rtept->attributes->getNamedItem("lat")->nodeValue;
127
        $lon = $rtept->attributes->getNamedItem("lon")->nodeValue;
128
        $components[] = new Point($lon, $lat);
129
      }
130
      $lines[] = new LineString($components);
131
    }
132
    return $lines;
133
  }
134
  
135
  protected function geometryToGPX($geom) {
136
    $type = strtolower($geom->getGeomType());
137
    switch ($type) {
138
      case 'point':
139
        return $this->pointToGPX($geom);
140
        break;
141
      case 'linestring':
142
        return $this->linestringToGPX($geom);
143
        break;
144
      case 'polygon':
145
      case 'multipoint':
146
      case 'multilinestring':
147
      case 'multipolygon':
148
      case 'geometrycollection':
149
        return $this->collectionToGPX($geom);
150
        break;
151
    }
152
  }
153
  
154
  private function pointToGPX($geom) {
155
    return '<'.$this->nss.'wpt lat="'.$geom->getY().'" lon="'.$geom->getX().'" />';
156
  }
157
  
158
  private function linestringToGPX($geom) {
159
    $gpx = '<'.$this->nss.'trk><'.$this->nss.'trkseg>';
160
    
161
    foreach ($geom->getComponents() as $comp) {
162
      $gpx .= '<'.$this->nss.'trkpt lat="'.$comp->getY().'" lon="'.$comp->getX().'" />';
163
    }
164
    
165
    $gpx .= '</'.$this->nss.'trkseg></'.$this->nss.'trk>';
166
    
167
    return $gpx;
168
  }
169
  
170
  public function collectionToGPX($geom) {
171
    $gpx = '';
172
    $components = $geom->getComponents();
173
    foreach ($geom->getComponents() as $comp) {
174
      $gpx .= $this->geometryToGPX($comp);
175
    }
176
    
177
    return $gpx;
178
  }
179
180
}
181