diff --git a/src/ol/format/kmlformat.js b/src/ol/format/kmlformat.js index e180d2a39f..4cfaa99bf8 100644 --- a/src/ol/format/kmlformat.js +++ b/src/ol/format/kmlformat.js @@ -1,6 +1,7 @@ // FIXME http://earth.google.com/kml/1.0 namespace? // FIXME why does node.getAttribute return an unknown type? // FIXME text +// FIXME serialize arbitrary feature properties goog.provide('ol.format.KML'); @@ -13,12 +14,15 @@ goog.require('goog.object'); goog.require('goog.string'); goog.require('ol.Feature'); goog.require('ol.array'); +goog.require('ol.color'); goog.require('ol.feature'); goog.require('ol.format.XMLFeature'); goog.require('ol.format.XSD'); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); goog.require('ol.geom.MultiLineString'); goog.require('ol.geom.MultiPoint'); goog.require('ol.geom.MultiPolygon'); @@ -32,6 +36,7 @@ goog.require('ol.style.IconOrigin'); goog.require('ol.style.Image'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); +goog.require('ol.style.Text'); goog.require('ol.xml'); @@ -1641,3 +1646,889 @@ ol.format.KML.prototype.readProjectionFromDocument = function(doc) { ol.format.KML.prototype.readProjectionFromNode = function(node) { return ol.proj.get('EPSG:4326'); }; + + +/** + * @param {Node} node Node to append a TextNode with the boolean to. + * @param {boolean} bool Boolean. + * @private + */ +ol.format.KML.writeBooleanTextNode_ = function(node, bool) { + ol.format.XSD.writeStringTextNode(node, (bool) ? '1' : '0'); +}; + + +/** + * @param {Node} node Node to append a TextNode with the color to. + * @param {ol.Color|string} color Color. + * @private + */ +ol.format.KML.writeColorTextNode_ = function(node, color) { + var rgba = ol.color.asArray(color); + var opacity = (rgba.length == 4) ? rgba[3] : 1; + var abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]]; + var i; + for (i = 0; i < 4; ++i) { + var hex = parseInt(abgr[i], 10).toString(16); + abgr[i] = (hex.length == 1) ? '0' + hex : hex; + } + ol.format.XSD.writeStringTextNode(node, abgr.join('')); +}; + + +/** + * @param {Node} node Node to append a TextNode with the coordinates to. + * @param {Array.} coordinates Coordinates. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeCoordinatesTextNode_ = + function(node, coordinates, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context)); + + var layout = goog.object.get(context, 'layout'); + var stride = goog.object.get(context, 'stride'); + + var dimension; + if (layout == ol.geom.GeometryLayout.XY || + layout == ol.geom.GeometryLayout.XYM) { + dimension = 2; + } else if (layout == ol.geom.GeometryLayout.XYZ || + layout == ol.geom.GeometryLayout.XYZM) { + dimension = 3; + } else { + goog.asserts.fail(); + } + + var d, i; + var ii = coordinates.length; + var text = ''; + if (ii > 0) { + text += coordinates[0]; + for (d = 1; d < dimension; ++d) { + text += ',' + coordinates[d]; + } + for (i = stride; i < ii; i += stride) { + text += ' ' + coordinates[i]; + for (d = 1; d < dimension; ++d) { + text += ',' + coordinates[i + d]; + } + } + } + ol.format.XSD.writeStringTextNode(node, text); +}; + + +/** + * @param {Node} node Node. + * @param {Array.} features Features. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeDocument_ = function(node, features, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, ol.format.KML.DOCUMENT_SERIALIZERS_, + ol.format.KML.DOCUMENT_NODE_FACTORY_, features, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Object} icon Icon object. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeIcon_ = function(node, icon, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.ICON_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(icon, orderedKeys); + ol.xml.pushSerializeAndPop(context, + ol.format.KML.ICON_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, objectStack, orderedKeys); + orderedKeys = + ol.format.KML.ICON_SEQUENCE_[ol.format.KML.GX_NAMESPACE_URIS_[0]]; + values = ol.xml.makeSequence(icon, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_SERIALIZERS_, + ol.format.KML.GX_NODE_FACTORY_, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Icon} style Icon style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeIconStyle_ = function(node, style, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + var properties = {}; + var src = style.getSrc(); + var size = style.getSize(); + var iconImageSize = style.getImageSize(); + var iconProperties = { + 'href': src + }; + + if (!goog.isNull(size)) { + goog.object.set(iconProperties, 'w', size[0]); + goog.object.set(iconProperties, 'h', size[1]); + var anchor = style.getAnchor(); // top-left + var origin = style.getOrigin(); // top-left + + if (!goog.isNull(origin) && !goog.isNull(iconImageSize) && + origin[0] !== 0 && origin[1] !== size[1]) { + goog.object.set(iconProperties, 'x', origin[0]); + goog.object.set(iconProperties, 'y', + iconImageSize[1] - (origin[1] + size[1])); + } + + if (!goog.isNull(anchor) && + anchor[0] !== 0 && anchor[1] !== size[1]) { + var /** @type {ol.format.KMLVec2_} */ hotSpot = { + x: anchor[0], + xunits: ol.style.IconAnchorUnits.PIXELS, + y: size[1] - anchor[1], + yunits: ol.style.IconAnchorUnits.PIXELS + }; + goog.object.set(properties, 'hotSpot', hotSpot); + } + } + + goog.object.set(properties, 'Icon', iconProperties); + + var scale = style.getScale(); + if (scale !== 1) { + goog.object.set(properties, 'scale', scale); + } + + var rotation = style.getRotation(); + if (rotation !== 0) { + goog.object.set(properties, 'heading', rotation); // 0-360 + } + + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.ICON_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Text} style style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeLabelStyle_ = function(node, style, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + var properties = {}; + var fill = style.getFill(); + if (!goog.isNull(fill)) { + goog.object.set(properties, 'color', fill.getColor()); + } + var scale = style.getScale(); + if (goog.isDef(scale) && scale !== 1) { + goog.object.set(properties, 'scale', scale); + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = + ol.format.KML.LABEL_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.LABEL_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Stroke} style style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeLineStyle_ = function(node, style, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + var properties = { + 'color': style.getColor(), + 'width': style.getWidth() + }; + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.LINE_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.LINE_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Geometry} geometry Geometry. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeMultiGeometry_ = + function(node, geometry, objectStack) { + goog.asserts.assert( + (geometry instanceof ol.geom.MultiPoint) || + (geometry instanceof ol.geom.MultiLineString) || + (geometry instanceof ol.geom.MultiPolygon)); + /** @type {ol.xml.NodeStackItem} */ + var context = {node: node}; + var type = geometry.getType(); + /** @type {Array.} */ + var geometries; + /** @type {function(*, Array.<*>, string=): (Node|undefined)} */ + var factory; + if (type == ol.geom.GeometryType.MULTI_POINT) { + geometries = + (/** @type {ol.geom.MultiPoint} */ (geometry)).getPoints(); + factory = ol.format.KML.POINT_NODE_FACTORY_; + } else if (type == ol.geom.GeometryType.MULTI_LINE_STRING) { + geometries = + (/** @type {ol.geom.MultiLineString} */ (geometry)).getLineStrings(); + factory = ol.format.KML.LINE_STRING_NODE_FACTORY_; + } else if (type == ol.geom.GeometryType.MULTI_POLYGON) { + geometries = + (/** @type {ol.geom.MultiPolygon} */ (geometry)).getPolygons(); + factory = ol.format.KML.POLYGON_NODE_FACTORY_; + } else { + goog.asserts.fail(); + } + ol.xml.pushSerializeAndPop(context, + ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_, factory, + geometries, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LinearRing} linearRing Linear ring. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeBoundaryIs_ = function(node, linearRing, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, + ol.format.KML.BOUNDARY_IS_SERIALIZERS_, + ol.format.KML.LINEAR_RING_NODE_FACTORY_, [linearRing], objectStack); +}; + + +/** + * FIXME currently we do serialize arbitrary/custom feature properties + * (ExtendedData). + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePlacemark_ = function(node, feature, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + + // set id + if (goog.isDefAndNotNull(feature.getId())) { + node.setAttribute('id', feature.getId()); + } + + // serialize geometry + ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, + ol.format.KML.GEOMETRY_NODE_FACTORY_, + [feature.getGeometry()], objectStack); + + // serialize properties (properties unknown to KML are not serialized) + var properties = feature.getProperties(); + var styleFunction = feature.getStyleFunction(); + if (goog.isDef(styleFunction)) { + // FIXME the styles returned by the style function are supposed to be + // resolution-independent here + var styles = styleFunction.call(feature, 0); + if (!goog.isNull(styles) && styles.length > 0) { + goog.object.set(properties, 'Style', styles[0]); + var textStyle = styles[0].getText(); + if (!goog.isNull(textStyle)) { + goog.object.set(properties, 'name', textStyle.getText()); + } + } + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.PLACEMARK_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePrimitiveGeometry_ = function(node, geometry, objectStack) { + goog.asserts.assert( + (geometry instanceof ol.geom.Point) || + (geometry instanceof ol.geom.LineString) || + (geometry instanceof ol.geom.LinearRing)); + var flatCoordinates = geometry.getFlatCoordinates(); + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + goog.object.set(context, 'layout', geometry.getLayout()); + goog.object.set(context, 'stride', geometry.getStride()); + ol.xml.pushSerializeAndPop(context, + ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_, + ol.format.KML.COORDINATES_NODE_FACTORY_, + [flatCoordinates], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Polygon} polygon Polygon. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePolygon_ = function(node, polygon, objectStack) { + goog.asserts.assertInstanceof(polygon, ol.geom.Polygon); + var linearRings = polygon.getLinearRings(); + goog.asserts.assert(linearRings.length > 0); + var outerRing = linearRings.shift(); + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + // inner rings + ol.xml.pushSerializeAndPop(context, + ol.format.KML.POLYGON_SERIALIZERS_, + ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_, + linearRings, objectStack); + // outer ring + ol.xml.pushSerializeAndPop(context, + ol.format.KML.POLYGON_SERIALIZERS_, + ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_, + [outerRing], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Fill} style Style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePolyStyle_ = function(node, style, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, ol.format.KML.POLY_STYLE_SERIALIZERS_, + ol.format.KML.COLOR_NODE_FACTORY_, [style.getColor()], objectStack); +}; + + +/** + * @param {Node} node Node to append a TextNode with the scale to. + * @param {number|undefined} scale Scale. + * @private + */ +ol.format.KML.writeScaleTextNode_ = function(node, scale) { + ol.format.XSD.writeDecimalTextNode(node, scale * scale); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Style} style Style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeStyle_ = function(node, style, objectStack) { + var /** @type {ol.xml.NodeStackItem} */ context = {node: node}; + var properties = {}; + var fillStyle = style.getFill(); + var strokeStyle = style.getStroke(); + var imageStyle = style.getImage(); + var textStyle = style.getText(); + if (!goog.isNull(imageStyle)) { + goog.object.set(properties, 'IconStyle', imageStyle); + } + if (!goog.isNull(textStyle)) { + goog.object.set(properties, 'LabelStyle', textStyle); + } + if (!goog.isNull(strokeStyle)) { + goog.object.set(properties, 'LineStyle', strokeStyle); + } + if (!goog.isNull(fillStyle)) { + goog.object.set(properties, 'PolyStyle', fillStyle); + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node to append a TextNode with the Vec2 to. + * @param {ol.format.KMLVec2_} vec2 Vec2. + * @private + */ +ol.format.KML.writeVec2_ = function(node, vec2) { + node.setAttribute('x', vec2.x); + node.setAttribute('y', vec2.y); + node.setAttribute('xunits', vec2.xunits); + node.setAttribute('yunits', vec2.yunits); +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.KML_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'Document', 'Placemark' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.KML_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Document': ol.xml.makeChildAppender(ol.format.KML.writeDocument_), + 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.DOCUMENT_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'Placemark' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.DOCUMENT_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) + }); + + +/** + * @const + * @type {Object.} + * @private + */ +ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_ = { + 'Point': 'Point', + 'LineString': 'LineString', + 'LinearRing': 'LinearRing', + 'Polygon': 'Polygon', + 'MultiPoint': 'MultiGeometry', + 'MultiLineString': 'MultiGeometry', + 'MultiPolygon': 'MultiGeometry' +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.ICON_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'href' + ], + ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, [ + 'x', 'y', 'w', 'h' + ])); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.ICON_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'href': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) + }, ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'x': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'y': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'w': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'h': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) + })); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.ICON_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'Icon', 'heading', 'hotSpot', 'scale' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.ICON_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Icon': ol.xml.makeChildAppender(ol.format.KML.writeIcon_), + 'heading': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'hotSpot': ol.xml.makeChildAppender(ol.format.KML.writeVec2_), + 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.LABEL_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'color', 'scale' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.LABEL_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), + 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.LINE_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'color', 'width' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.LINE_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), + 'width': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.BOUNDARY_IS_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LineString': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Point': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.PLACEMARK_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'Style', 'address', 'description', 'name', 'open', + 'phoneNumber', 'styleUrl', 'visibility' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.PLACEMARK_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'MultiGeometry': ol.xml.makeChildAppender( + ol.format.KML.writeMultiGeometry_), + 'LineString': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'LinearRing': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Point': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_), + 'Style': ol.xml.makeChildAppender(ol.format.KML.writeStyle_), + 'address': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'description': ol.xml.makeChildAppender( + ol.format.XSD.writeStringTextNode), + 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'open': ol.xml.makeChildAppender(ol.format.KML.writeBooleanTextNode_), + 'phoneNumber': ol.xml.makeChildAppender( + ol.format.XSD.writeStringTextNode), + 'styleUrl': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'visibility': ol.xml.makeChildAppender( + ol.format.KML.writeBooleanTextNode_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'coordinates': ol.xml.makeChildAppender( + ol.format.KML.writeCoordinatesTextNode_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.POLYGON_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'outerBoundaryIs': ol.xml.makeChildAppender( + ol.format.KML.writeBoundaryIs_), + 'innerBoundaryIs': ol.xml.makeChildAppender( + ol.format.KML.writeBoundaryIs_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.POLY_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'IconStyle', 'LabelStyle', 'LineStyle', 'PolyStyle' + ]); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.KML.STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'IconStyle': ol.xml.makeChildAppender(ol.format.KML.writeIconStyle_), + 'LabelStyle': ol.xml.makeChildAppender(ol.format.KML.writeLabelStyle_), + 'LineStyle': ol.xml.makeChildAppender(ol.format.KML.writeLineStyle_), + 'PolyStyle': ol.xml.makeChildAppender(ol.format.KML.writePolyStyle_) + }); + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.GX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + return ol.xml.createElementNS(ol.format.KML.GX_NAMESPACE_URIS_[0], + 'gx:' + opt_nodeName); +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.DOCUMENT_NODE_FACTORY_ = function(value, objectStack, + opt_nodeName) { + goog.asserts.assertInstanceof(value, ol.Feature); + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode)); + return ol.xml.createElementNS(parentNode.namespaceURI, 'Placemark'); +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, + opt_nodeName) { + if (goog.isDefAndNotNull(value)) { + goog.asserts.assertInstanceof(value, ol.geom.Geometry); + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode)); + return ol.xml.createElementNS(parentNode.namespaceURI, + ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_[value.getType()]); + } +}; + + +/** + * A factory for creating coordinates nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.COLOR_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('color'); + + +/** + * A factory for creating coordinates nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.COORDINATES_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('coordinates'); + + +/** + * A factory for creating innerBoundaryIs nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('innerBoundaryIs'); + + +/** + * A factory for creating Point nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.POINT_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('Point'); + + +/** + * A factory for creating LineString nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.LINE_STRING_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('LineString'); + + +/** + * A factory for creating LinearRing nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.LINEAR_RING_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('LinearRing'); + + +/** + * A factory for creating Polygon nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.POLYGON_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('Polygon'); + + +/** + * A factory for creating outerBoundaryIs nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('outerBoundaryIs'); + + +/** + * Encode an array of features in the KML format. + * + * @function + * @param {Array.} features Features. + * @return {ArrayBuffer|Node|Object|string} Result. + * @todo api + */ +ol.format.KML.prototype.writeFeatures; + + +/** + * @inheritDoc + */ +ol.format.KML.prototype.writeFeaturesNode = function(features) { + var kml = ol.xml.createElementNS('http://earth.google.com/kml/2.2', 'kml'); + kml.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:gx', + ol.format.KML.GX_NAMESPACE_URIS_[0]); + var /** @type {ol.xml.NodeStackItem} */ context = {node: kml}; + var properties = {}; + if (features.length > 1) { + goog.object.set(properties, 'Document', features); + } else if (features.length == 1) { + goog.object.set(properties, 'Placemark', features[0]); + } + var orderedKeys = ol.format.KML.KML_SEQUENCE_[kml.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.KML_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, [], orderedKeys); + return kml; +}; diff --git a/test/spec/ol/format/kmlformat.test.js b/test/spec/ol/format/kmlformat.test.js index c891f2cd35..494bd4e27f 100644 --- a/test/spec/ol/format/kmlformat.test.js +++ b/test/spec/ol/format/kmlformat.test.js @@ -36,6 +36,28 @@ describe('ol.format.KML', function() { expect(f.getId()).to.be(undefined); }); + it('can write a Feature', function() { + var features = [new ol.Feature()]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write a Feature\'s id', function() { + var feature = new ol.Feature(); + feature.setId('foo'); + var features = [feature]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + }); describe('geometry', function() { @@ -53,6 +75,16 @@ describe('ol.format.KML', function() { expect(g).to.be(null); }); + it('can write feature with null geometries', function() { + var features = [new ol.Feature(null)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read Point geometries', function() { var text = '' + @@ -71,6 +103,70 @@ describe('ol.format.KML', function() { expect(g.getCoordinates()).to.eql([1, 2, 3]); }); + it('can write XY Point geometries', function() { + var layout = ol.geom.GeometryLayout.XY; + var point = new ol.geom.Point([1, 2], layout); + var features = [new ol.Feature(point)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZ Point geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var point = new ol.geom.Point([1, 2, 3], layout); + var features = [new ol.Feature(point)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYM Point geometries', function() { + var layout = ol.geom.GeometryLayout.XYM; + var point = new ol.geom.Point([1, 2, 100], layout); + var features = [new ol.Feature(point)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZM Point geometries', function() { + var layout = ol.geom.GeometryLayout.XYZM; + var point = new ol.geom.Point([1, 2, 3, 100], layout); + var features = [new ol.Feature(point)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read LineString geometries', function() { var text = '' + @@ -89,6 +185,73 @@ describe('ol.format.KML', function() { expect(g.getCoordinates()).to.eql([[1, 2, 3], [4, 5, 6]]); }); + it('can write XY LineString geometries', function() { + var layout = ol.geom.GeometryLayout.XY; + var lineString = new ol.geom.LineString([[1, 2], [3, 4]], layout); + var features = [new ol.Feature(lineString)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2 3,4' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZ LineString geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var lineString = new ol.geom.LineString( + [[1, 2, 3], [4, 5, 6]], layout); + var features = [new ol.Feature(lineString)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3 4,5,6' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYM LineString geometries', function() { + var layout = ol.geom.GeometryLayout.XYM; + var lineString = new ol.geom.LineString( + [[1, 2, 100], [3, 4, 200]], layout); + var features = [new ol.Feature(lineString)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2 3,4' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZM LineString geometries', function() { + var layout = ol.geom.GeometryLayout.XYZM; + var lineString = new ol.geom.LineString( + [[1, 2, 3, 100], [4, 5, 6, 200]], layout); + var features = [new ol.Feature(lineString)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3 4,5,6' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read LinearRing geometries', function() { var text = '' + @@ -107,6 +270,74 @@ describe('ol.format.KML', function() { expect(g.getCoordinates()).to.eql([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]); }); + it('can write XY LinearRing geometries', function() { + var layout = ol.geom.GeometryLayout.XY; + var linearRing = new ol.geom.LinearRing( + [[1, 2], [3, 4], [1, 2]], layout); + var features = [new ol.Feature(linearRing)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2 3,4 1,2' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZ LinearRing geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var linearRing = new ol.geom.LinearRing( + [[1, 2, 3], [4, 5, 6], [1, 2, 3]], layout); + var features = [new ol.Feature(linearRing)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3 4,5,6 1,2,3' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYM LinearRing geometries', function() { + var layout = ol.geom.GeometryLayout.XYM; + var linearRing = new ol.geom.LinearRing( + [[1, 2, 100], [3, 4, 200], [1, 2, 100]], layout); + var features = [new ol.Feature(linearRing)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2 3,4 1,2' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZM LinearRing geometries', function() { + var layout = ol.geom.GeometryLayout.XYZM; + var linearRing = new ol.geom.LinearRing( + [[1, 2, 3, 100], [4, 5, 6, 200], [1, 2, 3, 100]], layout); + var features = [new ol.Feature(linearRing)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' 1,2,3 4,5,6 1,2,3' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read Polygon geometries', function() { var text = '' + @@ -130,6 +361,95 @@ describe('ol.format.KML', function() { [[[0, 0, 1], [0, 5, 1], [5, 5, 2], [5, 0, 3]]]); }); + it('can write XY Polygon geometries', function() { + var layout = ol.geom.GeometryLayout.XY; + var polygon = new ol.geom.Polygon( + [[[0, 0], [0, 2], [2, 2], [2, 0], [0, 0]]], layout); + var features = [new ol.Feature(polygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0 0,2 2,2 2,0 0,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZ Polygon geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var polygon = new ol.geom.Polygon( + [[[0, 0, 1], [0, 2, 2], [2, 2, 3], [2, 0, 4], [0, 0, 5]]], layout); + var features = [new ol.Feature(polygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0,1 0,2,2 2,2,3 2,0,4 0,0,5' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYM Polygon geometries', function() { + var layout = ol.geom.GeometryLayout.XYM; + var polygon = new ol.geom.Polygon( + [[[0, 0, 1], [0, 2, 1], [2, 2, 1], [2, 0, 1], [0, 0, 1]]], layout); + var features = [new ol.Feature(polygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0 0,2 2,2 2,0 0,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write XYZM Polygon geometries', function() { + var layout = ol.geom.GeometryLayout.XYZM; + var polygon = new ol.geom.Polygon( + [[[0, 0, 1, 1], [0, 2, 2, 1], [2, 2, 3, 1], + [2, 0, 4, 1], [0, 0, 5, 1]]], layout); + var features = [new ol.Feature(polygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0,1 0,2,2 2,2,3 2,0,4 0,0,5' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read complex Polygon geometries', function() { var text = '' + @@ -165,6 +485,39 @@ describe('ol.format.KML', function() { [[3, 3, 0], [3, 4, 0], [4, 4, 0], [4, 3, 0]]]); }); + it('can write complex Polygon geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var polygon = new ol.geom.Polygon( + [[[0, 0, 1], [0, 5, 1], [5, 5, 2], [5, 0, 3]], + [[1, 1, 0], [1, 2, 0], [2, 2, 0], [2, 1, 0]], + [[3, 3, 0], [3, 4, 0], [4, 4, 0], [4, 3, 0]]], layout); + var features = [new ol.Feature(polygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1,1,0 1,2,0 2,2,0 2,1,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3,3,0 3,4,0 4,4,0 4,3,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0,1 0,5,1 5,5,2 5,0,3' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read MultiPoint geometries', function() { var text = '' + @@ -188,6 +541,28 @@ describe('ol.format.KML', function() { expect(g.getCoordinates()).to.eql([[1, 2, 3], [4, 5, 6]]); }); + it('can write MultiPoint geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var multiPoint = new ol.geom.MultiPoint( + [[1, 2, 3], [4, 5, 6]], layout); + var features = [new ol.Feature(multiPoint)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' 1,2,3' + + ' ' + + ' ' + + ' 4,5,6' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read MultiLineString geometries', function() { var text = '' + @@ -212,6 +587,28 @@ describe('ol.format.KML', function() { [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]); }); + it('can write MultiLineString geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var multiLineString = new ol.geom.MultiLineString( + [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], layout); + var features = [new ol.Feature(multiLineString)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' 1,2,3 4,5,6' + + ' ' + + ' ' + + ' 7,8,9 10,11,12' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read MultiPolygon geometries', function() { var text = '' + @@ -245,6 +642,37 @@ describe('ol.format.KML', function() { [[[3, 0, 0], [3, 1, 0], [4, 1, 0], [4, 0, 0]]]]); }); + it('can write MultiPolygon geometries', function() { + var layout = ol.geom.GeometryLayout.XYZ; + var multiPolygon = new ol.geom.MultiPolygon( + [[[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]]], + [[[3, 0, 0], [3, 1, 0], [4, 1, 0], [4, 0, 0]]]], layout); + var features = [new ol.Feature(multiPolygon)]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 0,0,0 0,1,0 1,1,0 1,0,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3,0,0 3,1,0 4,1,0 4,0,0' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + it('can read empty GeometryCollection geometries', function() { var text = '' + @@ -488,6 +916,42 @@ describe('ol.format.KML', function() { expect(f.get('name')).to.be('My name in CDATA'); }); + it('can write Feature\'s string attributes', function() { + var feature = new ol.Feature(); + feature.set('address', 'My address'); + feature.set('description', 'My description'); + feature.set('name', 'My name'); + feature.set('phoneNumber', 'My phone number'); + var features = [feature]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + '
My address
' + + ' My description' + + ' My name' + + ' My phone number' + + '
' + + '
'; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write Feature\'s boolean attributes', function() { + var feature = new ol.Feature(); + feature.set('open', true); + feature.set('visibility', false); + var features = [feature]; + var node = format.writeFeatures(features); + var text = + '' + + ' ' + + ' 1' + + ' 0' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + }); describe('extended data', function() { @@ -719,6 +1183,9 @@ describe('ol.format.KML', function() { ' ' + '
'; var fs = format.readFeatures(text); + + + expect(fs).to.have.length(1); var f = fs[0]; expect(f).to.be.an(ol.Feature); @@ -850,6 +1317,127 @@ describe('ol.format.KML', function() { expect(style.getZIndex()).to.be(undefined); }); + it('can write an feature\'s icon style', function() { + var style = new ol.style.Style({ + image: new ol.style.Icon({ + anchor: [0.25, 36], + anchorOrigin: ol.style.IconOrigin.TOP_LEFT, + anchorXUnits: 'fraction', + anchorYUnits: 'pixels', + crossOrigin: 'anonymous', + offset: [96, 96], + offsetOrigin: ol.style.IconOrigin.TOP_LEFT, + rotation: 45, + scale: 0.5, + size: [48, 48], + src: 'http://foo.png' + }) + }); + var imageStyle = style.getImage(); + imageStyle.iconImage_.size_ = [192, 144]; // sprite de 12 images(4*3) + var feature = new ol.Feature(); + feature.setStyle([style]); + var node = format.writeFeatures([feature]); + var text = + '' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write an feature\'s text style', function() { + var style = new ol.style.Style({ + text: new ol.style.Text({ + scale: 0.5, + text: 'foo', + fill: new ol.style.Fill({ + color: 'rgb(12, 34, 223)' + }) + }) + }); + var feature = new ol.Feature(); + feature.setStyle([style]); + var node = format.writeFeatures([feature]); + var text = + '' + + ' ' + + ' ' + + ' foo' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write an feature\'s stroke style', function() { + var style = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#112233', + width: 2 + }) + }); + var feature = new ol.Feature(); + feature.setStyle([style]); + var node = format.writeFeatures([feature]); + var text = + '' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + + it('can write an feature\'s fill style', function() { + var style = new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(12, 34, 223, 0.7)' + }) + }); + var feature = new ol.Feature(); + feature.setStyle([style]); + var node = format.writeFeatures([feature]); + var text = + '' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); }); describe('style maps', function() { @@ -1323,6 +1911,25 @@ describe('ol.format.KML', function() { '
')).to.be.empty(); }); + it('can write multiple features', function() { + var feature1 = new ol.Feature(); + feature1.setId('1'); + var feature2 = new ol.Feature(); + feature2.setId('2'); + var node = format.writeFeatures([feature1, feature2]); + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + expect(node).to.xmleql(ol.xml.load(text)); + }); + }); describe('error handling', function() { @@ -1582,6 +2189,7 @@ goog.require('ol.Feature'); goog.require('ol.format.KML'); goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); goog.require('ol.geom.MultiLineString'); goog.require('ol.geom.MultiPoint'); goog.require('ol.geom.MultiPolygon'); @@ -1589,5 +2197,8 @@ goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.style.Fill'); goog.require('ol.style.Icon'); +goog.require('ol.style.IconOrigin'); goog.require('ol.style.Stroke'); goog.require('ol.style.Style'); +goog.require('ol.style.Text'); +goog.require('ol.xml');