diff --git a/lib/OpenLayers/Format/KML.js b/lib/OpenLayers/Format/KML.js index 1754fd8eb5..f76b4ff708 100644 --- a/lib/OpenLayers/Format/KML.js +++ b/lib/OpenLayers/Format/KML.js @@ -64,9 +64,25 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { * APIProperty: extractAttributes * {Boolean} Extract attributes from KML. Default is true. * Extracting styleUrls requires this to be set to true + * Note that currently only Data and SimpleData + * elements are handled. */ extractAttributes: true, + /** + * APIProperty: kvpAttributes + * {Boolean} Only used if extractAttributes is true. + * If set to true, attributes will be simple + * key-value pairs, compatible with other formats, + * Any displayName elements will be ignored. + * If set to false, attributes will be objects, + * retaining any displayName elements, but not + * compatible with other formats. Any CDATA in + * displayName will be read in as a string value. + * Default is false. + */ + kvpAttributes: false, + /** * Property: extractStyles * {Boolean} Extract styles from KML. Default is false. @@ -1078,12 +1094,16 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { var valueNode = data.getElementsByTagName("value"); if (valueNode.length) { ed['value'] = this.getChildValue(valueNode[0]); - } - var nameNode = data.getElementsByTagName("displayName"); - if (nameNode.length) { - ed['displayName'] = this.getChildValue(nameNode[0]); } - attributes[key] = ed; + if (this.kvpAttributes) { + attributes[key] = ed['value']; + } else { + var nameNode = data.getElementsByTagName("displayName"); + if (nameNode.length) { + ed['displayName'] = this.getChildValue(nameNode[0]); + } + attributes[key] = ed; + } } var simpleDataNodes = node.getElementsByTagName("SimpleData"); for (i = 0, len = simpleDataNodes.length; i < len; i++) { @@ -1091,8 +1111,12 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { data = simpleDataNodes[i]; key = data.getAttribute("name"); ed['value'] = this.getChildValue(data); - ed['displayName'] = key; - attributes[key] = ed; + if (this.kvpAttributes) { + attributes[key] = ed['value']; + } else { + ed['displayName'] = key; + attributes[key] = ed; + } } return attributes; @@ -1209,7 +1233,14 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { var geometryNode = this.buildGeometryNode(feature.geometry); placemarkNode.appendChild(geometryNode); - // TBD - deal with remaining (non name/description) attributes. + // output attributes as extendedData + if (feature.attributes) { + var edNode = this.buildExtendedData(feature.attributes); + if (edNode) { + placemarkNode.appendChild(edNode); + } + } + return placemarkNode; }, @@ -1440,5 +1471,48 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { return point.x + "," + point.y; }, + /** + * Method: buildExtendedData + * + * Parameters: + * attributes - {Object} + * + * Returns + * {DOMElement} A KML ExtendedData node or {null} if no attributes. + */ + buildExtendedData: function(attributes) { + var extendedData = this.createElementNS(this.kmlns, "ExtendedData"); + for (attributeName in attributes) { + // empty, name, description, styleUrl attributes ignored + if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") { + var data = this.createElementNS(this.kmlns, "Data"); + data.setAttribute("name", attributeName); + var value = this.createElementNS(this.kmlns, "value"); + if (typeof attributes[attributeName] == "object") { + // cater for object attributes with 'value' properties + // other object properties will output an empty node + if (attributes[attributeName].value) { + value.appendChild(this.createTextNode(attributes[attributeName].value)); + } + if (attributes[attributeName].displayName) { + var displayName = this.createElementNS(this.kmlns, "displayName"); + // displayName always written as CDATA + displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName)); + data.appendChild(displayName); + } + } else { + value.appendChild(this.createTextNode(attributes[attributeName])); + } + data.appendChild(value); + extendedData.appendChild(data); + } + } + if (this.isSimpleContent(extendedData)) { + return null; + } else { + return extendedData; + } + }, + CLASS_NAME: "OpenLayers.Format.KML" }); diff --git a/tests/Format/KML.html b/tests/Format/KML.html index 42b6fe6e0d..a7dfd976c2 100644 --- a/tests/Format/KML.html +++ b/tests/Format/KML.html @@ -198,21 +198,35 @@ t.ok(style.t, "getStyle returns copy of style rather than reference"); } function test_Format_KML_extendedData(t) { - t.plan(2); + t.plan(6); var f = new OpenLayers.Format.KML(); var features = f.read(OpenLayers.Util.getElement("kml_extendeddata").value); - t.eq(features[0].attributes.all_bridges.value, "3030", "read value from extendeddata correctly."); - t.eq(features[0].attributes.all_bridges.displayName, "all bridges", "read displayName from extendeddata correctly."); + t.eq(features[0].attributes.holeYardage.value, "234", "read value from extendeddata correctly."); + t.eq(features[0].attributes.holeYardage.displayName, "The yardage is ", "read displayName from extendeddata correctly."); + t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage.value, features[0].attributes.holeYardage.value, "attribute value written correctly"); + t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage.displayName, features[0].attributes.holeYardage.displayName, "attribute displayName written correctly"); + f.kvpAttributes = true; + features = f.read(OpenLayers.Util.getElement("kml_extendeddata").value); + t.eq(features[0].attributes.holeYardage, "234", "read kvp value from extendeddata correctly."); + t.eq(f.read(f.write(features[0]))[0].attributes.holeYardage, features[0].attributes.holeYardage, "kvp attribute value written correctly"); } function test_Format_KML_extendedData_SchemaData(t) { - t.plan(4); + t.plan(10); var f = new OpenLayers.Format.KML(); var features = f.read(OpenLayers.Util.getElement("kml_extendeddata2").value); t.eq(features[0].attributes.TrailHeadName.value, "Pi in the sky", "read value from extendeddata (schema data) correctly."); t.eq(features[0].attributes.TrailHeadName.displayName, "TrailHeadName", "read displayName from extendeddata correctly"); t.eq(features[0].attributes.ElevationGain.value, "10", "read value from extendeddata (schema data) correctly."); t.eq(features[0].attributes.ElevationGain.displayName, "ElevationGain", "read displayName from extendeddata correctly"); + t.eq(f.read(f.write(features[0]))[0].attributes.TrailHeadName.value, features[0].attributes.TrailHeadName.value, "attribute value from extendeddata (schema data) written correctly"); + t.eq(f.read(f.write(features[0]))[0].attributes.ElevationGain.value, features[0].attributes.ElevationGain.value, "attribute value from extendeddata (schema data) written correctly"); + f.kvpAttributes = true; + features = f.read(OpenLayers.Util.getElement("kml_extendeddata2").value); + t.eq(features[0].attributes.TrailHeadName, "Pi in the sky", "read kvp value from extendeddata (schema data) correctly."); + t.eq(features[0].attributes.ElevationGain, "10", "read kvp value from extendeddata (schema data) correctly."); + t.eq(f.read(f.write(features[0]))[0].attributes.TrailHeadName, features[0].attributes.TrailHeadName, "kvp attribute value from extendeddata (schema data) written correctly"); + t.eq(f.read(f.write(features[0]))[0].attributes.ElevationGain, features[0].attributes.ElevationGain, "kvp attribute value from extendeddata (schema data) written correctly"); } function test_Format_KML_placemarkName(t) { @@ -287,49 +301,61 @@