diff --git a/src/ol/utils/MapsetKmlFormat.ts b/src/ol/utils/MapsetKmlFormat.ts index d9331aba..636254c6 100644 --- a/src/ol/utils/MapsetKmlFormat.ts +++ b/src/ol/utils/MapsetKmlFormat.ts @@ -69,6 +69,11 @@ interface IconOptions { zIndex: number; } +interface ScaleOptions { + defaultScale?: number; + resolution: number; +} + // Comes from ol >= 6.7, // https://github.com/openlayers/openlayers/blob/main/src/ol/format/KML.js#L320 const scaleForSize = (size: Size) => { @@ -138,6 +143,53 @@ const getLineIcon = ( }; class MapsetKmlFormat { + /** + * Get Document properties. + * @param {String} propertyName The property name to get (name, description, zoomLimits). + * @param {String} kmlString A string representing a KML file. + * @returns {Object} An object containing the document properties (name, description, zoomLimits). + */ + public getDocumentProperty(propertyName: string, kmlString: string) { + if (!propertyName) { + return null; + } + + let kmlDoc: Document; + try { + kmlDoc = parse(kmlString); + } catch (error) { + console.error('Invalid KML string', error); + return null; + } + + const documentNode = kmlDoc.getElementsByTagName('Document')[0]; + if (!documentNode) { + return null; + } + + if (propertyName === 'name') { + return documentNode.getElementsByTagName('name')[0]?.textContent ?? null; + } + + if (propertyName === 'description') { + return ( + documentNode.getElementsByTagName('description')[0]?.textContent ?? null + ); + } + + const propertiesData = documentNode + .getElementsByTagName('ExtendedData')[0] + ?.getElementsByTagName('Data'); + + return ( + Array.from(propertiesData || []) + .find((data) => { + return data.getAttribute('name') === propertyName; + }) + ?.getElementsByTagName('value')[0]?.textContent ?? null + ); + } + /** * Read a KML string. * @param {String} kmlString A string representing a KML file. @@ -168,7 +220,7 @@ class MapsetKmlFormat { transform( JSON.parse(circleGeometryCenter as string) as Coordinate, EPSG_4326, - featureProjection || EPSG_4326, + featureProjection ?? EPSG_4326, ), parseFloat(circleGeometryRadius as string), ); @@ -259,6 +311,13 @@ class MapsetKmlFormat { style?.setZIndex(parseInt(feature.get('zIndex') as string, 10)); } + const scaleOptions = (feature.get('pictureOptions') ?? + feature.get('scaleOptions')) as ScaleOptions | string; + if (scaleOptions && typeof scaleOptions === 'string') { + const parsed = JSON.parse(scaleOptions) as ScaleOptions; + feature.set('scaleOptions', parsed); + } + // if the feature is a Point and we are offline, we use default vector // style. // if the feature is a Point and has a name with a text style, we @@ -409,32 +468,18 @@ class MapsetKmlFormat { stroke = null; styles = (feat, resolution) => { - /* Options to be used for picture scaling with map, should have at least + /* Options to be used for feature scaling with map, should have at least * a resolution attribute (this is the map resolution at the zoom level when - * the picture is created), can take an optional constant for further scale + * the feature is created), can take an optional constant for further scale * adjustment. * e.g. { resolution: 0.123, defaultScale: 1 / 6 } */ - interface PictureOptions { - defaultScale?: number; - resolution: number; - } - - if (feat.get('pictureOptions')) { - let pictureOptions = feat.get('pictureOptions') as - | PictureOptions - | string; - if (typeof pictureOptions === 'string') { - pictureOptions = JSON.parse(pictureOptions) as PictureOptions; - } - (feat as FeatureType).set('pictureOptions', pictureOptions); - if (pictureOptions.resolution) { - image?.setScale( - (pictureOptions.resolution / resolution) * - (pictureOptions?.defaultScale ?? 1), - ); - } + const scaleOpts = feat.get('scaleOptions') as ScaleOptions; + if (scaleOpts && image instanceof Icon && scaleOpts.resolution) { + image.setScale( + (scaleOpts.resolution / resolution) * (scaleOpts.defaultScale ?? 1), + ); } return new Style({ @@ -742,14 +787,6 @@ class MapsetKmlFormat { // We set the scale as extended metadata because the in the KML is related to a 32px img, since ol >= 6.10. clone.set('iconScale', newStyle.image.getScale()); } - - // Set map resolution to use for icon-to-map proportional scaling - if (feature.get('pictureOptions')) { - clone.set( - 'pictureOptions', - JSON.stringify(feature.get('pictureOptions')), - ); - } } // In case a fill pattern should be applied (use fillPattern attribute to store pattern id, color etc) @@ -768,6 +805,14 @@ class MapsetKmlFormat { clone.set('minZoom', parseFloat(feature.get('minZoom') as string)); } + // Set map resolution to use for feature-to-map proportional scaling + if (feature.get('pictureOptions') || feature.get('scaleOptions')) { + clone.set( + 'scaleOptions', + JSON.stringify(feature.get('scaleOptions')), + ); + } + // If only text is displayed we must specify an // image style with scale=0 if (newStyle.text && !newStyle.image) {