/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license. * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt * for the full text of the license. */ /** * @requires OpenLayers/Format/XML.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Geometry/Point.js * @requires OpenLayers/Geometry/LineString.js * @requires OpenLayers/Geometry/Polygon.js * @requires OpenLayers/Geometry/Collection.js * * Class: OpenLayers.Format.KML * Read/Wite KML. Create a new instance with the * constructor. * * Inherits from: * - */ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: kmlns * {String} KML Namespace to use. Defaults to 2.0 namespace. */ kmlns: "http://earth.google.com/kml/2.0", /** * APIProperty: placemarksDesc * {String} Name of the placemarks. Default is "No description available." */ placemarksDesc: "No description available", /** * APIProperty: foldersName * {String} Name of the folders. Default is "OpenLayers export." */ foldersName: "OpenLayers export", /** * APIProperty: foldersDesc * {String} Description of the folders. Default is "Exported on [date]." */ foldersDesc: "Exported on " + new Date(), /** * APIProperty: extractAttributes * {Boolean} Extract attributes from KML. Default is true. */ extractAttributes: true, /** * Constructor: OpenLayers.Format.KML * Create a new parser for KML. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { // compile regular expressions once instead of every time they are used this.regExes = { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }; OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Read data from a string, and return a list of features. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array()} List of features. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var featureNodes = this.getElementsByTagNameNS(data, this.kmlns, "Placemark"); var numFeatures = featureNodes.length; var features = new Array(numFeatures); for(var i=0; i} * * Returns: * {} A vector feature. */ parseFeature: function(node) { // only accept one geometry per feature - look for highest "order" var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; var type, nodeList, geometry, parser; for(var i=0; i 0) { // only deal with first geometry of this type var parser = this.parseGeometry[type.toLowerCase()]; if(parser) { geometry = parser.apply(this, [nodeList[0]]); } else { OpenLayers.Console.error("Unsupported geometry type: " + type); } // stop looking for different geometry types break; } } // construct feature (optionally with attributes) var attributes; if(this.extractAttributes) { attributes = this.parseAttributes(node); } var feature = new OpenLayers.Feature.Vector(geometry, attributes); var fid = node.getAttribute("id"); if(fid != null) { feature.fid = fid; } return feature; }, /** * Property: parseGeometry * Properties of this object are the functions that parse geometries based * on their type. */ parseGeometry: { /** * Method: parseGeometry.point * Given a KML node representing a point geometry, create an OpenLayers * point geometry. * * Parameters: * node - {DOMElement} A KML Point node. * * Returns: * {} A point geometry. */ point: function(node) { var nodeList = this.getElementsByTagNameNS(node, this.kmlns, "coordinates"); var coords = []; if(nodeList.length > 0) { var coordString = nodeList[0].firstChild.nodeValue; coordString = coordString.replace(this.regExes.removeSpace, ""); coords = coordString.split(","); } var point = null; if(coords.length > 1) { // preserve third dimension if(coords.length == 2) { coords[2] = null; } point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]); } else { throw "Bad coordinate string: " + coordString; } return point; }, /** * Method: parseGeometry.linestring * Given a KML node representing a linestring geometry, create an * OpenLayers linestring geometry. * * Parameters: * node - {DOMElement} A KML LineString node. * * Returns: * {} A linestring geometry. */ linestring: function(node, ring) { var nodeList = this.getElementsByTagNameNS(node, this.kmlns, "coordinates"); var line = null; if(nodeList.length > 0) { var coordString = nodeList[0].firstChild.nodeValue; coordString = coordString.replace(this.regExes.trimSpace, ""); coordString = coordString.replace(this.regExes.trimComma, ","); var pointList = coordString.split(this.regExes.splitSpace); var numPoints = pointList.length; var points = new Array(numPoints); var coords, numCoords; for(var i=0; i 1) { if(coords.length == 2) { coords[2] = null; } points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]); } else { throw "Bad LineString point coordinates: " + pointList[i]; } } if(numPoints) { if(ring) { line = new OpenLayers.Geometry.LinearRing(points); } else { line = new OpenLayers.Geometry.LineString(points); } } else { throw "Bad LineString coordinates: " + coordString; } } return line; }, /** * Method: parseGeometry.polygon * Given a KML node representing a polygon geometry, create an * OpenLayers polygon geometry. * * Parameters: * node - {DOMElement} A KML Polygon node. * * Returns: * {} A polygon geometry. */ polygon: function(node) { var nodeList = this.getElementsByTagNameNS(node, this.kmlns, "LinearRing"); var numRings = nodeList.length; var components = new Array(numRings); if(numRings > 0) { // this assumes exterior ring first, inner rings after var ring; for(var i=0; i} A geometry collection. */ multigeometry: function(node) { var child, parser; var parts = []; var children = node.childNodes; for(var i=0; i} * * Returns: * {Object} An attributes object. */ parseAttributes: function(node) { var attributes = {}; // assume attribute nodes are type 1 children with a type 3 child var child, grandchildren, grandchild; var children = node.childNodes; for(var i=0; i features. * * Returns: * {String} A KML string. */ write: function(features) { if(!(features instanceof Array)) { features = [features]; } var kml = this.createElementNS(this.kmlns, "kml"); var folder = this.createFolderXML(); for(var i=0; i} * * Returns: * {DOMElement} */ createPlacemarkXML: function(feature) { // Placemark name var placemarkName = this.createElementNS(this.kmlns, "name"); var name = (feature.attributes.name) ? feature.attributes.name : feature.id; placemarkName.appendChild(this.createTextNode(name)); // Placemark description var placemarkDesc = this.createElementNS(this.kmlns, "description"); var desc = (feature.attributes.description) ? feature.attributes.description : this.placemarksDesc; placemarkDesc.appendChild(this.createTextNode(desc)); // Placemark var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); if(feature.fid != null) { placemarkNode.setAttribute("id", feature.fid); } placemarkNode.appendChild(placemarkName); placemarkNode.appendChild(placemarkDesc); // Geometry node (Point, LineString, etc. nodes) var geometryNode = this.buildGeometryNode(feature.geometry); placemarkNode.appendChild(geometryNode); // TBD - deal with remaining (non name/description) attributes. return placemarkNode; }, /** * Method: buildGeometryNode * Builds and returns a KML geometry node with the given geometry. * * Parameters: * geometry - {} * * Returns: * {DOMElement} */ buildGeometryNode: function(geometry) { var className = geometry.CLASS_NAME; var type = className.substring(className.lastIndexOf(".") + 1); var builder = this.buildGeometry[type.toLowerCase()]; var node = null; if(builder) { node = builder.apply(this, [geometry]); } return node; }, /** * Property: buildGeometry * Object containing methods to do the actual geometry node building * based on geometry type. */ buildGeometry: { // TBD: Anybody care about namespace aliases here (these nodes have // no prefixes)? /** * Method: buildGeometry.point * Given an OpenLayers point geometry, create a KML point. * * Parameters: * geometry - {} A point geometry. * * Returns: * {DOMElement} A KML point node. */ point: function(geometry) { var kml = this.createElementNS(this.kmlns, "Point"); kml.appendChild(this.buildCoordinatesNode(geometry)); return kml; }, /** * Method: buildGeometry.multipoint * Given an OpenLayers multipoint geometry, create a KML * GeometryCollection. * * Parameters: * geometry - {} A multipoint geometry. * * Returns: * {DOMElement} A KML GeometryCollection node. */ multipoint: function(geometry) { return this.buildGeometry.collection(geometry); }, /** * Method: buildGeometry.linestring * Given an OpenLayers linestring geometry, create a KML linestring. * * Parameters: * geometry - {} A linestring geometry. * * Returns: * {DOMElement} A KML linestring node. */ linestring: function(geometry) { var kml = this.createElementNS(this.kmlns, "LineString"); kml.appendChild(this.buildCoordinatesNode(geometry)); return kml; }, /** * Method: buildGeometry.multilinestring * Given an OpenLayers multilinestring geometry, create a KML * GeometryCollection. * * Parameters: * geometry - {} A multilinestring geometry. * * Returns: * {DOMElement} A KML GeometryCollection node. */ multilinestring: function(geometry) { return this.buildGeometry.collection(geometry); }, /** * Method: buildGeometry.linearring * Given an OpenLayers linearring geometry, create a KML linearring. * * Parameters: * geometry - {} A linearring geometry. * * Returns: * {DOMElement} A KML linearring node. */ linearring: function(geometry) { var kml = this.createElementNS(this.kmlns, "LinearRing"); kml.appendChild(this.buildCoordinatesNode(geometry)); return kml; }, /** * Method: buildGeometry.polygon * Given an OpenLayers polygon geometry, create a KML polygon. * * Parameters: * geometry - {} A polygon geometry. * * Returns: * {DOMElement} A KML polygon node. */ polygon: function(geometry) { var kml = this.createElementNS(this.kmlns, "Polygon"); var rings = geometry.components; var ringMember, ringGeom, type; for(var i=0; i} A multipolygon geometry. * * Returns: * {DOMElement} A KML GeometryCollection node. */ multipolygon: function(geometry) { return this.buildGeometry.collection(geometry); }, /** * Method: buildGeometry.collection * Given an OpenLayers geometry collection, create a KML MultiGeometry. * * Parameters: * geometry - {} A geometry collection. * * Returns: * {DOMElement} A KML MultiGeometry node. */ collection: function(geometry) { var kml = this.createElementNS(this.kmlns, "MultiGeometry"); var child; for(var i=0; i... * * Parameters: * geometry - {} * * Return: * {DOMElement} */ buildCoordinatesNode: function(geometry) { var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); var path; var points = geometry.components; if(points) { // LineString or LinearRing var point; var numPoints = points.length; var parts = new Array(numPoints); for(var i=0; i