/[drupal]/contributions/modules/parser_kml/parser_kml.inc
ViewVC logotype

Contents of /contributions/modules/parser_kml/parser_kml.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Thu Dec 4 21:45:58 2008 UTC (11 months, 3 weeks ago) by alexb
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +55 -1 lines
File MIME type: text/x-php
Register namespaces and attach them to individual feed items (=placemarks).
XMLReader does not seem to have a function that allows us to enumerate all namespace handles and their URI's, so we register them as we go.
FeedAPI does not pass the  object to processors directly, so namespaces can only be passed down the FeedAPI conveyor belt by storing them to db and retrieving them
from within node processors or by attaching them to the feed item. We do the latter here. However, this approach remains a little hacky.
1 <?php
2
3 /**
4 * Parse a given KML string.
5 *
6 * @todo: Add KMZ support (grab code from parser_csv).
7 *
8 * @param $url
9 * URL that points to a valid KML file.
10 * @return
11 * a feed object with parsed and normalized data
12 */
13 function _parser_kml_parse($url) {
14 // Create FeedAPI feed object.
15 $feed = new stdClass();
16 $feed->title = t('Placemarks from !url', array('!url' => $url));
17 $feed->description = '';
18 $feed->options->link = $url;
19 $feed->items = array();
20
21 // Download and parse document @ URL
22 $kml_string = _parser_common_syndication_feedapi_get($url);
23 $reader = new XMLReader();
24 $reader->xml($kml_string);
25 $handler = new parserKMLPlacemarkHandler();
26 parser_kml_get_elements($reader, $handler, "Placemark", $feed->items);
27 $reader->close();
28 return $feed;
29 }
30
31 /**
32 * @param $reader
33 * An instance of XMLReader.
34 * @param $handler
35 * An implementation of the parserKMLHandler interface.
36 * @param $element_name
37 * The name of the element to parse.
38 * @param $items
39 * The array to add parsed elements to.
40 * @param $limit;
41 * The max number of elements to parse.
42 */
43 function parser_kml_get_elements($reader, $handler, $element_name, &$items, $limit = false) {
44 $field = null;
45 while($reader->read()) {
46 if ($reader->nodeType != XMLReader::END_ELEMENT) {
47 if ($reader->localName == $element_name) {
48 $handler->reset($reader);
49 }
50 elseif (!$reader->isEmptyElement) {
51 $field = &$handler->element_process($reader, $field);
52 }
53 }
54 elseif ($reader->localName == $element_name && isset($handler)) {
55 $item = $handler->element_end();
56
57 // If this is a place mark, attach namespaces used in placemark and
58 // reset namespaces.
59 if ($element_name == 'Placemark') {
60 $item->namespaces = parserKMLNamespaces::getNameSpaces();
61 parserKMLNamespaces::resetNameSpaces();
62 }
63 $items[] = $item;
64 if (is_numeric($limit) && count($items) >= $limit) {
65 return;
66 }
67 }
68 }
69 }
70
71 interface parserKMLHandler {
72 /**
73 * @param $reader
74 * An instance of XMLReader
75 */
76 public function reset($reader);
77
78 /**
79 * @param $reader
80 * An instance of XMLReader
81 * @param $field
82 * A reference to a item on the internal feed item
83 * @return
84 * A reference to a item on the internal feed item
85 */
86 public function &element_process($reader, &$field);
87
88 /**
89 * @return
90 * A parsed and processed element to at the the items array.
91 */
92 public function element_end();
93 }
94
95 class parserKMLPlacemarkHandler implements parserKMLHandler {
96 private $item;
97 public $depth;
98
99 function reset($reader) {
100 $this->depth = $reader->depth;
101
102 $this->item = new stdClass();
103 $this->item->title = t('No title');
104 $this->item->description = '';
105 $this->item->options = new stdClass();
106 $this->item->options->kml_type = 'placemark';
107
108 if ($reader->moveToAttribute('id')) {
109 $item->options->placemark = $reader->value;
110 }
111 return;
112 }
113
114 /**
115 * @todo:
116 * * comment
117 * * consolidate null pointer referencing and usage of readInnerXml() for simple
118 * plain text retrievals.
119 *
120 * @param $reader XMLreader object
121 * @param $field, a reference to the field on the current item that we're populating.
122 * @return reference to current field.
123 */
124 function &element_process($reader, &$field) {
125 switch ($reader->localName) {
126 case 'name':
127 return $this->item->title;
128 case 'description':
129 return $this->item->description;
130 case 'link':
131 $this->item->options->original_url = '';
132 return $this->item->options->original_url;
133 case 'Metadata':
134 $this->item->options->metadata = '';
135 $handler = new parserKMLMetadataHandler();
136 $handler->reset($reader);
137 $data = array();
138 parser_kml_get_elements($reader, $handler, 'Metadata', $data, 1);
139 $this->item->options->metadata = array_pop($data);
140 return null;
141 case 'coordinates':
142 // Normalize to FeedAPI convention.
143 list($lat, $lon) = explode(',', $reader->readInnerXml());
144 $this->item->options->location->latitude[0] = $lat;
145 $this->item->options->location->longitude[0] = $lon;
146 return null;
147 default:
148 if ($field !== null) {
149 if ($reader->nodeType === XMLReader::TEXT || $reader->nodeType === XMLReader::CDATA) {
150 $field = $reader->value;
151 }
152 unset($field);
153 }
154 break;
155 }
156 return null;
157 }
158
159 function element_end() {
160 // Generate GUID from URL + hash of serialized placemark. Let's not use the position of this
161 // placemark in feed, uniqueness of item doesn't change w/ position in KML feed.
162 $this->item->options->guid = hash('md5', serialize($this->item));
163 return $this->item;
164 }
165 }
166
167 class parserKMLMetadataHandler implements parserKMLHandler {
168 private $item;
169 private $elem;
170 public $depth;
171
172 function reset($reader) {
173 $this->depth = $reader->depth;
174 $item = array();
175 }
176
177 function &element_process($reader, &$field) {
178 switch ($reader->nodeType) {
179 case XMLReader::ELEMENT:
180 $this->elem = $reader->name;
181 break;
182 case XMLReader::TEXT:
183 if ($this->elem) {
184 parserKMLNamespaces::registerNamespace($reader, $this->elem);
185 $this->item[$this->elem] = $reader->value;
186 $this->elem = false;
187 }
188 break;
189 }
190 return null;
191 }
192
193 function element_end() {
194 return $this->item;
195 }
196 }
197
198 /**
199 * Static class used to register namespaces.
200 */
201 class parserKMLNamespaces {
202 static private $nameSpaces = array();
203
204 /**
205 * Register namespace in given $elem.
206 *
207 * @todo: Find out whether there is not a better way of retrieving
208 * all used name spaces in a KML feed or an item.
209 * Make parserKMLHandler anonymous class
210 * and move this function up to parserKMLHandler
211 *
212 * @param XMLReader $reader
213 * @param string $elem
214 */
215 static function registerNamespace($reader, $elem) {
216 // Resolve namespaces.
217 $e = explode(':', $elem);
218 if (count($e) > 1) {
219 if ($ns = $reader->lookupNamespace($e[0])) {
220 parserKMLNamespaces::$nameSpaces[$e[0]] = $ns;
221 }
222 }
223 }
224
225 /**
226 * Returns all nameSpaces registered during parsing.
227 *
228 * @return Array where keys are namespace handles and values are
229 * namespace URI's
230 */
231 static function getNamespaces() {
232 return parserKMLNamespaces::$nameSpaces;
233 }
234
235 /**
236 * Resets all namespaces.
237 */
238 static function resetNamespaces() {
239 parserKMLNamespaces::$nameSpaces = array();
240 }
241 }

  ViewVC Help
Powered by ViewVC 1.1.2