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. */
6 * @requires OpenLayers/Format/XML.js
7 * @requires OpenLayers/Feature/Vector.js
8 * @requires OpenLayers/Geometry/Point.js
9 * @requires OpenLayers/Geometry/LineString.js
10 * @requires OpenLayers/Geometry/Polygon.js
11 * @requires OpenLayers/Geometry/Collection.js
12 * @requires OpenLayers/Request/XMLHttpRequest.js
13 * @requires OpenLayers/Console.js
17 * Class: OpenLayers.Format.KML
18 * Read/Wite KML. Create a new instance with the <OpenLayers.Format.KML>
22 * - <OpenLayers.Format.XML>
24 OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
28 * {String} KML Namespace to use. Defaults to 2.0 namespace.
30 kmlns: "http://earth.google.com/kml/2.0",
33 * APIProperty: placemarksDesc
34 * {String} Name of the placemarks. Default is "No description available."
36 placemarksDesc: "No description available",
39 * APIProperty: foldersName
40 * {String} Name of the folders. Default is "OpenLayers export."
42 foldersName: "OpenLayers export",
45 * APIProperty: foldersDesc
46 * {String} Description of the folders. Default is "Exported on [date]."
48 foldersDesc: "Exported on " + new Date(),
51 * APIProperty: extractAttributes
52 * {Boolean} Extract attributes from KML. Default is true.
53 * Extracting styleUrls requires this to be set to true
55 extractAttributes: true,
58 * Property: extractStyles
59 * {Boolean} Extract styles from KML. Default is false.
60 * Extracting styleUrls also requires extractAttributes to be
66 * Property: internalns
67 * {String} KML Namespace to use -- defaults to the namespace of the
68 * Placemark node being parsed, but falls back to kmlns.
74 * {Array} Array of features
81 * {Object} Storage of style objects
87 * Property: styleBaseUrl
94 * {Object} Storage of KML URLs that have been fetched before
95 * in order to prevent reloading them.
100 * APIProperty: maxDepth
101 * {Integer} Maximum depth for recursive loading external KML URLs
102 * Defaults to 0: do no external fetching
107 * Constructor: OpenLayers.Format.KML
108 * Create a new parser for KML.
111 * options - {Object} An optional object whose properties will be set on
114 initialize: function(options) {
115 // compile regular expressions once instead of every time they are used
117 trimSpace: (/^\s*|\s*$/g),
118 removeSpace: (/\s*/g),
120 trimComma: (/\s*,\s*/g),
121 kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
122 kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
123 straightBracket: (/\$\[(.*?)\]/g)
125 OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
130 * Read data from a string, and return a list of features.
133 * data - {String} or {DOMElement} data to read/parse.
136 * {Array(<OpenLayers.Feature.Vector>)} List of features.
138 read: function(data) {
143 // Set default options
146 styleBaseUrl: this.styleBaseUrl
149 return this.parseData(data, options);
154 * Read data from a string, and return a list of features.
157 * data - {String} or {DOMElement} data to read/parse.
158 * options - {Object} Hash of options
161 * {Array(<OpenLayers.Feature.Vector>)} List of features.
163 parseData: function(data, options) {
164 if(typeof data == "string") {
165 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
168 // Loop throught the following node types in this order and
169 // process the nodes found
170 var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"];
171 for(var i=0, len=types.length; i<len; ++i) {
174 var nodes = this.getElementsByTagNameNS(data, "*", type);
176 // skip to next type if no nodes are found
177 if(nodes.length == 0) {
181 switch (type.toLowerCase()) {
183 // Fetch external links
186 this.parseLinks(nodes, options);
189 // parse style information
191 if (this.extractStyles) {
192 this.parseStyles(nodes, options);
196 if (this.extractStyles) {
197 this.parseStyleMaps(nodes, options);
203 this.parseFeatures(nodes, options);
208 return this.features;
213 * Finds URLs of linked KML documents and fetches them
216 * nodes - {Array} of {DOMElement} data to read/parse.
217 * options - {Object} Hash of options
220 parseLinks: function(nodes, options) {
222 // Fetch external links <NetworkLink> and <Link>
223 // Don't do anything if we have reached our maximum depth for recursion
224 if (options.depth >= this.maxDepth) {
229 var newOptions = OpenLayers.Util.extend({}, options);
232 for(var i=0, len=nodes.length; i<len; i++) {
233 var href = this.parseProperty(nodes[i], "*", "href");
234 if(href && !this.fetched[href]) {
235 this.fetched[href] = true; // prevent reloading the same urls
236 var data = this.fetchLink(href);
238 this.parseData(data, newOptions);
247 * Fetches a URL and returns the result
250 * href - {String} url to be fetched
253 fetchLink: function(href) {
254 var request = OpenLayers.Request.GET({url: href, async: false});
256 return request.responseText;
261 * Method: parseStyles
262 * Looks for <Style> nodes in the data and parses them
263 * Also parses <StyleMap> nodes, but only uses the 'normal' key
266 * nodes - {Array} of {DOMElement} data to read/parse.
267 * options - {Object} Hash of options
270 parseStyles: function(nodes, options) {
271 for(var i=0, len=nodes.length; i<len; i++) {
272 var style = this.parseStyle(nodes[i]);
274 styleName = (options.styleBaseUrl || "") + "#" + style.id;
276 this.styles[styleName] = style;
283 * Parses the children of a <Style> node and builds the style hash
287 * node - {DOMElement} <Style> node
290 parseStyle: function(node) {
293 var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle"];
294 var type, nodeList, geometry, parser;
295 for(var i=0, len=types.length; i<len; ++i) {
297 styleTypeNode = this.getElementsByTagNameNS(node,
303 // only deal with first geometry of this type
304 switch (type.toLowerCase()) {
306 var color = this.parseProperty(styleTypeNode, "*", "color");
308 var matches = (color.toString()).match(
309 this.regExes.kmlColor);
312 var alpha = matches[1];
313 style["strokeOpacity"] = parseInt(alpha, 16) / 255;
315 // rgb colors (google uses bgr)
319 style["strokeColor"] = "#" + r + g + b;
322 var width = this.parseProperty(styleTypeNode, "*", "width");
324 style["strokeWidth"] = width;
328 var color = this.parseProperty(styleTypeNode, "*", "color");
330 var matches = (color.toString()).match(
331 this.regExes.kmlColor);
334 var alpha = matches[1];
335 style["fillOpacity"] = parseInt(alpha, 16) / 255;
337 // rgb colors (google uses bgr)
341 style["fillColor"] = "#" + r + g + b;
343 // Check is fill is disabled
344 var fill = this.parseProperty(styleTypeNode, "*", "fill");
346 style["fillColor"] = "none";
352 var scale = parseFloat(this.parseProperty(styleTypeNode,
355 // set default width and height of icon
356 var width = 32 * scale;
357 var height = 32 * scale;
359 var iconNode = this.getElementsByTagNameNS(styleTypeNode,
363 var href = this.parseProperty(iconNode, "*", "href");
366 var w = this.parseProperty(iconNode, "*", "w");
367 var h = this.parseProperty(iconNode, "*", "h");
369 // Settings for Google specific icons that are 64x64
370 // We set the width and height to 64 and halve the
371 // scale to prevent icons from being too big
372 var google = "http://maps.google.com/mapfiles/kml";
373 if (OpenLayers.String.startsWith(
374 href, google) && !w && !h) {
380 // if only dimension is defined, make sure the
381 // other one has the same value
386 width = parseInt(w) * scale;
390 height = parseInt(h) * scale;
393 // support for internal icons
394 // (/root://icons/palette-x.png)
395 // x and y tell the position on the palette:
397 // - starting from the left bottom
398 // We translate that to a position in the list
399 // and request the appropriate icon from the
400 // google maps website
401 var matches = href.match(this.regExes.kmlIconPalette);
403 var palette = matches[1];
404 var file_extension = matches[2];
406 var x = this.parseProperty(iconNode, "*", "x");
407 var y = this.parseProperty(iconNode, "*", "y");
409 var posX = x ? x/32 : 0;
410 var posY = y ? (7 - y/32) : 7;
412 var pos = posY * 8 + posX;
413 href = "http://maps.google.com/mapfiles/kml/pal"
414 + palette + "/icon" + pos + file_extension;
417 style["graphicOpacity"] = 1; // fully opaque
418 style["externalGraphic"] = href;
424 // hotSpots define the offset for an Icon
425 var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,
429 var x = parseFloat(hotSpotNode.getAttribute("x"));
430 var y = parseFloat(hotSpotNode.getAttribute("y"));
432 var xUnits = hotSpotNode.getAttribute("xunits");
433 if (xUnits == "pixels") {
434 style["graphicXOffset"] = -x * scale;
436 else if (xUnits == "insetPixels") {
437 style["graphicXOffset"] = -width + (x * scale);
439 else if (xUnits == "fraction") {
440 style["graphicXOffset"] = -width * x;
443 var yUnits = hotSpotNode.getAttribute("yunits");
444 if (yUnits == "pixels") {
445 style["graphicYOffset"] = -height + (y * scale) + 1;
447 else if (yUnits == "insetPixels") {
448 style["graphicYOffset"] = -(y * scale) + 1;
450 else if (yUnits == "fraction") {
451 style["graphicYOffset"] = -height * (1 - y) + 1;
455 style["graphicWidth"] = width;
456 style["graphicHeight"] = height;
460 var balloonStyle = OpenLayers.Util.getXmlNodeValue(
463 style["balloonStyle"] = balloonStyle.replace(
464 this.regExes.straightBracket, "${$1}");
471 // Some polygons have no line color, so we use the fillColor for that
472 if (!style["strokeColor"] && style["fillColor"]) {
473 style["strokeColor"] = style["fillColor"];
476 var id = node.getAttribute("id");
485 * Method: parseStyleMaps
486 * Looks for <Style> nodes in the data and parses them
487 * Also parses <StyleMap> nodes, but only uses the 'normal' key
490 * nodes - {Array} of {DOMElement} data to read/parse.
491 * options - {Object} Hash of options
494 parseStyleMaps: function(nodes, options) {
495 // Only the default or "normal" part of the StyleMap is processed now
496 // To do the select or "highlight" bit, we'd need to change lots more
498 for(var i=0, len=nodes.length; i<len; i++) {
500 var pairs = this.getElementsByTagNameNS(node, "*",
503 var id = node.getAttribute("id");
504 for (var j=0, jlen=pairs.length; j<jlen; j++) {
506 // Use the shortcut in the SLD format to quickly retrieve the
507 // value of a node. Maybe it's good to have a method in
508 // Format.XML to do this
509 var key = this.parseProperty(pair, "*", "key");
510 var styleUrl = this.parseProperty(pair, "*", "styleUrl");
512 if (styleUrl && key == "normal") {
513 this.styles[(options.styleBaseUrl || "") + "#" + id] =
514 this.styles[(options.styleBaseUrl || "") + styleUrl];
517 if (styleUrl && key == "highlight") {
518 // TODO: implement the "select" part
528 * Method: parseFeatures
529 * Loop through all Placemark nodes and parse them.
530 * Will create a list of features
533 * nodes - {Array} of {DOMElement} data to read/parse.
534 * options - {Object} Hash of options
537 parseFeatures: function(nodes, options) {
538 var features = new Array(nodes.length);
539 for(var i=0, len=nodes.length; i<len; i++) {
540 var featureNode = nodes[i];
541 var feature = this.parseFeature.apply(this,[featureNode]) ;
544 // Create reference to styleUrl
545 if (this.extractStyles && feature.attributes &&
546 feature.attributes.styleUrl) {
547 feature.style = this.getStyle(feature.attributes.styleUrl, options);
550 if (this.extractStyles) {
551 // Make sure that <Style> nodes within a placemark are
553 var inlineStyleNode = this.getElementsByTagNameNS(featureNode,
556 if (inlineStyleNode) {
557 var inlineStyle= this.parseStyle(inlineStyleNode);
559 feature.style = OpenLayers.Util.extend(
560 feature.style, inlineStyle
566 // add feature to list of features
567 features[i] = feature;
569 throw "Bad Placemark: " + i;
573 // add new features to existing feature list
574 this.features = this.features.concat(features);
578 * Method: parseFeature
579 * This function is the core of the KML parsing code in OpenLayers.
580 * It creates the geometries that are then attached to the returned
581 * feature, and calls parseAttributes() to get attribute data out.
584 * node - {DOMElement}
587 * {<OpenLayers.Feature.Vector>} A vector feature.
589 parseFeature: function(node) {
590 // only accept one geometry per feature - look for highest "order"
591 var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
592 var type, nodeList, geometry, parser;
593 for(var i=0, len=order.length; i<len; ++i) {
595 this.internalns = node.namespaceURI ?
596 node.namespaceURI : this.kmlns;
597 nodeList = this.getElementsByTagNameNS(node,
598 this.internalns, type);
599 if(nodeList.length > 0) {
600 // only deal with first geometry of this type
601 var parser = this.parseGeometry[type.toLowerCase()];
603 geometry = parser.apply(this, [nodeList[0]]);
604 if (this.internalProjection && this.externalProjection) {
605 geometry.transform(this.externalProjection,
606 this.internalProjection);
609 OpenLayers.Console.error(OpenLayers.i18n(
610 "unsupportedGeometryType", {'geomType':type}));
612 // stop looking for different geometry types
617 // construct feature (optionally with attributes)
619 if(this.extractAttributes) {
620 attributes = this.parseAttributes(node);
622 var feature = new OpenLayers.Feature.Vector(geometry, attributes);
624 var fid = node.getAttribute("id") || node.getAttribute("name");
634 * Retrieves a style from a style hash using styleUrl as the key
635 * If the styleUrl doesn't exist yet, we try to fetch it
639 * styleUrl - {String} URL of style
640 * options - {Object} Hash of options
643 * {Object} - (reference to) Style hash
645 getStyle: function(styleUrl, options) {
647 var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);
649 var newOptions = OpenLayers.Util.extend({}, options);
651 newOptions.styleBaseUrl = styleBaseUrl;
653 // Fetch remote Style URLs (if not fetched before)
654 if (!this.styles[styleUrl]
655 && !OpenLayers.String.startsWith(styleUrl, "#")
656 && newOptions.depth <= this.maxDepth
657 && !this.fetched[styleBaseUrl] ) {
659 var data = this.fetchLink(styleBaseUrl);
661 this.parseData(data, newOptions);
666 // return requested style
667 var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);
672 * Property: parseGeometry
673 * Properties of this object are the functions that parse geometries based
679 * Method: parseGeometry.point
680 * Given a KML node representing a point geometry, create an OpenLayers
684 * node - {DOMElement} A KML Point node.
687 * {<OpenLayers.Geometry.Point>} A point geometry.
689 point: function(node) {
690 var nodeList = this.getElementsByTagNameNS(node, this.internalns,
693 if(nodeList.length > 0) {
694 var coordString = nodeList[0].firstChild.nodeValue;
695 coordString = coordString.replace(this.regExes.removeSpace, "");
696 coords = coordString.split(",");
700 if(coords.length > 1) {
701 // preserve third dimension
702 if(coords.length == 2) {
705 point = new OpenLayers.Geometry.Point(coords[0], coords[1],
708 throw "Bad coordinate string: " + coordString;
714 * Method: parseGeometry.linestring
715 * Given a KML node representing a linestring geometry, create an
716 * OpenLayers linestring geometry.
719 * node - {DOMElement} A KML LineString node.
722 * {<OpenLayers.Geometry.LineString>} A linestring geometry.
724 linestring: function(node, ring) {
725 var nodeList = this.getElementsByTagNameNS(node, this.internalns,
728 if(nodeList.length > 0) {
729 var coordString = this.getChildValue(nodeList[0]);
731 coordString = coordString.replace(this.regExes.trimSpace,
733 coordString = coordString.replace(this.regExes.trimComma,
735 var pointList = coordString.split(this.regExes.splitSpace);
736 var numPoints = pointList.length;
737 var points = new Array(numPoints);
738 var coords, numCoords;
739 for(var i=0; i<numPoints; ++i) {
740 coords = pointList[i].split(",");
741 numCoords = coords.length;
743 if(coords.length == 2) {
746 points[i] = new OpenLayers.Geometry.Point(coords[0],
750 throw "Bad LineString point coordinates: " +
756 line = new OpenLayers.Geometry.LinearRing(points);
758 line = new OpenLayers.Geometry.LineString(points);
761 throw "Bad LineString coordinates: " + coordString;
769 * Method: parseGeometry.polygon
770 * Given a KML node representing a polygon geometry, create an
771 * OpenLayers polygon geometry.
774 * node - {DOMElement} A KML Polygon node.
777 * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
779 polygon: function(node) {
780 var nodeList = this.getElementsByTagNameNS(node, this.internalns,
782 var numRings = nodeList.length;
783 var components = new Array(numRings);
785 // this assumes exterior ring first, inner rings after
787 for(var i=0, len=nodeList.length; i<len; ++i) {
788 ring = this.parseGeometry.linestring.apply(this,
789 [nodeList[i], true]);
791 components[i] = ring;
793 throw "Bad LinearRing geometry: " + i;
797 return new OpenLayers.Geometry.Polygon(components);
801 * Method: parseGeometry.multigeometry
802 * Given a KML node representing a multigeometry, create an
803 * OpenLayers geometry collection.
806 * node - {DOMElement} A KML MultiGeometry node.
809 * {<OpenLayers.Geometry.Collection>} A geometry collection.
811 multigeometry: function(node) {
814 var children = node.childNodes;
815 for(var i=0, len=children.length; i<len; ++i ) {
817 if(child.nodeType == 1) {
818 var type = (child.prefix) ?
819 child.nodeName.split(":")[1] :
821 var parser = this.parseGeometry[type.toLowerCase()];
823 parts.push(parser.apply(this, [child]));
827 return new OpenLayers.Geometry.Collection(parts);
833 * Method: parseAttributes
836 * node - {DOMElement}
839 * {Object} An attributes object.
841 parseAttributes: function(node) {
844 // Extended Data is parsed first.
845 var edNodes = node.getElementsByTagName("ExtendedData");
846 if (edNodes.length) {
847 attributes = this.parseExtendedData(edNodes[0]);
850 // assume attribute nodes are type 1 children with a type 3 or 4 child
851 var child, grandchildren, grandchild;
852 var children = node.childNodes;
854 for(var i=0, len=children.length; i<len; ++i) {
856 if(child.nodeType == 1) {
857 grandchildren = child.childNodes;
858 if(grandchildren.length == 1 || grandchildren.length == 3) {
860 switch (grandchildren.length) {
862 grandchild = grandchildren[0];
866 grandchild = grandchildren[1];
869 if(grandchild.nodeType == 3 || grandchild.nodeType == 4) {
870 var name = (child.prefix) ?
871 child.nodeName.split(":")[1] :
873 var value = OpenLayers.Util.getXmlNodeValue(grandchild);
875 value = value.replace(this.regExes.trimSpace, "");
876 attributes[name] = value;
886 * Method: parseExtendedData
887 * Parse ExtendedData from KML. No support for schemas/datatypes.
888 * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata
889 * for more information on extendeddata.
891 parseExtendedData: function(node) {
893 var dataNodes = node.getElementsByTagName("Data");
894 for (var i = 0, len = dataNodes.length; i < len; i++) {
895 var data = dataNodes[i];
896 var key = data.getAttribute("name");
898 var valueNode = data.getElementsByTagName("value");
899 if (valueNode.length) {
900 ed['value'] = this.getChildValue(valueNode[0]);
902 var nameNode = data.getElementsByTagName("displayName");
903 if (nameNode.length) {
904 ed['displayName'] = this.getChildValue(nameNode[0]);
906 attributes[key] = ed;
912 * Method: parseProperty
913 * Convenience method to find a node and return its value
916 * xmlNode - {<DOMElement>}
917 * namespace - {String} namespace of the node to find
918 * tagName - {String} name of the property to parse
921 * {String} The value for the requested property (defaults to null)
923 parseProperty: function(xmlNode, namespace, tagName) {
925 var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);
927 value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);
937 * Accept Feature Collection, and return a string.
940 * features - {Array(<OpenLayers.Feature.Vector>} An array of features.
943 * {String} A KML string.
945 write: function(features) {
946 if(!(features instanceof Array)) {
947 features = [features];
949 var kml = this.createElementNS(this.kmlns, "kml");
950 var folder = this.createFolderXML();
951 for(var i=0, len=features.length; i<len; ++i) {
952 folder.appendChild(this.createPlacemarkXML(features[i]));
954 kml.appendChild(folder);
955 return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);
959 * Method: createFolderXML
960 * Creates and returns a KML folder node
965 createFolderXML: function() {
967 var folderName = this.createElementNS(this.kmlns, "name");
968 var folderNameText = this.createTextNode(this.foldersName);
969 folderName.appendChild(folderNameText);
971 // Folder description
972 var folderDesc = this.createElementNS(this.kmlns, "description");
973 var folderDescText = this.createTextNode(this.foldersDesc);
974 folderDesc.appendChild(folderDescText);
977 var folder = this.createElementNS(this.kmlns, "Folder");
978 folder.appendChild(folderName);
979 folder.appendChild(folderDesc);
985 * Method: createPlacemarkXML
986 * Creates and returns a KML placemark node representing the given feature.
989 * feature - {<OpenLayers.Feature.Vector>}
994 createPlacemarkXML: function(feature) {
996 var placemarkName = this.createElementNS(this.kmlns, "name");
997 var name = (feature.attributes.name) ?
998 feature.attributes.name : feature.id;
999 placemarkName.appendChild(this.createTextNode(name));
1001 // Placemark description
1002 var placemarkDesc = this.createElementNS(this.kmlns, "description");
1003 var desc = (feature.attributes.description) ?
1004 feature.attributes.description : this.placemarksDesc;
1005 placemarkDesc.appendChild(this.createTextNode(desc));
1008 var placemarkNode = this.createElementNS(this.kmlns, "Placemark");
1009 if(feature.fid != null) {
1010 placemarkNode.setAttribute("id", feature.fid);
1012 placemarkNode.appendChild(placemarkName);
1013 placemarkNode.appendChild(placemarkDesc);
1015 // Geometry node (Point, LineString, etc. nodes)
1016 var geometryNode = this.buildGeometryNode(feature.geometry);
1017 placemarkNode.appendChild(geometryNode);
1019 // TBD - deal with remaining (non name/description) attributes.
1020 return placemarkNode;
1024 * Method: buildGeometryNode
1025 * Builds and returns a KML geometry node with the given geometry.
1028 * geometry - {<OpenLayers.Geometry>}
1033 buildGeometryNode: function(geometry) {
1034 if (this.internalProjection && this.externalProjection) {
1035 geometry = geometry.clone();
1036 geometry.transform(this.internalProjection,
1037 this.externalProjection);
1039 var className = geometry.CLASS_NAME;
1040 var type = className.substring(className.lastIndexOf(".") + 1);
1041 var builder = this.buildGeometry[type.toLowerCase()];
1044 node = builder.apply(this, [geometry]);
1050 * Property: buildGeometry
1051 * Object containing methods to do the actual geometry node building
1052 * based on geometry type.
1055 // TBD: Anybody care about namespace aliases here (these nodes have
1059 * Method: buildGeometry.point
1060 * Given an OpenLayers point geometry, create a KML point.
1063 * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
1066 * {DOMElement} A KML point node.
1068 point: function(geometry) {
1069 var kml = this.createElementNS(this.kmlns, "Point");
1070 kml.appendChild(this.buildCoordinatesNode(geometry));
1075 * Method: buildGeometry.multipoint
1076 * Given an OpenLayers multipoint geometry, create a KML
1077 * GeometryCollection.
1080 * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.
1083 * {DOMElement} A KML GeometryCollection node.
1085 multipoint: function(geometry) {
1086 return this.buildGeometry.collection.apply(this, [geometry]);
1090 * Method: buildGeometry.linestring
1091 * Given an OpenLayers linestring geometry, create a KML linestring.
1094 * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
1097 * {DOMElement} A KML linestring node.
1099 linestring: function(geometry) {
1100 var kml = this.createElementNS(this.kmlns, "LineString");
1101 kml.appendChild(this.buildCoordinatesNode(geometry));
1106 * Method: buildGeometry.multilinestring
1107 * Given an OpenLayers multilinestring geometry, create a KML
1108 * GeometryCollection.
1111 * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.
1114 * {DOMElement} A KML GeometryCollection node.
1116 multilinestring: function(geometry) {
1117 return this.buildGeometry.collection.apply(this, [geometry]);
1121 * Method: buildGeometry.linearring
1122 * Given an OpenLayers linearring geometry, create a KML linearring.
1125 * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
1128 * {DOMElement} A KML linearring node.
1130 linearring: function(geometry) {
1131 var kml = this.createElementNS(this.kmlns, "LinearRing");
1132 kml.appendChild(this.buildCoordinatesNode(geometry));
1137 * Method: buildGeometry.polygon
1138 * Given an OpenLayers polygon geometry, create a KML polygon.
1141 * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
1144 * {DOMElement} A KML polygon node.
1146 polygon: function(geometry) {
1147 var kml = this.createElementNS(this.kmlns, "Polygon");
1148 var rings = geometry.components;
1149 var ringMember, ringGeom, type;
1150 for(var i=0, len=rings.length; i<len; ++i) {
1151 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
1152 ringMember = this.createElementNS(this.kmlns, type);
1153 ringGeom = this.buildGeometry.linearring.apply(this,
1155 ringMember.appendChild(ringGeom);
1156 kml.appendChild(ringMember);
1162 * Method: buildGeometry.multipolygon
1163 * Given an OpenLayers multipolygon geometry, create a KML
1164 * GeometryCollection.
1167 * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.
1170 * {DOMElement} A KML GeometryCollection node.
1172 multipolygon: function(geometry) {
1173 return this.buildGeometry.collection.apply(this, [geometry]);
1177 * Method: buildGeometry.collection
1178 * Given an OpenLayers geometry collection, create a KML MultiGeometry.
1181 * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.
1184 * {DOMElement} A KML MultiGeometry node.
1186 collection: function(geometry) {
1187 var kml = this.createElementNS(this.kmlns, "MultiGeometry");
1189 for(var i=0, len=geometry.components.length; i<len; ++i) {
1190 child = this.buildGeometryNode.apply(this,
1191 [geometry.components[i]]);
1193 kml.appendChild(child);
1201 * Method: buildCoordinatesNode
1202 * Builds and returns the KML coordinates node with the given geometry
1203 * <coordinates>...</coordinates>
1206 * geometry - {<OpenLayers.Geometry>}
1211 buildCoordinatesNode: function(geometry) {
1212 var coordinatesNode = this.createElementNS(this.kmlns, "coordinates");
1215 var points = geometry.components;
1217 // LineString or LinearRing
1219 var numPoints = points.length;
1220 var parts = new Array(numPoints);
1221 for(var i=0; i<numPoints; ++i) {
1223 parts[i] = point.x + "," + point.y;
1225 path = parts.join(" ");
1228 path = geometry.x + "," + geometry.y;
1231 var txtNode = this.createTextNode(path);
1232 coordinatesNode.appendChild(txtNode);
1234 return coordinatesNode;
1237 CLASS_NAME: "OpenLayers.Format.KML"