| 94 | | var featureNodes = this.getElementsByTagNameNS(data, |
|---|
| 95 | | '*', |
|---|
| 96 | | "Placemark"); |
|---|
| 97 | | var numFeatures = featureNodes.length; |
|---|
| 98 | | var features = new Array(numFeatures); |
|---|
| 99 | | for(var i=0; i<numFeatures; i++) { |
|---|
| 100 | | var feature = this.parseFeature(featureNodes[i]); |
|---|
| | 134 | |
|---|
| | 135 | // create default options if none provided |
|---|
| | 136 | options = options || {}; |
|---|
| | 137 | |
|---|
| | 138 | // Set default options |
|---|
| | 139 | OpenLayers.Util.applyDefaults(options, { |
|---|
| | 140 | depth: 0, |
|---|
| | 141 | styleBaseUrl: "" |
|---|
| | 142 | }); |
|---|
| | 143 | |
|---|
| | 144 | // Loop throught the following node types in this order and |
|---|
| | 145 | // process the nodes found |
|---|
| | 146 | var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; |
|---|
| | 147 | for(var i=0; i<types.length; ++i) { |
|---|
| | 148 | var type = types[i]; |
|---|
| | 149 | |
|---|
| | 150 | var nodes = this.getElementsByTagNameNS(data, "*", type); |
|---|
| | 151 | |
|---|
| | 152 | if(nodes.length == 0) { |
|---|
| | 153 | continue; |
|---|
| | 154 | } |
|---|
| | 155 | |
|---|
| | 156 | switch (type.toLowerCase()) { |
|---|
| | 157 | |
|---|
| | 158 | // Fetch external links |
|---|
| | 159 | case "link": |
|---|
| | 160 | case "networklink": |
|---|
| | 161 | this.parseLinks(nodes, options); |
|---|
| | 162 | break; |
|---|
| | 163 | |
|---|
| | 164 | // parse style information |
|---|
| | 165 | case "style": |
|---|
| | 166 | this.parseStyles(nodes, options); |
|---|
| | 167 | break; |
|---|
| | 168 | case "stylemap": |
|---|
| | 169 | this.parseStyleMaps(nodes, options); |
|---|
| | 170 | break; |
|---|
| | 171 | |
|---|
| | 172 | // parse features |
|---|
| | 173 | case "placemark": |
|---|
| | 174 | this.parseFeatures(nodes, options); |
|---|
| | 175 | break; |
|---|
| | 176 | } |
|---|
| | 177 | } |
|---|
| | 178 | |
|---|
| | 179 | // Reversing the features, only on highest recursion level |
|---|
| | 180 | if (this.reverseFeatures && options.depth == 0) { |
|---|
| | 181 | this.features.reverse(); |
|---|
| | 182 | } |
|---|
| | 183 | |
|---|
| | 184 | return this.features; |
|---|
| | 185 | }, |
|---|
| | 186 | |
|---|
| | 187 | /** |
|---|
| | 188 | * Method: parseLinks |
|---|
| | 189 | * Finds URLs of linked KML documents and fetches them |
|---|
| | 190 | * |
|---|
| | 191 | * Parameters: |
|---|
| | 192 | * nodes - {Array} of {DOMElement} data to read/parse. |
|---|
| | 193 | * options - {Object} Hash of options |
|---|
| | 194 | * |
|---|
| | 195 | */ |
|---|
| | 196 | parseLinks: function(nodes, options) { |
|---|
| | 197 | |
|---|
| | 198 | // Fetch external links <NetworkLink> and <Link> |
|---|
| | 199 | // Don't do anything if we have reached our maximum depth for recursion |
|---|
| | 200 | if (options.depth >= this.maxDepth) { |
|---|
| | 201 | return false; |
|---|
| | 202 | } |
|---|
| | 203 | |
|---|
| | 204 | // increase depth |
|---|
| | 205 | var newOptions = OpenLayers.Util.extend({}, options); |
|---|
| | 206 | newOptions.depth++; |
|---|
| | 207 | |
|---|
| | 208 | for(var i=0; i < nodes.length; i++) { |
|---|
| | 209 | var href = this.parseProperty(nodes[i], "*", "href"); |
|---|
| | 210 | if(href && !this.fetched[href]) { |
|---|
| | 211 | this.fetched[href] = true; // prevent reloading the same urls |
|---|
| | 212 | var data = this.fetchLink(href); |
|---|
| | 213 | if (data) { |
|---|
| | 214 | this.read(data, newOptions); |
|---|
| | 215 | } |
|---|
| | 216 | } |
|---|
| | 217 | } |
|---|
| | 218 | |
|---|
| | 219 | }, |
|---|
| | 220 | |
|---|
| | 221 | /** |
|---|
| | 222 | * Method: fetchLink |
|---|
| | 223 | * Fetches a URL and returns the result |
|---|
| | 224 | * |
|---|
| | 225 | * Parameters: |
|---|
| | 226 | * href - {String} url to be fetched |
|---|
| | 227 | * |
|---|
| | 228 | */ |
|---|
| | 229 | fetchLink: function(href) { |
|---|
| | 230 | |
|---|
| | 231 | if (OpenLayers.ProxyHost |
|---|
| | 232 | && OpenLayers.String.startsWith(href, "http")) { |
|---|
| | 233 | href = OpenLayers.ProxyHost + escape(href); |
|---|
| | 234 | } |
|---|
| | 235 | |
|---|
| | 236 | var request = new OpenLayers.Ajax.Request(href, |
|---|
| | 237 | {method: 'get', asynchronous: false }); |
|---|
| | 238 | |
|---|
| | 239 | if (request && request.transport) { |
|---|
| | 240 | return request.transport.responseText; |
|---|
| | 241 | } |
|---|
| | 242 | |
|---|
| | 243 | }, |
|---|
| | 244 | |
|---|
| | 245 | /** |
|---|
| | 246 | * Method: parseStyles |
|---|
| | 247 | * Looks for <Style> nodes in the data and parses them |
|---|
| | 248 | * Also parses <StyleMap> nodes, but only uses the 'normal' key |
|---|
| | 249 | * |
|---|
| | 250 | * Parameters: |
|---|
| | 251 | * nodes - {Array} of {DOMElement} data to read/parse. |
|---|
| | 252 | * options - {Object} Hash of options |
|---|
| | 253 | * |
|---|
| | 254 | */ |
|---|
| | 255 | parseStyles: function(nodes, options) { |
|---|
| | 256 | for(var i=0; i < nodes.length; i++) { |
|---|
| | 257 | var style = this.parseStyle(nodes[i]); |
|---|
| | 258 | if(style && style.name) { |
|---|
| | 259 | styleName = (options.styleBaseUrl || "") + "#" + style.name; |
|---|
| | 260 | this.styles[styleName] = style; |
|---|
| | 261 | } |
|---|
| | 262 | } |
|---|
| | 263 | }, |
|---|
| | 264 | |
|---|
| | 265 | /** |
|---|
| | 266 | * Method: parseStyleMaps |
|---|
| | 267 | * Looks for <Style> nodes in the data and parses them |
|---|
| | 268 | * Also parses <StyleMap> nodes, but only uses the 'normal' key |
|---|
| | 269 | * |
|---|
| | 270 | * Parameters: |
|---|
| | 271 | * nodes - {Array} of {DOMElement} data to read/parse. |
|---|
| | 272 | * options - {Object} Hash of options |
|---|
| | 273 | * |
|---|
| | 274 | */ |
|---|
| | 275 | parseStyleMaps: function(nodes, options) { |
|---|
| | 276 | // Only the default or "normal" part of the StyleMap is processed now |
|---|
| | 277 | // To do the select or "highlight" bit, we'd need to change lots more |
|---|
| | 278 | |
|---|
| | 279 | for(var i=0; i < nodes.length; i++) { |
|---|
| | 280 | var node = nodes[i]; |
|---|
| | 281 | var pairs = this.getElementsByTagNameNS(node, "*", |
|---|
| | 282 | "Pair"); |
|---|
| | 283 | |
|---|
| | 284 | var id = node.getAttribute("id"); |
|---|
| | 285 | for (var j=0; j<pairs.length; j++) { |
|---|
| | 286 | var pair = pairs[j]; |
|---|
| | 287 | // Use the shortcut in the SLD format to quickly retrieve the |
|---|
| | 288 | // value of a node. Maybe it's good to have a method in |
|---|
| | 289 | // Format.XML to do this |
|---|
| | 290 | var key = this.parseProperty(pair, "*", "key"); |
|---|
| | 291 | var styleUrl = this.parseProperty(pair, "*", "styleUrl"); |
|---|
| | 292 | |
|---|
| | 293 | if (styleUrl && key == "normal") { |
|---|
| | 294 | this.styles[(options.styleBaseUrl || "") + "#" + id] = |
|---|
| | 295 | this.styles[(options.styleBaseUrl || "") + styleUrl]; |
|---|
| | 296 | } |
|---|
| | 297 | |
|---|
| | 298 | if (styleUrl && key == "highlight") { |
|---|
| | 299 | // TODO: implement the "select" part |
|---|
| | 300 | } |
|---|
| | 301 | |
|---|
| | 302 | } |
|---|
| | 303 | } |
|---|
| | 304 | |
|---|
| | 305 | }, |
|---|
| | 306 | |
|---|
| | 307 | /** |
|---|
| | 308 | * Method: parseStyle |
|---|
| | 309 | * Parses the children of a <Style> node and builds the style hash |
|---|
| | 310 | * accordingly |
|---|
| | 311 | * |
|---|
| | 312 | * Parameters: |
|---|
| | 313 | * node - {DOMElement} <Style> node |
|---|
| | 314 | * |
|---|
| | 315 | */ |
|---|
| | 316 | parseStyle: function(node) { |
|---|
| | 317 | var style = {}; |
|---|
| | 318 | |
|---|
| | 319 | var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle"]; |
|---|
| | 320 | var type, nodeList, geometry, parser; |
|---|
| | 321 | for(var i=0; i<types.length; ++i) { |
|---|
| | 322 | type = types[i]; |
|---|
| | 323 | //"*" = node.namespaceURI ? |
|---|
| | 324 | //node.namespaceURI : this.kmlns; |
|---|
| | 325 | styleTypeNode = this.getElementsByTagNameNS(node, |
|---|
| | 326 | "*", type)[0]; |
|---|
| | 327 | if(!styleTypeNode) { |
|---|
| | 328 | continue; |
|---|
| | 329 | } |
|---|
| | 330 | |
|---|
| | 331 | // only deal with first geometry of this type |
|---|
| | 332 | switch (type.toLowerCase()) { |
|---|
| | 333 | case "linestyle": |
|---|
| | 334 | var color = this.parseProperty(styleTypeNode, "*", "color"); |
|---|
| | 335 | if (color) { |
|---|
| | 336 | var matches = (color.toString()).match( |
|---|
| | 337 | this.regExes.kmlColor); |
|---|
| | 338 | |
|---|
| | 339 | // transparency |
|---|
| | 340 | var alpha = matches[1]; |
|---|
| | 341 | style["strokeOpacity"] = parseInt(alpha, 16) / 255; |
|---|
| | 342 | |
|---|
| | 343 | // rgb colors (google uses bgr) |
|---|
| | 344 | var b = matches[2]; |
|---|
| | 345 | var g = matches[3]; |
|---|
| | 346 | var r = matches[4]; |
|---|
| | 347 | style["strokeColor"] = "#" + r + g + b; |
|---|
| | 348 | } |
|---|
| | 349 | |
|---|
| | 350 | var width = this.parseProperty(styleTypeNode, "*", "width"); |
|---|
| | 351 | if (width) { |
|---|
| | 352 | style["strokeWidth"] = width; |
|---|
| | 353 | } |
|---|
| | 354 | |
|---|
| | 355 | case "polystyle": |
|---|
| | 356 | var color = this.parseProperty(styleTypeNode, "*", "color"); |
|---|
| | 357 | if (color) { |
|---|
| | 358 | var matches = (color.toString()).match( |
|---|
| | 359 | this.regExes.kmlColor); |
|---|
| | 360 | |
|---|
| | 361 | // transparency |
|---|
| | 362 | var alpha = matches[1]; |
|---|
| | 363 | style["fillOpacity"] = parseInt(alpha, 16) / 255; |
|---|
| | 364 | |
|---|
| | 365 | // rgb colors (google uses bgr) |
|---|
| | 366 | var b = matches[2]; |
|---|
| | 367 | var g = matches[3]; |
|---|
| | 368 | var r = matches[4]; |
|---|
| | 369 | style["fillColor"] = "#" + r + g + b; |
|---|
| | 370 | } |
|---|
| | 371 | |
|---|
| | 372 | break; |
|---|
| | 373 | case "iconstyle": |
|---|
| | 374 | var iconNode = this.getElementsByTagNameNS(styleTypeNode, |
|---|
| | 375 | "*", |
|---|
| | 376 | "Icon")[0]; |
|---|
| | 377 | |
|---|
| | 378 | // set default width and height of icon |
|---|
| | 379 | style["graphicWidth"] = 32; |
|---|
| | 380 | style["graphicHeight"] = 32; |
|---|
| | 381 | |
|---|
| | 382 | if (iconNode) { |
|---|
| | 383 | var href = this.parseProperty(iconNode, "*", "href"); |
|---|
| | 384 | if (href) { |
|---|
| | 385 | |
|---|
| | 386 | // support for internal icons |
|---|
| | 387 | // (/root://icons/palette-x.png) |
|---|
| | 388 | // x and y tell the position on the palette: |
|---|
| | 389 | // - in pixels |
|---|
| | 390 | // - starting from the left bottom |
|---|
| | 391 | // We need to translate that to a position in the |
|---|
| | 392 | // list and request the appropriate icon from the |
|---|
| | 393 | // google maps website |
|---|
| | 394 | var matches = href.match(this.regExes.kmlIconPalette); |
|---|
| | 395 | if (matches) { |
|---|
| | 396 | var palette = matches[1]; |
|---|
| | 397 | var file_extension = matches[2]; |
|---|
| | 398 | |
|---|
| | 399 | var x = this.parseProperty(iconNode, "*", "x"); |
|---|
| | 400 | var y = this.parseProperty(iconNode, "*", "y"); |
|---|
| | 401 | |
|---|
| | 402 | var posX = x ? x/32 : 0; |
|---|
| | 403 | var posY = y ? (7 - y/32) : 7; |
|---|
| | 404 | |
|---|
| | 405 | var pos = posY * 8 + posX; |
|---|
| | 406 | var href = "http://maps.google.com/mapfiles/kml/pal" |
|---|
| | 407 | + palette + "/icon" + pos + file_extension; |
|---|
| | 408 | } |
|---|
| | 409 | |
|---|
| | 410 | |
|---|
| | 411 | var w = this.parseProperty(iconNode, "*", "w"); |
|---|
| | 412 | if (w) { |
|---|
| | 413 | style["graphicWidth"] = parseInt(w); |
|---|
| | 414 | } |
|---|
| | 415 | |
|---|
| | 416 | var h = this.parseProperty(iconNode, "*", "h"); |
|---|
| | 417 | if (h) { |
|---|
| | 418 | style["graphicHeight"] = parseInt(h); |
|---|
| | 419 | } |
|---|
| | 420 | |
|---|
| | 421 | style["graphicOpacity"] = 1; // fully opaque |
|---|
| | 422 | style["externalGraphic"] = href; |
|---|
| | 423 | } |
|---|
| | 424 | |
|---|
| | 425 | } |
|---|
| | 426 | |
|---|
| | 427 | |
|---|
| | 428 | // hotSpots define the offset for an Icon |
|---|
| | 429 | var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, |
|---|
| | 430 | "*", |
|---|
| | 431 | "hotSpot")[0]; |
|---|
| | 432 | if (hotSpotNode) { |
|---|
| | 433 | var x = hotSpotNode.getAttribute("x"); |
|---|
| | 434 | var y = hotSpotNode.getAttribute("y"); |
|---|
| | 435 | |
|---|
| | 436 | var xUnits = hotSpotNode.getAttribute("xunits"); |
|---|
| | 437 | if (xUnits == "pixels") { |
|---|
| | 438 | style["graphicXOffset"] = parseInt(x); |
|---|
| | 439 | } |
|---|
| | 440 | else if (xUnits == "insetPixels") { |
|---|
| | 441 | style["graphicXOffset"] = style["graphicWidth"] |
|---|
| | 442 | - parseInt(x); |
|---|
| | 443 | } |
|---|
| | 444 | else if (xUnits == "fraction") { |
|---|
| | 445 | style["graphicXOffset"] = style["graphicWidth"] |
|---|
| | 446 | * parseFloat(x); |
|---|
| | 447 | } |
|---|
| | 448 | |
|---|
| | 449 | var yUnits = hotSpotNode.getAttribute("yunits"); |
|---|
| | 450 | if (yUnits == "pixels") { |
|---|
| | 451 | style["graphicYOffset"] = parseInt(y); |
|---|
| | 452 | } |
|---|
| | 453 | else if (yUnits == "insetPixels") { |
|---|
| | 454 | style["graphicYOffset"] = style["graphicHeight"] |
|---|
| | 455 | - parseInt(y); |
|---|
| | 456 | } |
|---|
| | 457 | else if (yUnits == "fraction") { |
|---|
| | 458 | style["graphicYOffset"] = style["graphicHeight"] |
|---|
| | 459 | * parseFloat(y); |
|---|
| | 460 | } |
|---|
| | 461 | } |
|---|
| | 462 | break; |
|---|
| | 463 | case "balloonstyle": |
|---|
| | 464 | var balloonStyle = OpenLayers.Util.getXmlNodeValue( |
|---|
| | 465 | styleTypeNode); |
|---|
| | 466 | if (balloonStyle) { |
|---|
| | 467 | style["balloonStyle"] = balloonStyle.replace( |
|---|
| | 468 | this.regExes.straightBracket, "${$1}"); |
|---|
| | 469 | } |
|---|
| | 470 | break; |
|---|
| | 471 | default: |
|---|
| | 472 | } |
|---|
| | 473 | } |
|---|
| | 474 | |
|---|
| | 475 | // Some polygons have no line color, so we use the fillColor for that |
|---|
| | 476 | if (!style["strokeColor"] && style["fillColor"]) { |
|---|
| | 477 | style["strokeColor"] = style["fillColor"]; |
|---|
| | 478 | } |
|---|
| | 479 | |
|---|
| | 480 | // set default options for style object |
|---|
| | 481 | var styleOptions = { |
|---|
| | 482 | name: node.getAttribute("id") |
|---|
| | 483 | }; |
|---|
| | 484 | |
|---|
| | 485 | // convert style into OpenLayers.Style |
|---|
| | 486 | var styleObject = new OpenLayers.Style(style, styleOptions); |
|---|
| | 487 | |
|---|
| | 488 | return styleObject; |
|---|
| | 489 | }, |
|---|
| | 490 | |
|---|
| | 491 | |
|---|
| | 492 | /** |
|---|
| | 493 | * Method: parseFeatures |
|---|
| | 494 | * Loop through all Placemark nodes and parse them. |
|---|
| | 495 | * Will create a list of features |
|---|
| | 496 | * |
|---|
| | 497 | * Parameters: |
|---|
| | 498 | * nodes - {Array} of {DOMElement} data to read/parse. |
|---|
| | 499 | * options - {Object} Hash of options |
|---|
| | 500 | * |
|---|
| | 501 | */ |
|---|
| | 502 | parseFeatures: function(nodes, options) { |
|---|
| | 503 | var features = new Array(nodes.length); |
|---|
| | 504 | for(var i=0; i < nodes.length; i++) { |
|---|
| | 505 | //var feature = this.parseFeature(featureNodes[i]); |
|---|
| | 506 | var featureNode = nodes[i]; |
|---|
| | 507 | var feature = this.parseFeature.apply(this,[featureNode]) ; |
|---|