| | 1 | /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD |
|---|
| | 2 | * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|---|
| | 3 | * full text of the license. */ |
|---|
| | 4 | |
|---|
| | 5 | /** |
|---|
| | 6 | * @requires OpenLayers/Format/XML.js |
|---|
| | 7 | * @requires OpenLayers/Format/GML/v3.js |
|---|
| | 8 | */ |
|---|
| | 9 | |
|---|
| | 10 | /** |
|---|
| | 11 | * Class: OpenLayers.Format.Atom |
|---|
| | 12 | * Read/write Atom + GeoRSS parser. Create a new instance with the |
|---|
| | 13 | * <OpenLayers.Format.Atom> constructor. |
|---|
| | 14 | * |
|---|
| | 15 | * Inherits from: |
|---|
| | 16 | * - <OpenLayers.Format.XML> |
|---|
| | 17 | */ |
|---|
| | 18 | OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { |
|---|
| | 19 | |
|---|
| | 20 | /** |
|---|
| | 21 | * Property: namespaces |
|---|
| | 22 | * {Object} Mapping of namespace aliases to namespace URIs. |
|---|
| | 23 | */ |
|---|
| | 24 | namespaces: { |
|---|
| | 25 | atom: "http://www.w3.org/2005/Atom", |
|---|
| | 26 | georss: "http://www.georss.org/georss", |
|---|
| | 27 | gml: "http://www.opengis.net/gml" |
|---|
| | 28 | }, |
|---|
| | 29 | |
|---|
| | 30 | /** |
|---|
| | 31 | * Property: defaultPrefix |
|---|
| | 32 | * {String} The namespace alias to use for node names with no prefix. |
|---|
| | 33 | * Default is "atom". |
|---|
| | 34 | */ |
|---|
| | 35 | defaultPrefix: "atom", |
|---|
| | 36 | |
|---|
| | 37 | /** |
|---|
| | 38 | * Property: categoryScheme |
|---|
| | 39 | * {String} An IRI that identifies a categorization scheme for parsing |
|---|
| | 40 | * of atom:category elements. Default is |
|---|
| | 41 | * http://namespace.openlayers.org/atom#attributes. |
|---|
| | 42 | */ |
|---|
| | 43 | categoryScheme: "http://namespace.openlayers.org/atom#attributes", |
|---|
| | 44 | |
|---|
| | 45 | /** |
|---|
| | 46 | * Property: writeOptions |
|---|
| | 47 | * {Object} Optional configuration for <write>. |
|---|
| | 48 | */ |
|---|
| | 49 | writeOptions: null, |
|---|
| | 50 | |
|---|
| | 51 | /** |
|---|
| | 52 | * Property: writeCallback |
|---|
| | 53 | * {Function} |
|---|
| | 54 | */ |
|---|
| | 55 | writeCallback: null, |
|---|
| | 56 | |
|---|
| | 57 | /** |
|---|
| | 58 | * Property: readOptions |
|---|
| | 59 | * {Object} Optional configuration for <read>. |
|---|
| | 60 | */ |
|---|
| | 61 | readOptions: null, |
|---|
| | 62 | |
|---|
| | 63 | /** |
|---|
| | 64 | * Property: readCallback |
|---|
| | 65 | * {Function} |
|---|
| | 66 | */ |
|---|
| | 67 | readCallback: null, |
|---|
| | 68 | |
|---|
| | 69 | /** |
|---|
| | 70 | * APIProperty: xy |
|---|
| | 71 | * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) |
|---|
| | 72 | * For GeoRSS the default is (y,x), therefore: false |
|---|
| | 73 | */ |
|---|
| | 74 | xy: false, |
|---|
| | 75 | |
|---|
| | 76 | /** |
|---|
| | 77 | * APIProperty: srsName |
|---|
| | 78 | * {String} URI for spatial reference system. This is optional for |
|---|
| | 79 | * single part geometries and mandatory for collections and multis. |
|---|
| | 80 | * If set, the srsName attribute will be written for all geometries. |
|---|
| | 81 | * Default is null. |
|---|
| | 82 | */ |
|---|
| | 83 | srsName: null, |
|---|
| | 84 | |
|---|
| | 85 | /** |
|---|
| | 86 | * Property: regExes |
|---|
| | 87 | * Compiled regular expressions for manipulating strings. |
|---|
| | 88 | */ |
|---|
| | 89 | regExes: OpenLayers.Format.GML.Base.prototype.regExes, |
|---|
| | 90 | |
|---|
| | 91 | /** |
|---|
| | 92 | * Constructor: OpenLayers.Format.Atom |
|---|
| | 93 | * Create a new parser for Atom. |
|---|
| | 94 | * |
|---|
| | 95 | * Parameters: |
|---|
| | 96 | * options - {Object} An optional object whose properties will be set on |
|---|
| | 97 | * this instance. |
|---|
| | 98 | */ |
|---|
| | 99 | initialize: function(options) { |
|---|
| | 100 | OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); |
|---|
| | 101 | this.writeOptions = this.writeOptions || {}; |
|---|
| | 102 | this.readOptions = this.readOptions || {}; |
|---|
| | 103 | }, |
|---|
| | 104 | |
|---|
| | 105 | /** |
|---|
| | 106 | * APIMethod: read |
|---|
| | 107 | * Return a list of features from an Atom feed or Atom entry doc. |
|---|
| | 108 | |
|---|
| | 109 | * Parameters: |
|---|
| | 110 | * data - {Element} |
|---|
| | 111 | * options - {Object} Optional object for configuring the read. |
|---|
| | 112 | * |
|---|
| | 113 | * Supported options: |
|---|
| | 114 | * callback - {Function} A function that will be called for each atom:entry |
|---|
| | 115 | * node prior to returning the feature representing that node. The |
|---|
| | 116 | * callback will receive node and feature as arguments. |
|---|
| | 117 | * |
|---|
| | 118 | * Returns: |
|---|
| | 119 | * {Array(<OpenLayers.Feature.Vector>)} An array of features. |
|---|
| | 120 | */ |
|---|
| | 121 | read: function(doc, options) { |
|---|
| | 122 | options = options || {}; |
|---|
| | 123 | this.readCallback = options.callback || this.readOptions.callback; |
|---|
| | 124 | if (typeof doc == "string") { |
|---|
| | 125 | doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); |
|---|
| | 126 | } |
|---|
| | 127 | |
|---|
| | 128 | var obj = {features: []}; |
|---|
| | 129 | this.readChildNodes(doc, obj); |
|---|
| | 130 | |
|---|
| | 131 | return obj.features; |
|---|
| | 132 | }, |
|---|
| | 133 | |
|---|
| | 134 | /** |
|---|
| | 135 | * Property: readers |
|---|
| | 136 | * Contains public functions, grouped by namespace prefix, that will |
|---|
| | 137 | * be applied when a namespaced node is found matching the function |
|---|
| | 138 | * name. The function will be applied in the scope of this parser |
|---|
| | 139 | * with two arguments: the node being read and a context object passed |
|---|
| | 140 | * from the parent. |
|---|
| | 141 | */ |
|---|
| | 142 | readers: { |
|---|
| | 143 | "atom": { |
|---|
| | 144 | "feed": function(node, obj) { |
|---|
| | 145 | // only extracting entries out of feed for now |
|---|
| | 146 | var entries = this.getElementsByTagNameNS( |
|---|
| | 147 | node, this.namespaces.atom, "entry" |
|---|
| | 148 | ); |
|---|
| | 149 | if(entries) { |
|---|
| | 150 | for(var i=0, len=entries.length; i<len; ++i) { |
|---|
| | 151 | this.readers.atom.entry.apply(this, [entries[i], obj]); |
|---|
| | 152 | } |
|---|
| | 153 | } |
|---|
| | 154 | }, |
|---|
| | 155 | "entry": function(node, obj) { |
|---|
| | 156 | var feature = new OpenLayers.Feature.Vector(); |
|---|
| | 157 | this.readChildNodes(node, feature); |
|---|
| | 158 | |
|---|
| | 159 | if(feature.geometry && |
|---|
| | 160 | this.internalProjection && this.externalProjection) { |
|---|
| | 161 | feature.geometry.transform( |
|---|
| | 162 | this.externalProjection, |
|---|
| | 163 | this.internalProjection |
|---|
| | 164 | ); |
|---|
| | 165 | } |
|---|
| | 166 | if(this.readCallback) { |
|---|
| | 167 | this.readCallback(node, feature); |
|---|
| | 168 | } |
|---|
| | 169 | obj.features.push(feature); |
|---|
| | 170 | }, |
|---|
| | 171 | "title": function(node, feature) { |
|---|
| | 172 | feature.attributes.title = this.getChildValue(node); |
|---|
| | 173 | }, |
|---|
| | 174 | "summary": function(node, feature) { |
|---|
| | 175 | feature.attributes.description = this.getChildValue(node); |
|---|
| | 176 | }, |
|---|
| | 177 | "content": function(node, feature) { |
|---|
| | 178 | feature.attributes.content = this.getChildValue(node); |
|---|
| | 179 | }, |
|---|
| | 180 | "link": function(node, feature) { |
|---|
| | 181 | feature.attributes.link = node.getAttribute("href"); |
|---|
| | 182 | }, |
|---|
| | 183 | "id": function(node, feature) { |
|---|
| | 184 | feature.fid = this.getChildValue(node); |
|---|
| | 185 | }, |
|---|
| | 186 | "category": function(node, feature) { |
|---|
| | 187 | /** |
|---|
| | 188 | * Only parses category with a scheme of |
|---|
| | 189 | * http://namespace.openlayers.org/atom#attributes |
|---|
| | 190 | * Assumes term values map to feature attributes. |
|---|
| | 191 | * |
|---|
| | 192 | * atom:category elements should look like |
|---|
| | 193 | * <category scheme=http://namespace.openlayers.org/atom#attributes" |
|---|
| | 194 | * term="key/value"/> |
|---|
| | 195 | * |
|---|
| | 196 | * Where this would become feature.attributes[key] = value |
|---|
| | 197 | */ |
|---|
| | 198 | if(node.getAttribute("scheme") == this.categoryScheme) { |
|---|
| | 199 | var pair = node.getAttribute("term").split("/"); |
|---|
| | 200 | feature.attributes[decodeURIComponent(pair[0])] = |
|---|
| | 201 | decodeURIComponent(pair[1]); |
|---|
| | 202 | } |
|---|
| | 203 | } |
|---|
| | 204 | }, |
|---|
| | 205 | "georss": { |
|---|
| | 206 | "point": function(node, feature) { |
|---|
| | 207 | if(!feature.geometry) { |
|---|
| | 208 | var location = OpenLayers.String.trim( |
|---|
| | 209 | this.getNodeValue(node).split(/\s+/) |
|---|
| | 210 | ); |
|---|
| | 211 | if(location.length !=2) { |
|---|
| | 212 | location = OpenLayers.String.trim( |
|---|
| | 213 | this.getNodeValue(node).split(/\s*,\s*/) |
|---|
| | 214 | ); |
|---|
| | 215 | } |
|---|
| | 216 | feature.geometry = new OpenLayers.Geometry.Point( |
|---|
| | 217 | parseFloat(location[1]), |
|---|
| | 218 | parseFloat(location[0]) |
|---|
| | 219 | ); |
|---|
| | 220 | } |
|---|
| | 221 | }, |
|---|
| | 222 | "line": function(node, feature) { |
|---|
| | 223 | if(!feature.geometry) { |
|---|
| | 224 | var coords = OpenLayers.String.trim( |
|---|
| | 225 | this.getChildValue(node).split(/\s+/) |
|---|
| | 226 | ); |
|---|
| | 227 | var components = []; |
|---|
| | 228 | for(var i=0, len=coords.length; i<len; i+=2) { |
|---|
| | 229 | components.push(new OpenLayers.Geometry.Point( |
|---|
| | 230 | parseFloat(coords[i+1]), |
|---|
| | 231 | parseFloat(coords[i]) |
|---|
| | 232 | )); |
|---|
| | 233 | } |
|---|
| | 234 | feature.geometry = new OpenLayers.Geometry.LineString(components); |
|---|
| | 235 | } |
|---|
| | 236 | }, |
|---|
| | 237 | "polygon": function(node, feature) { |
|---|
| | 238 | if(!feature.geometry) { |
|---|
| | 239 | var coords = OpenLayers.String.trim( |
|---|
| | 240 | this.getChildValue(node).split(/\s+/) |
|---|
| | 241 | ); |
|---|
| | 242 | var components = []; |
|---|
| | 243 | for(var i=0, len=coords.length; i<len; i+=2) { |
|---|
| | 244 | components.push(new OpenLayers.Geometry.Point( |
|---|
| | 245 | parseFloat(coords[i+1]), |
|---|
| | 246 | parseFloat(coords[i]) |
|---|
| | 247 | )); |
|---|
| | 248 | } |
|---|
| | 249 | feature.geometry = new OpenLayers.Geometry.Polygon( |
|---|
| | 250 | [new OpenLayers.Geometry.LinearRing(components)] |
|---|
| | 251 | ); |
|---|
| | 252 | } |
|---|
| | 253 | }, |
|---|
| | 254 | "where": function(node, feature) { |
|---|
| | 255 | if(!feature.geometry) { |
|---|
| | 256 | var obj = {components: []}; |
|---|
| | 257 | this.readChildNodes(node, obj); |
|---|
| | 258 | if(obj.components.length > 0) { |
|---|
| | 259 | feature.geometry = obj.components[0]; |
|---|
| | 260 | } |
|---|
| | 261 | } |
|---|
| | 262 | } |
|---|
| | 263 | }, |
|---|
| | 264 | "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"] |
|---|
| | 265 | }, |
|---|
| | 266 | |
|---|
| | 267 | /** |
|---|
| | 268 | * APIMethod: write |
|---|
| | 269 | * Accept Feature Collection, and return a string. |
|---|
| | 270 | * |
|---|
| | 271 | * Parameters: |
|---|
| | 272 | * features - {Array(<OpenLayers.Feature.Vector>)} List of features to |
|---|
| | 273 | * serialize into a string. |
|---|
| | 274 | * options - {Object} Optional object for configuring the write. |
|---|
| | 275 | * |
|---|
| | 276 | * Supported options: |
|---|
| | 277 | * callback - {Function} A function that will be called for each feature |
|---|
| | 278 | * prior to returning the node representing that feature. The callback |
|---|
| | 279 | * will receive feature and node as arguments. |
|---|
| | 280 | * |
|---|
| | 281 | * Returns: |
|---|
| | 282 | * {String} An Atom doc as string. |
|---|
| | 283 | */ |
|---|
| | 284 | write: function(features, options) { |
|---|
| | 285 | options = options || {}; |
|---|
| | 286 | this.writeCallback = options.callback || this.writeOptions.callback; |
|---|
| | 287 | var atom; |
|---|
| | 288 | if (features instanceof Array) { |
|---|
| | 289 | atom = this.writeNode("feed", features); |
|---|
| | 290 | } else { |
|---|
| | 291 | atom = this.writeNode("entry", features); |
|---|
| | 292 | } |
|---|
| | 293 | return OpenLayers.Format.XML.prototype.write.apply(this, [atom]); |
|---|
| | 294 | }, |
|---|
| | 295 | |
|---|
| | 296 | /** |
|---|
| | 297 | * Property: writers |
|---|
| | 298 | * As a compliment to the <readers> property, this structure contains public |
|---|
| | 299 | * writing functions grouped by namespace alias and named like the |
|---|
| | 300 | * node names they produce. |
|---|
| | 301 | */ |
|---|
| | 302 | writers: { |
|---|
| | 303 | "atom": { |
|---|
| | 304 | "feed": function(features) { |
|---|
| | 305 | var node = this.createElementNSPlus("feed"); |
|---|
| | 306 | for(var i=0, len=features.length; i<len; ++i) { |
|---|
| | 307 | this.writeNode("entry", features[i], node); |
|---|
| | 308 | } |
|---|
| | 309 | return node; |
|---|
| | 310 | }, |
|---|
| | 311 | "entry": function(feature) { |
|---|
| | 312 | var node = this.createElementNSPlus("entry"); |
|---|
| | 313 | // write attributes |
|---|
| | 314 | var value; |
|---|
| | 315 | for(var key in feature.attributes) { |
|---|
| | 316 | var value = feature.attributes[key]; |
|---|
| | 317 | if(this.attributeMap[key]) { |
|---|
| | 318 | this.writeNode( |
|---|
| | 319 | this.attributeMap[key], value, node |
|---|
| | 320 | ); |
|---|
| | 321 | } else { |
|---|
| | 322 | this.writeNode( |
|---|
| | 323 | "category", |
|---|
| | 324 | { |
|---|
| | 325 | scheme: this.categoryScheme, |
|---|
| | 326 | term: encodeURIComponent(key) + "/"+ |
|---|
| | 327 | encodeURIComponent(value) |
|---|
| | 328 | }, |
|---|
| | 329 | node |
|---|
| | 330 | ); |
|---|
| | 331 | } |
|---|
| | 332 | } |
|---|
| | 333 | // write geometry |
|---|
| | 334 | if(feature.geometry) { |
|---|
| | 335 | this.writeNode("georss:where", feature.geometry, node); |
|---|
| | 336 | } |
|---|
| | 337 | // write fid |
|---|
| | 338 | if(feature.fid) { |
|---|
| | 339 | this.writeNode("id", feature.fid, node); |
|---|
| | 340 | } |
|---|
| | 341 | if(this.writeCallback) { |
|---|
| | 342 | this.writeCallback(feature, node); |
|---|
| | 343 | } |
|---|
| | 344 | return node; |
|---|
| | 345 | }, |
|---|
| | 346 | "title": function(value) { |
|---|
| | 347 | return this.createElementNSPlus("title", {value: value}); |
|---|
| | 348 | }, |
|---|
| | 349 | "summary": function(value) { |
|---|
| | 350 | return this.createElementNSPlus("summary", {value: value}); |
|---|
| | 351 | }, |
|---|
| | 352 | "content": function(value) { |
|---|
| | 353 | return this.createElementNSPlus("content", {value: value}); |
|---|
| | 354 | }, |
|---|
| | 355 | "link": function(href) { |
|---|
| | 356 | return this.createElementNSPlus("link", { |
|---|
| | 357 | attributes: {href: href} |
|---|
| | 358 | }); |
|---|
| | 359 | }, |
|---|
| | 360 | "id": function(value) { |
|---|
| | 361 | return this.createElementNSPlus("id", {value: value}); |
|---|
| | 362 | }, |
|---|
| | 363 | "category": function(obj) { |
|---|
| | 364 | return this.createElementNSPlus("category", { |
|---|
| | 365 | attributes: { |
|---|
| | 366 | scheme: obj.scheme, term: obj.term, label: obj.label |
|---|
| | 367 | } |
|---|
| | 368 | }); |
|---|
| | 369 | } |
|---|
| | 370 | }, |
|---|
| | 371 | "georss": { |
|---|
| | 372 | "where": function(geometry) { |
|---|
| | 373 | if(this.externalProjection && this.internalProjection) { |
|---|
| | 374 | geometry = geometry.clone().transform( |
|---|
| | 375 | this.internalProjection, this.externalProjection |
|---|
| | 376 | ); |
|---|
| | 377 | } |
|---|
| | 378 | var node = this.createElementNSPlus("georss:where"); |
|---|
| | 379 | var type = this.geometryTypes[geometry.CLASS_NAME]; |
|---|
| | 380 | this.writeNode("gml:" + type, geometry, node); |
|---|
| | 381 | return node; |
|---|
| | 382 | } |
|---|
| | 383 | }, |
|---|
| | 384 | "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"] |
|---|
| | 385 | }, |
|---|
| | 386 | |
|---|
| | 387 | /** |
|---|
| | 388 | * Property: geometryTypes |
|---|
| | 389 | * {Object} Maps OpenLayers geometry class names to GML element names. |
|---|
| | 390 | */ |
|---|
| | 391 | geometryTypes: OpenLayers.Format.GML.Base.prototype.geometryTypes, |
|---|
| | 392 | |
|---|
| | 393 | /** |
|---|
| | 394 | * Property: attributeMap |
|---|
| | 395 | * {Object} Maps feature attribute names to atom element names. |
|---|
| | 396 | */ |
|---|
| | 397 | attributeMap: { |
|---|
| | 398 | "title": "title", |
|---|
| | 399 | "description": "summary", |
|---|
| | 400 | "content": "content", |
|---|
| | 401 | "link": "link" |
|---|
| | 402 | }, |
|---|
| | 403 | |
|---|
| | 404 | CLASS_NAME: "OpenLayers.Format.Atom" |
|---|
| | 405 | }); |