1 : <?php
2 : /**
3 : * Way.php
4 : * 25-Apr-2011
5 : *
6 : * PHP Version 5
7 : *
8 : * @category Services
9 : * @package Services_Openstreetmap
10 : * @author Ken Guest <kguest@php.net>
11 : * @license BSD http://www.opensource.org/licenses/bsd-license.php
12 : * @version Release: @package_version@
13 : * @link Way.php
14 : */
15 :
16 : /**
17 : * Services_Openstreetmap_Way
18 : *
19 : * @category Services
20 : * @package Services_Openstreetmap
21 : * @author Ken Guest <kguest@php.net>
22 : * @license BSD http://www.opensource.org/licenses/bsd-license.php
23 : * @link Way.php
24 : */
25 : class Services_Openstreetmap_Way extends Services_Openstreetmap_Object
26 : {
27 : protected $type = 'way';
28 : protected $nodes = array();
29 : protected $nodesNew = array();
30 : protected $dirtyNodes = false;
31 :
32 : /**
33 : * Return true if the way can be considered 'closed'.
34 : *
35 : * @return boolean
36 : */
37 : public function isClosed()
38 : {
39 : // Not closed if there's just one node.
40 : // Otherwise a way is considered closed if the first node has
41 : // the same id as the last.
42 4 : if (empty($this->nodes)) {
43 4 : $nodes = $this->getNodes();
44 4 : } else {
45 0 : $nodes = $this->nodes;
46 : }
47 4 : if (sizeof($nodes) == 1) {
48 1 : $closed = false;
49 1 : } else {
50 3 : $closed = ($nodes[0]) == ($nodes[count($nodes) - 1]);
51 : }
52 4 : return $closed;
53 : }
54 :
55 : /**
56 : * Return an array containing the IDs of all nodes in the way.
57 : *
58 : * @return array
59 : */
60 : public function getNodes()
61 : {
62 8 : if (empty($this->nodes)) {
63 8 : $obj = simplexml_load_string($this->xml);
64 8 : $nds = $obj->xpath('//nd');
65 8 : $nodes = array();
66 8 : foreach ($nds as $node) {
67 8 : $nodes[] = (string) $node->attributes()->ref;
68 8 : }
69 8 : $this->nodes = $nodes;
70 8 : }
71 8 : return $this->nodes;
72 : }
73 :
74 : /**
75 : * Add a node to the way.
76 : *
77 : * @param node $node An Services_Openstreetmap_Node object.
78 : *
79 : * @return Services_Openstreetmap_Way
80 : */
81 : public function addNode(Services_Openstreetmap_Node $node)
82 : {
83 1 : $id = $node->getId();
84 1 : $pos = array_search($id, $this->nodes);
85 1 : if ($pos === false) {
86 1 : $this->action = 'modify';
87 1 : $this->nodes[] = $id;
88 1 : $this->dirty = true;
89 1 : $this->dirtyNodes = true;
90 1 : $this->nodesNew[] = $id;
91 1 : }
92 1 : return $this;
93 : }
94 :
95 : /**
96 : * Remove a node from the way.
97 : *
98 : * @param node $node Either a Node object or an id/ref of such an object.
99 : *
100 : * @return Services_Openstreetmap_Way
101 : * @throws Services_Openstreetmap_InvalidArgumentException
102 : */
103 : public function removeNode($node)
104 : {
105 2 : if (empty($this->nodes)) {
106 1 : $this->nodes = $this->getNodes();
107 1 : }
108 2 : $id = null;
109 2 : if (is_numeric($node)) {
110 1 : $id = $node;
111 2 : } elseif ($node instanceof Services_Openstreetmap_Node) {
112 0 : $id = $node->id;
113 0 : } else {
114 1 : throw new Services_Openstreetmap_InvalidArgumentException(
115 : '$node must be either ' .
116 : 'an instance of Services_Openstreetmap_Node or a numeric id'
117 1 : );
118 : }
119 1 : $pos = array_search($id, $this->nodes);
120 1 : if ($pos !== false) {
121 1 : unset($this->nodes[$pos]);
122 1 : $this->dirty = true;
123 1 : $this->action = 'modify';
124 1 : $this->dirtyNodes = true;
125 1 : }
126 1 : return $this;
127 : }
128 :
129 : /**
130 : * Amend osmChangeXml with specific updates pertinent to this Way object.
131 : *
132 : * @param string $xml OSM Change XML as generated by getOsmChangeXml
133 : *
134 : * @return string
135 : * @see getOsmChangeXml
136 : * @link http://wiki.openstreetmap.org/wiki/OsmChange
137 : */
138 : public function osmChangeXml($xml)
139 : {
140 0 : if ($this->dirtyNodes) {
141 0 : $domd = new DomDocument();
142 0 : $domd->loadXml($xml);
143 0 : $xpath = new DomXPath($domd);
144 0 : $nodelist = $xpath->query('//' . $this->action . '/way');
145 0 : $nd = $xpath->query("//{$this->action}/way/nd");
146 :
147 : // Remove nodes if appropriate.
148 0 : for ($i = 0; $i < $nd->length; $i++) {
149 0 : $ref = $nd->item($i)->getAttribute('ref');
150 0 : if (array_search($ref, $this->nodes) === false) {
151 0 : $nodelist->item(0)->removeChild($nd->item($i));
152 0 : }
153 0 : }
154 :
155 : // Add new nodes.
156 0 : foreach ($this->nodesNew as $new) {
157 0 : $el = $domd->createElement('nd');
158 0 : $el->setAttribute('ref', $new);
159 0 : $nodelist->item(0)->appendChild($el);
160 0 : }
161 :
162 : // Remove blank lines in XML - minimise bandwidth usage.
163 0 : return preg_replace(
164 0 : "/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/",
165 0 : '',
166 0 : $domd->saveXml($nodelist->item(0))
167 0 : );
168 :
169 :
170 : return $domd->saveXml($nodelist->item(0));
171 : } else {
172 0 : return $xml;
173 : }
174 : }
175 :
176 : /**
177 : * Return address [tags], as an array, if set on a closed way.
178 : *
179 : * @return array
180 : */
181 : public function getAddress()
182 : {
183 1 : if (!$this->isClosed()) {
184 0 : return null;
185 : }
186 :
187 : $ret = array(
188 1 : 'addr_housename' => null,
189 1 : 'addr_housenumber' => null,
190 1 : 'addr_street' => null,
191 1 : 'addr_city' => null,
192 : 'addr_country' => null
193 1 : );
194 1 : $tags = $this->getTags();
195 1 : $detailsSet = false;
196 1 : foreach ($tags as $key => $value) {
197 1 : if (strpos($key, 'addr') === 0) {
198 1 : $ret[str_replace(':', '_', $key)] = $value;
199 1 : $detailsSet = true;
200 1 : }
201 1 : }
202 1 : if (!$detailsSet) {
203 0 : $ret = null;
204 0 : }
205 1 : return $ret;
206 : }
207 :
208 : }
209 : // vim:set et ts=4 sw=4:
210 : ?>
|