diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js index 77dc5a7aff..96eb136447 100644 --- a/src/ol/format/KML.js +++ b/src/ol/format/KML.js @@ -882,32 +882,32 @@ class KML extends XMLFeature { * @return {Style} style Style. */ function createNameStyleFunction(foundStyle, name) { - let textStyle = null; const textOffset = [0, 0]; let textAlign = 'start'; - if (foundStyle.getImage()) { - let imageSize = foundStyle.getImage().getImageSize(); + const imageStyle = foundStyle.getImage(); + if (imageStyle) { + let imageSize = imageStyle.getImageSize(); if (imageSize === null) { imageSize = DEFAULT_IMAGE_STYLE_SIZE; } if (imageSize.length == 2) { - const imageScale = foundStyle.getImage().getScale(); - // Offset the label to be centered to the right of the icon, if there is - // one. + const imageScale = imageStyle.getScale(); + // Offset the label to be centered to the right of the icon, + // if there is one. textOffset[0] = imageScale * imageSize[0] / 2; textOffset[1] = -imageScale * imageSize[1] / 2; textAlign = 'left'; } } - if (foundStyle.getText() !== null) { + let textStyle = foundStyle.getText(); + if (textStyle) { // clone the text style, customizing it with name, alignments and offset. // Note that kml does not support many text options that OpenLayers does (rotation, textBaseline). - const foundText = foundStyle.getText(); - textStyle = foundText.clone(); - textStyle.setFont(foundText.getFont() || DEFAULT_TEXT_STYLE.getFont()); - textStyle.setScale(foundText.getScale() || DEFAULT_TEXT_STYLE.getScale()); - textStyle.setFill(foundText.getFill() || DEFAULT_TEXT_STYLE.getFill()); - textStyle.setStroke(foundText.getStroke() || DEFAULT_TEXT_STROKE_STYLE); + textStyle = textStyle.clone(); + textStyle.setFont(textStyle.getFont() || DEFAULT_TEXT_STYLE.getFont()); + textStyle.setScale(textStyle.getScale() || DEFAULT_TEXT_STYLE.getScale()); + textStyle.setFill(textStyle.getFill() || DEFAULT_TEXT_STYLE.getFill()); + textStyle.setStroke(textStyle.getStroke() || DEFAULT_TEXT_STROKE_STYLE); } else { textStyle = DEFAULT_TEXT_STYLE.clone(); } @@ -916,8 +916,10 @@ function createNameStyleFunction(foundStyle, name) { textStyle.setOffsetY(textOffset[1]); textStyle.setTextAlign(textAlign); - const nameStyle = foundStyle.clone(); - nameStyle.setText(textStyle); + const nameStyle = new Style({ + image: imageStyle, + text: textStyle + }); return nameStyle; } @@ -1766,13 +1768,57 @@ function readStyle(node, objectStack) { const textStyle = /** @type {Text} */ ('textStyle' in styleObject ? styleObject['textStyle'] : DEFAULT_TEXT_STYLE); - let strokeStyle = /** @type {Stroke} */ + const strokeStyle = /** @type {Stroke} */ ('strokeStyle' in styleObject ? styleObject['strokeStyle'] : DEFAULT_STROKE_STYLE); const outline = /** @type {boolean|undefined} */ (styleObject['outline']); if (outline !== undefined && !outline) { - strokeStyle = null; + // if the polystyle specifies no outline two styles are needed, + // one for non-polygon geometries where linestrings require a stroke + // and one for polygons where there should be no stroke + return [ + new Style({ + geometry: function(feature) { + const geometry = feature.getGeometry(); + const type = geometry.getType(); + if (type === GeometryType.GEOMETRY_COLLECTION) { + return new GeometryCollection( + geometry.getGeometriesArray().filter(function(geometry) { + const type = geometry.getType(); + return type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON; + }) + ); + } else if (type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON) { + return geometry; + } + }, + fill: fillStyle, + image: imageStyle, + stroke: strokeStyle, + text: textStyle, + zIndex: undefined // FIXME + }), + new Style({ + geometry: function(feature) { + const geometry = feature.getGeometry(); + const type = geometry.getType(); + if (type === GeometryType.GEOMETRY_COLLECTION) { + return new GeometryCollection( + geometry.getGeometriesArray().filter(function(geometry) { + const type = geometry.getType(); + return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON; + }) + ); + } else if (type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON) { + return geometry; + } + }, + fill: fillStyle, + stroke: null, + zIndex: undefined // FIXME + }) + ]; } return [new Style({ fill: fillStyle, @@ -2534,7 +2580,7 @@ function writeLineStyle(node, style, objectStack) { const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node}; const properties = { 'color': style.getColor(), - 'width': style.getWidth() + 'width': Number(style.getWidth()) || 1 }; const parentNode = objectStack[objectStack.length - 1].node; const orderedKeys = LINE_STYLE_SEQUENCE[parentNode.namespaceURI]; diff --git a/test/spec/ol/format/kml.test.js b/test/spec/ol/format/kml.test.js index 6f4e2fe2d6..700d8273c8 100644 --- a/test/spec/ol/format/kml.test.js +++ b/test/spec/ol/format/kml.test.js @@ -2167,6 +2167,11 @@ describe('ol.format.KML', function() { }); it('disables the stroke when outline is \'0\'', function() { + const lineString = new LineString([[1, 2], [3, 4]]); + const polygon = new Polygon([[[0, 0], [0, 2], [2, 2], [2, 0], [0, 0]]]); + const lineStringFeature = new Feature(lineString); + const polygonFeature = new Feature(polygon); + const collectionFeature = new Feature(new GeometryCollection([lineString, polygon])); const text = '' + ' ' + @@ -2190,20 +2195,53 @@ describe('ol.format.KML', function() { expect(styleFunction).not.to.be(undefined); const styleArray = styleFunction(f, 0); expect(styleArray).to.be.an(Array); - expect(styleArray).to.have.length(1); + expect(styleArray).to.have.length(2); + const style = styleArray[0]; expect(style).to.be.an(Style); + expect(style.getGeometryFunction()(lineStringFeature)).to.be(lineString); + expect(style.getGeometryFunction()(polygonFeature)).to.be(undefined); + const gc = style.getGeometryFunction()(collectionFeature); + expect(gc).to.be.an(GeometryCollection); + const gs = gc.getGeometries(); + expect(gs).to.be.an(Array); + expect(gs).to.have.length(1); + expect(gs[0]).to.be.an(LineString); + expect(gs[0].getCoordinates()).to.eql(lineString.getCoordinates()); const fillStyle = style.getFill(); expect(fillStyle).to.be.an(Fill); expect(fillStyle.getColor()).to.eql([0x78, 0x56, 0x34, 0x12 / 255]); expect(style.getImage()).to.be(getDefaultImageStyle()); - expect(style.getStroke()).to.be(null); + const strokeStyle = style.getStroke(); + expect(strokeStyle).to.be.an(Stroke); + expect(strokeStyle.getColor()).to.eql([0x78, 0x56, 0x34, 0x12 / 255]); + expect(strokeStyle.getWidth()).to.be(9); expect(style.getText()).to.be(getDefaultTextStyle()); expect(style.getZIndex()).to.be(undefined); + + const style1 = styleArray[1]; + expect(style1).to.be.an(Style); + expect(style1.getGeometryFunction()(lineStringFeature)).to.be(undefined); + expect(style1.getGeometryFunction()(polygonFeature)).to.be(polygon); + const gc1 = style1.getGeometryFunction()(collectionFeature); + expect(gc1).to.be.an(GeometryCollection); + const gs1 = gc1.getGeometries(); + expect(gs1).to.be.an(Array); + expect(gs1).to.have.length(1); + expect(gs1[0]).to.be.an(Polygon); + expect(gs1[0].getCoordinates()).to.eql(polygon.getCoordinates()); + expect(style1.getFill()).to.be(fillStyle); + expect(style1.getStroke()).to.be(null); + expect(style1.getZIndex()).to.be(undefined); }); it('disables both fill and stroke when fill and outline are \'0\'', function() { + const lineString = new LineString([[1, 2], [3, 4]]); + const polygon = new Polygon([[[0, 0], [0, 2], [2, 2], [2, 0], [0, 0]]]); + const lineStringFeature = new Feature(lineString); + const polygonFeature = new Feature(polygon); + const collectionFeature = new Feature(new GeometryCollection([lineString, polygon])); const text = '' + ' ' + @@ -2228,14 +2266,42 @@ describe('ol.format.KML', function() { expect(styleFunction).not.to.be(undefined); const styleArray = styleFunction(f, 0); expect(styleArray).to.be.an(Array); - expect(styleArray).to.have.length(1); + expect(styleArray).to.have.length(2); + const style = styleArray[0]; expect(style).to.be.an(Style); + expect(style.getGeometryFunction()(lineStringFeature)).to.be(lineString); + expect(style.getGeometryFunction()(polygonFeature)).to.be(undefined); + const gc = style.getGeometryFunction()(collectionFeature); + expect(gc).to.be.an(GeometryCollection); + const gs = gc.getGeometries(); + expect(gs).to.be.an(Array); + expect(gs).to.have.length(1); + expect(gs[0]).to.be.an(LineString); + expect(gs[0].getCoordinates()).to.eql(lineString.getCoordinates()); expect(style.getFill()).to.be(null); expect(style.getImage()).to.be(getDefaultImageStyle()); - expect(style.getStroke()).to.be(null); + const strokeStyle = style.getStroke(); + expect(strokeStyle).to.be.an(Stroke); + expect(strokeStyle.getColor()).to.eql([0x78, 0x56, 0x34, 0x12 / 255]); + expect(strokeStyle.getWidth()).to.be(9); expect(style.getText()).to.be(getDefaultTextStyle()); expect(style.getZIndex()).to.be(undefined); + + const style1 = styleArray[1]; + expect(style1).to.be.an(Style); + expect(style1.getGeometryFunction()(lineStringFeature)).to.be(undefined); + expect(style1.getGeometryFunction()(polygonFeature)).to.be(polygon); + const gc1 = style1.getGeometryFunction()(collectionFeature); + expect(gc1).to.be.an(GeometryCollection); + const gs1 = gc1.getGeometries(); + expect(gs1).to.be.an(Array); + expect(gs1).to.have.length(1); + expect(gs1[0]).to.be.an(Polygon); + expect(gs1[0].getCoordinates()).to.eql(polygon.getCoordinates()); + expect(style1.getFill()).to.be(null); + expect(style1.getStroke()).to.be(null); + expect(style1.getZIndex()).to.be(undefined); }); it('can create text style for named point placemarks (including html character codes)', function() {