From 2542d48d23f26c067e52f5bcb2ae01291d3446b3 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 15 Jun 2026 14:07:12 +0200 Subject: [PATCH 1/9] feat: add popup info on cesium and ol style to display acurazy zone --- api-idee-js/src/facade/js/control/Location.js | 82 +++++++++++++++++-- api-idee-js/src/facade/js/i18n/en.json | 6 ++ api-idee-js/src/facade/js/i18n/es.json | 6 ++ .../src/impl/cesium/js/control/Location.js | 61 ++++++++++---- .../src/impl/ol/js/control/Attributions.js | 2 +- .../src/impl/ol/js/control/Location.js | 63 +++++++++----- 6 files changed, 176 insertions(+), 44 deletions(-) diff --git a/api-idee-js/src/facade/js/control/Location.js b/api-idee-js/src/facade/js/control/Location.js index 8d20c893b..575fb27b4 100644 --- a/api-idee-js/src/facade/js/control/Location.js +++ b/api-idee-js/src/facade/js/control/Location.js @@ -5,6 +5,7 @@ import LocationImpl from 'impl/control/Location'; import locationTemplate from 'templates/location'; import myhelp from 'templates/locationhelp'; import 'assets/css/controls/location'; +import * as EventType from 'IDEE/event/eventtype'; import { getValue } from '../i18n/language'; import ControlBase from './Control'; import { @@ -68,7 +69,11 @@ class Location extends ControlBase { } // implementation of this control - const impl = new LocationImpl(tracking, highAccuracy, 60000, vendorOptions); + const impl = new LocationImpl({ + tracking, + highAccuracy, + vendorOptions, + }); // calls the super constructor super(Location.NAME, impl, options); @@ -101,9 +106,10 @@ class Location extends ControlBase { * @api */ createView(map) { + const lang = this.translation || {}; const element = compileTemplate(locationTemplate, { vars: { - title: this.tooltip ?? getValue('location').title, + title: this.tooltip ?? lang.title, }, }); @@ -131,6 +137,60 @@ class Location extends ControlBase { return element.querySelector('button#m-location-button'); } + /** + * Genera un objeto clave-valor traducido con los datos actualizados. + * Perfecto para el InfoBox nativo de Cesium. + * @param {number} lon + * @param {number} lat + * @returns {Object} Diccionario mapeado con traducciones vigentes + */ + getPopupProperties(lon, lat) { + const lang = this.translation || {}; + const labels = lang.popup; + + return { + [labels.title]: labels.titleValue || 'Mi ubicación', + [labels.longitude]: typeof lon === 'number' ? lon.toFixed(6) : lon, + [labels.latitude]: typeof lat === 'number' ? lat.toFixed(6) : lat, + }; + } + + /** + * Crea un contenedor DOM con una tabla estructurada y traducida. + * Perfecto para inyectar en Overlays de OpenLayers. + * @param {number} lon + * @param {number} lat + * @returns {HTMLElement} Elemento contenedor de la tabla + */ + createPopupContent(lon, lat) { + const lang = this.translation || {}; + const labels = lang.popup; + + const container = document.createElement('div'); + container.className = 'm-location-popup-container'; + + container.innerHTML = ` + + + + + + + + + + + + + + + + +
${labels.titleValue || 'Mi ubicación'}
+ `; + return container; + } + /** * Obtiene la ayuda del control * @@ -139,7 +199,8 @@ class Location extends ControlBase { * @api */ getHelp() { - const textHelp = getValue('location').textHelp; + const lang = this.translation || {}; + const textHelp = lang.textHelp; return { title: Location.NAME, content: new Promise((success) => { @@ -147,9 +208,9 @@ class Location extends ControlBase { vars: { urlImages: `${IDEE.config.STATIC_RESOURCES_URL}/imagenes/controles`, translations: { - help1: textHelp.text1, - help2: textHelp.text2, - help3: textHelp.text2, + help1: textHelp.text1 ?? '', + help2: textHelp.text2 ?? '', + help3: textHelp.text2 ?? '', }, }, }); @@ -183,6 +244,15 @@ class Location extends ControlBase { setTracking(tracking) { this.getImpl().tracking = tracking; } + + /** + * @override + * Destroys facade implementacion of this control + */ + destroy() { + super.destroy(); + this.un(EventType.CHANGE); + } } /** diff --git a/api-idee-js/src/facade/js/i18n/en.json b/api-idee-js/src/facade/js/i18n/en.json index a124e63e1..7d8507f3a 100644 --- a/api-idee-js/src/facade/js/i18n/en.json +++ b/api-idee-js/src/facade/js/i18n/en.json @@ -20,6 +20,12 @@ }, "location": { "title": "Get current location", + "popup": { + "title": "Location", + "titleValue": "Current Position", + "longitude": "Longitude", + "latitude": "Latitude" + }, "textHelp": { "text1": "The location tool allows you to obtain the position of a device and position it on the map. Its operation is restricted to WEB pages that are published securely over the HTTPS protocol and in local environments.", "text2": "If used on a mobile device equipped with a GNSS system, the position will be obtained according to the device's location permissions.", diff --git a/api-idee-js/src/facade/js/i18n/es.json b/api-idee-js/src/facade/js/i18n/es.json index e40106273..1933a4c7e 100644 --- a/api-idee-js/src/facade/js/i18n/es.json +++ b/api-idee-js/src/facade/js/i18n/es.json @@ -21,6 +21,12 @@ "location": { "error": "El control ubicación no acepta http. Debe usar https.", "title": "Obtener ubicación actual", + "popup": { + "title": "Ubicación", + "titleValue": "Posición actual", + "longitude": "Longitud", + "latitude": "Latitud" + }, "textHelp": { "text1": "La herramienta de localización permite obtener la posición de un dispositivo y posicionarla en el mapa. Su funcionamiento queda restringido a páginas WEB que se publiquen de forma segura sobre el protocolo HTTPS y en entornos locales.", "text2": "En el caso de su utilización en un dispositivo móvil provisto de un sistema GNSS, se obtendrá la posición según los permisos de localización del dispositivo.", diff --git a/api-idee-js/src/impl/cesium/js/control/Location.js b/api-idee-js/src/impl/cesium/js/control/Location.js index 736923f64..6692e011e 100644 --- a/api-idee-js/src/impl/cesium/js/control/Location.js +++ b/api-idee-js/src/impl/cesium/js/control/Location.js @@ -8,6 +8,17 @@ import * as Cesium from 'cesium'; import Control from './Control'; import Feature from '../feature/Feature'; +/** + * @typedef {Object} Options Opciones de configuración del control de implementación + * @param {Boolean} [tracking] Seguimiento de la localización, por defecto verdadero. + * @param {Boolean} [highAccuracy] Alta precisión del seguimiento, por defecto falso. + * @param {Number} [maximumAge] Indica la antigüedad máxima en milisegundos de una posible + * posición almacenada en caché. + * Valor por defecto 60000. + * @param {Object} [vendorOptions] Opciones de proveedor para la biblioteca base, + * por defecto objeto vacío. Estos valores no son configurables. +*/ + /** * @classdesc * Hereda de {@link module:IDEE/impl/control/Control|Control}. @@ -25,24 +36,22 @@ import Feature from '../feature/Feature'; class Location extends Control { /** * @constructor - * @param {Boolean} tracking Seguimiento de la localización. - * @param {Boolean} highAccuracy Alta precisión del seguimiento. - * @param {Number} maximumAge Antigüedad máxima en caché. - * @param {Object} vendorOptions Opciones de proveedor para Cesium/Geolocation. + * @extends {IDEE.impl.Control} + * @param {Options} options * @example * const control = new IDEE.impl.ol.control.Location(true, false, 60000, { * enableHighAccuracy: true, * }); */ - constructor(tracking, highAccuracy, maximumAge, vendorOptions) { - super(vendorOptions); + constructor(options = {}) { + super(options.vendorOptions); /** * Opciones para la biblioteca base. * @private * @type {Object} */ - this.vendorOptions_ = vendorOptions; + this.vendorOptions_ = options.vendorOptions; this.watchId_ = null; /** @@ -50,21 +59,21 @@ class Location extends Control { * @private * @type {Boolean} */ - this.tracking_ = tracking; + this.tracking_ = options.tracking ?? true; /** * Alta precisión del seguimiento, por defecto falso. * @private * @type {Boolean} */ - this.highAccuracy_ = highAccuracy; + this.highAccuracy_ = options.highAccuracy ?? false; /** * Valor por defecto 60000. * @private * @type {Number} */ - this.maximumAge_ = maximumAge; + this.maximumAge_ = options.maximumAge ?? 6000; /** * Activa el control. @@ -101,7 +110,7 @@ class Location extends Control { outlineWidth: 1, }, }); - // Inyección de compatibilidad (Duck Typing) para la fachada Feature.js + cesiumAccuracyEntity.get = (key) => { return this[key]; }; @@ -121,8 +130,20 @@ class Location extends Control { disableDepthTestDistance: Number.POSITIVE_INFINITY, }, }); - // Inyección de compatibilidad (Duck Typing) para la fachada Feature.js + + cesiumPositionEntity.getAttributes = () => { + return this._rawProperties; + }; + + cesiumPositionEntity.getAttribute = (key) => { + return this._rawProperties[key]; + }; + cesiumPositionEntity.get = (key) => { + // eslint-disable-next-line no-prototype-builtins + if (this._rawProperties && this._rawProperties.hasOwnProperty(key)) { + return this._rawProperties[key]; + } return this[key]; }; cesiumPositionEntity.isUtilityFeature = true; @@ -150,10 +171,9 @@ class Location extends Control { const accuracy = position.coords.accuracy; const newCoord = [lon, lat]; - // Cesium trabaja con coordenadas Cartesianas 3D basándose en WGS84 (grados) const centerCartesian = Cesium.Cartesian3.fromDegrees(lon, lat); - // 1. Actualizar posición y radio de la geometría de precisión + // Actualizar anillo de precisión const accEntity = this.accuracyFeature_.getImpl().getFeature(); accEntity.position = centerCartesian; if (accEntity.ellipse) { @@ -161,11 +181,18 @@ class Location extends Control { accEntity.ellipse.semiMinorAxis = accuracy; } - // 2. Actualizar posición del punto indicador const posEntity = this.positionFeature_.getImpl().getFeature(); posEntity.position = centerCartesian; - // 3. Reposicionar el mapa global (Fachada) + if (!isNullOrEmpty(this.facadeObj_)) { + const translatedProps = this.facadeObj_.getPopupProperties(lon, lat); + + posEntity.properties = new Cesium.PropertyBag(translatedProps); + + // eslint-disable-next-line no-underscore-dangle + posEntity._rawProperties = translatedProps; + } + this.facadeMap_.setCenter(newCoord); if (this.element.classList.contains('m-locating')) { this.facadeMap_.setZoom(Location.ZOOM); @@ -174,12 +201,10 @@ class Location extends Control { this.element.classList.remove('m-locating'); this.element.classList.add('m-located'); - // Si no requiere trackear continuamente, apagamos el watcher tras la primera lectura fija if (!this.tracking_) { this.clearWatch_(); } - // 4. Notificar cambios a la fachada mediante eventos abstractos if (!isNullOrEmpty(this.facadeObj_)) { if (!setEquals(newCoord, this.lastCoord_)) { this.facadeObj_.fire(EventType.CHANGE, [newCoord]); diff --git a/api-idee-js/src/impl/ol/js/control/Attributions.js b/api-idee-js/src/impl/ol/js/control/Attributions.js index 3617f68ca..40633809f 100644 --- a/api-idee-js/src/impl/ol/js/control/Attributions.js +++ b/api-idee-js/src/impl/ol/js/control/Attributions.js @@ -27,7 +27,7 @@ class Attributions extends Control { * @api stable */ constructor(options = {}) { - super(); + super(options); /** * Map of the plugin * @private diff --git a/api-idee-js/src/impl/ol/js/control/Location.js b/api-idee-js/src/impl/ol/js/control/Location.js index 14260db24..2e3a0564b 100644 --- a/api-idee-js/src/impl/ol/js/control/Location.js +++ b/api-idee-js/src/impl/ol/js/control/Location.js @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ /** * @module IDEE/impl/control/Location */ @@ -17,6 +18,17 @@ import OLStyleStroke from 'ol/style/Stroke'; import Control from './Control'; import Feature from '../feature/Feature'; +/** + * @typedef {Object} Options Opciones de configuración del control de implementación + * @param {Boolean} [tracking] Seguimiento de la localización, por defecto verdadero. + * @param {Boolean} [highAccuracy] Alta precisión del seguimiento, por defecto falso. + * @param {Number} [maximumAge] Indica la antigüedad máxima en milisegundos de una posible + * posición almacenada en caché. + * Valor por defecto 60000. + * @param {Object} [vendorOptions] Opciones de proveedor para la biblioteca base, + * por defecto objeto vacío. Estos valores no son configurables. +*/ + /** * @classdesc * Hereda de {@link module:IDEE/impl/control/Control|Control}. @@ -41,30 +53,23 @@ class Location extends Control { * posición en el mapa. * * @constructor - * @param {Boolean} tracking Seguimiento de la localización, por defecto verdadero. - * @param {Boolean} highAccuracy Alta precisión del seguimiento, por defecto falso. - * @param {Number} maximumAge Indica la antigüedad máxima en milisegundos de una posible - * posición almacenada en caché. - * Valor por defecto 60000. - * @param {Object} vendorOptions Opciones de proveedor para la biblioteca base, - * por defecto objeto vacío. Estos valores no son configurables. + * @extends {IDEE.impl.Control} + * @param {Options} options * @example * const control = new IDEE.impl.ol.control.Location(true, false, 60000, { * enableHighAccuracy: true, * }); - * @extends {IDEE.impl.Control} * @api stable */ - - constructor(tracking, highAccuracy, maximumAge, vendorOptions) { - super(vendorOptions); + constructor(options = {}) { + super(options.vendorOptions); /** * Opciones para la biblioteca base. * @private * @type {Object} */ - this.vendorOptions_ = vendorOptions; + this.vendorOptions_ = options.vendorOptions; /** * Proporcionar Geolocalización HTML5. @@ -79,7 +84,7 @@ class Location extends Control { * @type {OLFeature} */ const olAccuracyFeature = new OLFeature(); - olAccuracyFeature.set('isUtilityFeature', true); // No interactivo + olAccuracyFeature.set('isUtilityFeature', true); this.accuracyFeature_ = Feature.feature2Facade(olAccuracyFeature); /** @@ -87,21 +92,21 @@ class Location extends Control { * @private * @type {Boolean} */ - this.tracking_ = tracking; + this.tracking_ = options.tracking ?? true; /** * Alta precisión del seguimiento, por defecto falso. * @private * @type {Boolean} */ - this.highAccuracy_ = highAccuracy; + this.highAccuracy_ = options.highAccuracy ?? false; /** * Valor por defecto 60000. * @private * @type {Number} */ - this.maximumAge_ = maximumAge; + this.maximumAge_ = options.maximumAge ?? 6000; /** * Activa el control. @@ -168,18 +173,38 @@ class Location extends Control { }, }, this.vendorOptions_, true)); this.geolocation_.on('change:accuracyGeometry', (evt) => { - const accuracyGeom = evt.target.get(evt.key); + // const accuracyGeom = evt.target.get(evt.key); + const accuracyGeom = evt.target.getAccuracyGeometry(); + if (accuracyGeom) { + const nativeMap = this.facadeMap_.getImpl().map_; + const mapProjection = nativeMap.getView().getProjection(); + accuracyGeom.transform('EPSG:4326', mapProjection); + const nativeAccuracyFeature = this.accuracyFeature_.getImpl().getFeature(); + nativeAccuracyFeature.setGeometry(accuracyGeom); + nativeAccuracyFeature.setStyle( + new OLStyle({ + fill: new OLStyleFill({ + color: 'rgba(0, 204, 255, 0.12)', + }), + stroke: new OLStyleStroke({ + color: 'rgba(0, 68, 85, 0.42)', + width: 1.5, + }), + }), + ); + } this.accuracyFeature_.getImpl().getFeature().setGeometry(accuracyGeom); }); this.geolocation_.on('change:position', (evt) => { - const newCoord = evt.target.get(evt.key); + const [lon, lat] = evt.target.get(evt.key); + const newCoord = [lon, lat]; const newPosition = isNullOrEmpty(newCoord) ? null : new OLGeomPoint(newCoord); this.positionFeature_.getImpl().getFeature().setGeometry(newPosition); this.facadeMap_.setCenter(newCoord); if (this.element.classList.contains('m-locating')) { - this.facadeMap_.setZoom(Location.ZOOM); // solo 1a vez + this.facadeMap_.setZoom(Location.ZOOM); } this.element.classList.remove('m-locating'); this.element.classList.add('m-located'); From 14b4b4adce6f6c08c08266722b96502e4c893089 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 15 Jun 2026 14:10:03 +0200 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20a=C3=B1ade=20metodos=20para=20a?= =?UTF-8?q?=C3=B1adir/eliminar=20interacciones=20y=20consulta=20de=20la=20?= =?UTF-8?q?proyecci=C3=B3n=20nativa=20de=20la=20tecnolog=C3=ADa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-idee-js/src/facade/js/Map.js | 34 +++++++++++ api-idee-js/src/facade/js/i18n/en.json | 2 + api-idee-js/src/facade/js/i18n/es.json | 2 + api-idee-js/src/impl/cesium/js/Map.js | 58 +++++++++++++++++++ api-idee-js/src/impl/ol/js/Map.js | 28 +++++++++ .../development/CP-0003-controls/CP-011.js | 37 ++++++++++++ 6 files changed, 161 insertions(+) diff --git a/api-idee-js/src/facade/js/Map.js b/api-idee-js/src/facade/js/Map.js index 41b5e2369..d50eede91 100644 --- a/api-idee-js/src/facade/js/Map.js +++ b/api-idee-js/src/facade/js/Map.js @@ -5421,6 +5421,40 @@ class Map extends Base { return this; } + /** + * Este método añade una interacción de OpenLayers al mapa. + * + * @function + * @param {ol.interaction.Interaction} interaction Interacción a añadir. + * @returns {IDEE.Map} Mapa. + * @public + * @api + */ + addInteraction(interaction) { + if (isUndefined(MapImpl.prototype.addInteraction)) { + Exception(getValue('exception').addinteraction_method); + } + this.getImpl().addInteraction(interaction); + return this; + } + + /** + * Este método elimina una interacción de OpenLayers del mapa. + * + * @function + * @param {ol.interaction.Interaction} interaction Interacción a eliminar. + * @returns {IDEE.Map} Mapa. + * @public + * @api + */ + removeInteraction(interaction) { + if (isUndefined(MapImpl.prototype.removeInteraction)) { + Exception(getValue('exception').removeinteraction_method); + } + this.getImpl().removeInteraction(interaction); + return this; + } + /** * Este método permite activar o desactivar la interacción de panneo. * El valor por defecto es true. diff --git a/api-idee-js/src/facade/js/i18n/en.json b/api-idee-js/src/facade/js/i18n/en.json index a124e63e1..0b16f3388 100644 --- a/api-idee-js/src/facade/js/i18n/en.json +++ b/api-idee-js/src/facade/js/i18n/en.json @@ -262,6 +262,8 @@ "invalidBase64Encoding": "Invalid base64 encoding", "invalidJsonFormat": "Invalid JSON format", "removecontrol_method": "The used implementation does not have the removeControls method.", + "addinteraction_method": "La implementación usada no posee el método addInteraction.", + "removeinteraction_method": "La implementación usada no posee el método removeInteraction.", "setmaxextent_method": "The used implementation does not have the setMaxExtent method.", "flyTo_method": "The used implementation does not have the flyTo method.", "getbbox_method": "The used implementation does not have the getBbox method.", diff --git a/api-idee-js/src/facade/js/i18n/es.json b/api-idee-js/src/facade/js/i18n/es.json index e40106273..cc20f52ea 100644 --- a/api-idee-js/src/facade/js/i18n/es.json +++ b/api-idee-js/src/facade/js/i18n/es.json @@ -263,6 +263,8 @@ "invalidBase64Encoding": "Codificación base64 inválida", "invalidJsonFormat": "Formato JSON inválido", "removecontrol_method": "La implementación usada no posee el método removeControls.", + "addinteraction_method": "The used implementation does not have the addInteraction method.", + "removeinteraction_method": "The used implementation does not have the removeInteraction method.", "setmaxextent_method": "La implementación usada no posee el método setMaxExtent.", "flyTo_method": "La implementación usada no posee el método flyTo.", "getbbox_method": "La implementación usada no posee el método getBbox.", diff --git a/api-idee-js/src/impl/cesium/js/Map.js b/api-idee-js/src/impl/cesium/js/Map.js index 42a77fc49..fb34120c1 100755 --- a/api-idee-js/src/impl/cesium/js/Map.js +++ b/api-idee-js/src/impl/cesium/js/Map.js @@ -140,6 +140,13 @@ class Map extends MObject { */ this.controls_ = []; + /** + * Interacciones añadidas al mapa. + * @private + * @type {Array} + */ + this.interactions_ = []; + /** * Indica si el zoom inicial fue calculado. Por defecto verdadero. * @private @@ -2460,6 +2467,18 @@ class Map extends MObject { }; } + /** + * Obtiene la proyección nativa de Cesium activa en el mapa. + * + * @function + * @returns {GeographicProjection} Instancia nativa de Cesium MapProjection. + * @public + * @api + */ + getNativeProjection() { + return this.map_.scene.mapProjection; + } + /** * Este método obtiene la implementación del mapa. * @@ -2739,6 +2758,45 @@ class Map extends MObject { }); } + /** + * Este método añade una interacción al mapa. + * + * @function + * @param {Object} interaction Interacción a añadir. + * @returns {Map} Mapa. + * @public + * @api + */ + addInteraction(interaction) { + if (!isNullOrEmpty(interaction) && !this.interactions_.includes(interaction)) { + this.interactions_.push(interaction); + if (typeof interaction.activate === 'function') { + interaction.activate(this.map_); + } + } + return this; + } + + /** + * Este método elimina una interacción del mapa. + * + * @function + * @param {Object} interaction Interacción a eliminar. + * @returns {Map} Mapa. + * @public + * @api + */ + removeInteraction(interaction) { + const idx = this.interactions_.indexOf(interaction); + if (idx !== -1) { + if (typeof interaction.deactivate === 'function') { + interaction.deactivate(); + } + this.interactions_.splice(idx, 1); + } + return this; + } + /** * Función que obtiene el nombre de la implementación del mapa. * diff --git a/api-idee-js/src/impl/ol/js/Map.js b/api-idee-js/src/impl/ol/js/Map.js index c2ddc9896..9e6659412 100644 --- a/api-idee-js/src/impl/ol/js/Map.js +++ b/api-idee-js/src/impl/ol/js/Map.js @@ -3889,6 +3889,34 @@ class Map extends MObject { this.getMapImpl().getView().setResolution(resolution); } + /** + * Este método añade una interacción de OpenLayers al mapa. + * + * @function + * @param {ol.interaction.Interaction} interaction Interacción a añadir. + * @returns {Map} Mapa. + * @public + * @api + */ + addInteraction(interaction) { + this.map_.addInteraction(interaction); + return this; + } + + /** + * Este método elimina una interacción de OpenLayers del mapa. + * + * @function + * @param {ol.interaction.Interaction} interaction Interacción a eliminar. + * @returns {Map} Mapa. + * @public + * @api + */ + removeInteraction(interaction) { + this.map_.removeInteraction(interaction); + return this; + } + /** * Función que obtiene el nombre de la implementación del mapa. * diff --git a/api-idee-js/test/development/CP-0003-controls/CP-011.js b/api-idee-js/test/development/CP-0003-controls/CP-011.js index 7f132a7c6..d4b755ba0 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-011.js +++ b/api-idee-js/test/development/CP-0003-controls/CP-011.js @@ -66,3 +66,40 @@ removeButton.addEventListener('click', () => { }); recreate(); + +// ── Test: getNativeProjection ───────────────────────────────────────────────── +const nativeProj = map.getNativeProjection(); +console.log('[getNativeProjection] tipo:', nativeProj?.constructor?.name); +console.log('[getNativeProjection] valor:', nativeProj); + +// ── Test: addInteraction ────────────────────────────────────────────────────── +const mockInteraction = { + name: 'test-interaction', + activate(mapImpl) { + console.log('[mockInteraction.activate] llamado con impl:', mapImpl); + }, + deactivate() { + console.log('[mockInteraction.deactivate] llamado'); + }, +}; + +const returnAdd = map.addInteraction(mockInteraction); +console.log('[addInteraction] retorna el propio mapa (===map):', returnAdd === map); +console.log('[addInteraction] interactions_ tras añadir:', map.getImpl().interactions_.map((i) => i.name)); + +// Añadir la misma interacción dos veces: no debe duplicarse +map.addInteraction(mockInteraction); +console.log('[addInteraction] interactions_ tras 2º add (sin duplicados):', map.getImpl().interactions_.map((i) => i.name)); + +// ── Test: removeInteraction ─────────────────────────────────────────────────── +const returnRemove = map.removeInteraction(mockInteraction); +console.log('[removeInteraction] retorna el propio mapa (===map):', returnRemove === map); +console.log('[removeInteraction] interactions_ tras eliminar:', map.getImpl().interactions_.map((i) => i.name)); + +// Eliminar una interacción inexistente: no debe lanzar error +try { + map.removeInteraction(mockInteraction); + console.log('[removeInteraction] eliminar inexistente: OK (sin error)'); +} catch (e) { + console.error('[removeInteraction] eliminar inexistente ERROR:', e.message); +} From 07fb400c2faae7f327a70edb98f4b5eacbae94d1 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 15 Jun 2026 15:51:07 +0200 Subject: [PATCH 3/9] fix: add default public style to display accuracy area --- .../src/impl/ol/js/control/Location.js | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/api-idee-js/src/impl/ol/js/control/Location.js b/api-idee-js/src/impl/ol/js/control/Location.js index 2e3a0564b..162e8cb68 100644 --- a/api-idee-js/src/impl/ol/js/control/Location.js +++ b/api-idee-js/src/impl/ol/js/control/Location.js @@ -181,17 +181,7 @@ class Location extends Control { accuracyGeom.transform('EPSG:4326', mapProjection); const nativeAccuracyFeature = this.accuracyFeature_.getImpl().getFeature(); nativeAccuracyFeature.setGeometry(accuracyGeom); - nativeAccuracyFeature.setStyle( - new OLStyle({ - fill: new OLStyleFill({ - color: 'rgba(0, 204, 255, 0.12)', - }), - stroke: new OLStyleStroke({ - color: 'rgba(0, 68, 85, 0.42)', - width: 1.5, - }), - }), - ); + nativeAccuracyFeature.setStyle(Location.ACCURACY_STYLE); } this.accuracyFeature_.getImpl().getFeature().setGeometry(accuracyGeom); }); @@ -308,6 +298,24 @@ Location.POSITION_STYLE = new OLStyle({ }), }); +/** + * Estilo del area de cercanía para la localización. + * @const + * @type {ol.style.Style} + * @public + * @api stable + * @memberof module:IDEE/impl/control/Location~ + */ +Location.ACCURACY_STYLE = new OLStyle({ + fill: new OLStyleFill({ + color: 'rgba(0, 204, 255, 0.12)', + }), + stroke: new OLStyleStroke({ + color: 'rgba(0, 68, 85, 0.42)', + width: 1.5, + }), +}); + /** * Zoom de la localización. * @const From 87618746291e9c53ac4c0cab438f24e238149b39 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 15 Jun 2026 17:56:28 +0200 Subject: [PATCH 4/9] fix: elimina console.logs del test --- .../development/CP-0003-controls/CP-011.js | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/api-idee-js/test/development/CP-0003-controls/CP-011.js b/api-idee-js/test/development/CP-0003-controls/CP-011.js index d4b755ba0..7f132a7c6 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-011.js +++ b/api-idee-js/test/development/CP-0003-controls/CP-011.js @@ -66,40 +66,3 @@ removeButton.addEventListener('click', () => { }); recreate(); - -// ── Test: getNativeProjection ───────────────────────────────────────────────── -const nativeProj = map.getNativeProjection(); -console.log('[getNativeProjection] tipo:', nativeProj?.constructor?.name); -console.log('[getNativeProjection] valor:', nativeProj); - -// ── Test: addInteraction ────────────────────────────────────────────────────── -const mockInteraction = { - name: 'test-interaction', - activate(mapImpl) { - console.log('[mockInteraction.activate] llamado con impl:', mapImpl); - }, - deactivate() { - console.log('[mockInteraction.deactivate] llamado'); - }, -}; - -const returnAdd = map.addInteraction(mockInteraction); -console.log('[addInteraction] retorna el propio mapa (===map):', returnAdd === map); -console.log('[addInteraction] interactions_ tras añadir:', map.getImpl().interactions_.map((i) => i.name)); - -// Añadir la misma interacción dos veces: no debe duplicarse -map.addInteraction(mockInteraction); -console.log('[addInteraction] interactions_ tras 2º add (sin duplicados):', map.getImpl().interactions_.map((i) => i.name)); - -// ── Test: removeInteraction ─────────────────────────────────────────────────── -const returnRemove = map.removeInteraction(mockInteraction); -console.log('[removeInteraction] retorna el propio mapa (===map):', returnRemove === map); -console.log('[removeInteraction] interactions_ tras eliminar:', map.getImpl().interactions_.map((i) => i.name)); - -// Eliminar una interacción inexistente: no debe lanzar error -try { - map.removeInteraction(mockInteraction); - console.log('[removeInteraction] eliminar inexistente: OK (sin error)'); -} catch (e) { - console.error('[removeInteraction] eliminar inexistente ERROR:', e.message); -} From b4c38f5de512cace846040106ea7b7b7d33c16f5 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 15 Jun 2026 17:59:00 +0200 Subject: [PATCH 5/9] feat: centralices registration of new proyections --- api-idee-js/src/impl/ol/js/projections.js | 22 ++++- .../src/facade/js/infocoordinatescontrol.js | 84 +++++++++---------- .../src/impl/ol/js/mousesrscontrol.js | 17 +--- .../src/facade/js/templateCustomizer.js | 70 ++++++++-------- 4 files changed, 103 insertions(+), 90 deletions(-) diff --git a/api-idee-js/src/impl/ol/js/projections.js b/api-idee-js/src/impl/ol/js/projections.js index 6c16ff9b4..e3c3f9175 100644 --- a/api-idee-js/src/impl/ol/js/projections.js +++ b/api-idee-js/src/impl/ol/js/projections.js @@ -5,7 +5,7 @@ import proj4 from 'proj4'; import OLProjection from 'ol/proj/Projection'; import { register } from 'ol/proj/proj4'; -import { addEquivalentProjections } from 'ol/proj'; +import { addEquivalentProjections, get as getProj } from 'ol/proj'; import { parseCRSWKTtoJSON } from '../../../facade/js/util/Utils'; /** @@ -709,6 +709,25 @@ const setNewProjection = async (projection) => { addProjections([newProjection]); }; +/** + * Asegura que una proyección está registrada. Si no lo está, la obtiene de epsg.io y la registra. + * @param {String} projCode Código EPSG de la proyección + * @returns {Promise} true si la proyección está disponible, false si no existe + * @public + * @function + * @api + */ +const ensureProjection = async (projCode) => { + const normalizedCode = projCode.startsWith('EPSG:') ? projCode : `EPSG:${projCode}`; + if (getProj(normalizedCode)) return true; + try { + await setNewProjection(normalizedCode); + return true; + } catch { + return false; + } +}; + // register proj4 addProjections(projections, false); @@ -724,4 +743,5 @@ export default { addProjections, getSupportedProjs, setNewProjection, + ensureProjection, }; diff --git a/api-idee-js/src/plugins/infocoordinates/src/facade/js/infocoordinatescontrol.js b/api-idee-js/src/plugins/infocoordinates/src/facade/js/infocoordinatescontrol.js index f9e1e41e0..9bef06eb7 100644 --- a/api-idee-js/src/plugins/infocoordinates/src/facade/js/infocoordinatescontrol.js +++ b/api-idee-js/src/plugins/infocoordinates/src/facade/js/infocoordinatescontrol.js @@ -156,6 +156,7 @@ export default class InfocoordinatesControl extends IDEE.Control { const openDropdown = () => { if (!isEditable) { + this.refreshProjectionsSelector(selector); selector.classList.remove('noDisplay'); arrow.classList.add('arrow-up'); } @@ -194,6 +195,9 @@ export default class InfocoordinatesControl extends IDEE.Control { }); arrow.addEventListener('click', () => { + if (selector.classList.contains('noDisplay')) { + this.refreshProjectionsSelector(selector); + } selector.classList.toggle('noDisplay'); arrow.classList.toggle('arrow-up'); }); @@ -491,6 +495,32 @@ export default class InfocoordinatesControl extends IDEE.Control { idBotonTab.classList.add('active'); } + /** + * Refresca el listado de proyecciones del selector de EPSG con las + * proyecciones actualmente soportadas, incluyendo las registradas + * al vuelo por otros plugins. + * @param {HTMLElement} selector Elemento
    del selector de EPSG + * @public + * @function + * @api + */ + refreshProjectionsSelector(selector) { + this.projections = IDEE.impl.ol.js.projections.getSupportedProjs(); + // eslint-disable-next-line no-param-reassign + selector.innerHTML = ` +
  • + ${getValue('choose_create_epsg')} +
  • + ${this.projections.map((proj) => ` +
  • + + ${proj.codes[0]} + +
  • + `).join('')} + `; + } + changeSelectSRSorChangeFormat() { // Cojo la tab activa const elem = document.querySelector('.tablinks.active'); @@ -520,38 +550,18 @@ export default class InfocoordinatesControl extends IDEE.Control { const formatGMS = document.getElementById('m-infocoordinates-buttonConversorFormat').checked; // Cambio coordenadas y calculo las UTM - let pointDataOutput; - try { - pointDataOutput = this.getImpl().getCoordinates( - featureSelected, - selectSRS, - formatGMS, - this.decimalGEOcoord_, - this.decimalUTMcoord_, - ); + if (!await IDEE.impl.ol.js.projections.ensureProjection(selectSRS)) { + IDEE.dialog.error(`${getValue('exception.srs')} ${this.selectedProjection}`); + } else { this.selectedProjection = selectSRS; - } catch (error) { - try { - await IDEE.impl.ol.js.projections.setNewProjection(selectSRS); - pointDataOutput = this.getImpl().getCoordinates( - featureSelected, - selectSRS, - formatGMS, - this.decimalGEOcoord_, - this.decimalUTMcoord_, - ); - this.selectedProjection = selectSRS; - } catch (err) { - pointDataOutput = this.getImpl().getCoordinates( - featureSelected, - this.selectedProjection, - formatGMS, - this.decimalGEOcoord_, - this.decimalUTMcoord_, - ); - IDEE.dialog.error(`${getValue('exception.srs')} ${this.selectedProjection}`); - } } + const pointDataOutput = this.getImpl().getCoordinates( + featureSelected, + this.selectedProjection, + formatGMS, + this.decimalGEOcoord_, + this.decimalUTMcoord_, + ); inputSRS.value = this.selectedProjection; this.isProjGeographic = this.getImpl().isProjGeographic(this.selectedProjection); const gmsButton = document.getElementById('m-infocoordinates-buttonConversorFormat'); @@ -565,19 +575,7 @@ export default class InfocoordinatesControl extends IDEE.Control { gmsButton.checked = false; gmsButton.setAttribute('disabled', 'disabled'); } - this.projections = IDEE.impl.ol.js.projections.getSupportedProjs(); - selector.innerHTML = ` -
  • - ${getValue('choose_create_epsg')} -
  • - ${this.projections.map((proj) => ` -
  • - - ${proj.codes[0]} - -
  • - `).join('')} - `; + this.refreshProjectionsSelector(selector); // pinto pointBox.innerHTML = pointDataOutput.NumPoint; diff --git a/api-idee-js/src/plugins/mousesrs/src/impl/ol/js/mousesrscontrol.js b/api-idee-js/src/plugins/mousesrs/src/impl/ol/js/mousesrscontrol.js index 1bf9adace..9c104ce53 100644 --- a/api-idee-js/src/plugins/mousesrs/src/impl/ol/js/mousesrscontrol.js +++ b/api-idee-js/src/plugins/mousesrs/src/impl/ol/js/mousesrscontrol.js @@ -303,24 +303,15 @@ export default class MouseSRSControl extends IDEE.impl.Control { */ async getDecimalUnits() { let decimalDigits; - let srsUnits; - try { - // eslint-disable-next-line no-underscore-dangle - srsUnits = ol.proj.get(this.srs).units_; - } catch (e) { - try { - await IDEE.impl.ol.js.projections.setNewProjection(this.srs); - const newProj = ol.proj.get(this.srs); - // eslint-disable-next-line no-underscore-dangle - srsUnits = newProj.units_; - } catch (err) { + if (!ol.proj.get(this.srs)) { + if (!await IDEE.impl.ol.js.projections.ensureProjection(this.srs)) { this.srs = 'EPSG:4326'; this.label = this.formatEPSG(this.srs); IDEE.dialog.error(`${getValue('exception.srs')} ${this.srs}`); - // eslint-disable-next-line no-underscore-dangle - srsUnits = ol.proj.get('EPSG:4326').units_; } } + // eslint-disable-next-line no-underscore-dangle + const srsUnits = ol.proj.get(this.srs).units_; if (srsUnits === 'd' && this.geoDecimalDigits !== undefined) { decimalDigits = this.geoDecimalDigits; diff --git a/api-idee-js/src/plugins/printviewmanagement/src/facade/js/templateCustomizer.js b/api-idee-js/src/plugins/printviewmanagement/src/facade/js/templateCustomizer.js index 80daf14d1..4786fde49 100644 --- a/api-idee-js/src/plugins/printviewmanagement/src/facade/js/templateCustomizer.js +++ b/api-idee-js/src/plugins/printviewmanagement/src/facade/js/templateCustomizer.js @@ -791,6 +791,32 @@ export default class TemplateCustomizer extends IDEE.Control { }); } + /** + * Refresca el listado de proyecciones del selector de EPSG con las + * proyecciones actualmente soportadas, incluyendo las registradas + * al vuelo por otros plugins. + * @param {HTMLElement} selectorEl Elemento
      del selector de EPSG + * @public + * @function + * @api + */ + refreshProjectionsSelector(selectorEl) { + this.projectionsOptions_ = IDEE.impl.ol.js.projections.getSupportedProjs(); + // eslint-disable-next-line no-param-reassign + selectorEl.innerHTML = ` +
    • + ${getValue('choose_create_epsg')} +
    • + ${this.projectionsOptions_.map((proj) => ` +
    • + + ${proj.codes[0]} + +
    • + `).join('')} + `; + } + /** * Configura el control de entrada de SRS * @param {String} inputElementId ID del elemento de entrada de SRS @@ -806,6 +832,7 @@ export default class TemplateCustomizer extends IDEE.Control { inputElement.addEventListener('focus', () => { if (!isEditable) { + this.refreshProjectionsSelector(selectorElement); selectorElement.style.display = 'block'; const list = selectorElement.querySelectorAll('li a'); list.forEach((li) => { @@ -861,42 +888,19 @@ export default class TemplateCustomizer extends IDEE.Control { const selectorEl = document.querySelector(ID_TEMPLATE_SRS_SELECTOR); if (currentProjection !== this.projection) { const currentCenter = previewView.getCenter(); - let transformedCenter; - try { - transformedCenter = this.getImpl().transformCoordinates( - currentCenter, - currentProjection, - this.projection, - ); - } catch (error) { - try { - await IDEE.impl.ol.js.projections.setNewProjection(epsg); - transformedCenter = this.getImpl().transformCoordinates( - currentCenter, - currentProjection, - this.projection, - ); - } catch (err) { - this.projection = previousProjection; - IDEE.dialog.error(`${getValue('exception.srs')} ${this.projection}`); - return; - } + if (!await IDEE.impl.ol.js.projections.ensureProjection(epsg)) { + this.projection = previousProjection; + IDEE.dialog.error(`${getValue('exception.srs')} ${this.projection}`); + return; } + const transformedCenter = this.getImpl().transformCoordinates( + currentCenter, + currentProjection, + this.projection, + ); inputElement.value = this.projection; - this.projectionsOptions_ = IDEE.impl.ol.js.projections.getSupportedProjs(); - selectorEl.innerHTML = ` -
    • - ${getValue('choose_create_epsg')} -
    • - ${this.projectionsOptions_.map((proj) => ` -
    • - - ${proj.codes[0]} - -
    • - `).join('')} - `; + this.refreshProjectionsSelector(selectorEl); const newView = this.getImpl().createView({ projection: this.projection, From 1a623a41302cbfa6e1ed0e169ef582a04b058550 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 16 Jun 2026 17:03:00 +0200 Subject: [PATCH 6/9] feat: It is verified that the bug of the generated panel residue comes from an asynchronicity error generated because the map does not finish generating completely --- api-idee-js/src/facade/js/ui/panels/Panel.js | 5 ++--- .../test/development/CP-0003-controls/CP-011.js | 12 +++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/api-idee-js/src/facade/js/ui/panels/Panel.js b/api-idee-js/src/facade/js/ui/panels/Panel.js index 1b859a6ca..aec93595d 100644 --- a/api-idee-js/src/facade/js/ui/panels/Panel.js +++ b/api-idee-js/src/facade/js/ui/panels/Panel.js @@ -334,14 +334,13 @@ class Panel extends MObject { /** * Este método elimina los controles del panel. * - ⚠️ Advertencia: Este método no debe ser llamado por el usuario. - * @public + * @private * @function * @param {array} controls Control. * @api */ _removeControl(controlsParam) { - const controls = this.map.controls(controlsParam); - controls.forEach((control) => { + this.map.controls(controlsParam).forEach((control) => { const index = this.controls.indexOf(control); if (index !== -1) { this.controls.splice(index, 1); diff --git a/api-idee-js/test/development/CP-0003-controls/CP-011.js b/api-idee-js/test/development/CP-0003-controls/CP-011.js index 7f132a7c6..ec8c8b365 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-011.js +++ b/api-idee-js/test/development/CP-0003-controls/CP-011.js @@ -2,12 +2,10 @@ import { map as Mmap } from 'IDEE/api-idee'; import ImplementationSwitcher from 'IDEE/control/ImplementationSwitcher'; -// IDEE.config('PROXY_URL', 'https://mapea4-sigc.juntadeandalucia.es/mapea/api/proxy'); - const map = Mmap({ container: 'map', projection: 'EPSG:4326', - controls: ['location'], + controls: ['location', 'implementationswitcher'], zoom: 8, center: [ -7.68, @@ -31,7 +29,9 @@ const create = (options) => { const remove = () => { const ctrls = map.getControls(ImplementationSwitcher.NAME); - if (ctrls.length === 1) map.removeControls(ctrls[0]); + ctrls.forEach((ctrl) => { + map.removeControls(ctrl); + }); }; const recreate = () => { @@ -65,4 +65,6 @@ removeButton.addEventListener('click', () => { remove(); }); -recreate(); +map.once('load', () => { + recreate(); +}); From 937cb3cc171b7df4fc44578af597bd331c13021c Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 16 Jun 2026 18:04:26 +0200 Subject: [PATCH 7/9] feat: enables download and load of features with text content --- api-idee-js/src/facade/js/util/LoadFiles.js | 26 +++++++ .../src/facade/js/downloadcontrol.js | 71 +++++++++++++------ .../plugins/vectorsmanagement/test/test.js | 2 +- 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/api-idee-js/src/facade/js/util/LoadFiles.js b/api-idee-js/src/facade/js/util/LoadFiles.js index 248147631..f8ad5b35d 100644 --- a/api-idee-js/src/facade/js/util/LoadFiles.js +++ b/api-idee-js/src/facade/js/util/LoadFiles.js @@ -28,6 +28,32 @@ export const loadFeaturesLoadFilesImpl = (map, layerName, features) => { const layer = new Vector({ name: layerName, legend: layerName, extract: true }); layer.addFeatures(features); map.addLayers(layer); + features.forEach((feature) => { + let labelText = feature.getAttribute('lbl_txt'); + let labelFont = feature.getAttribute('lbl_font'); + let labelColor = feature.getAttribute('lbl_clr'); + if (!labelText) { + const desc = feature.getAttribute('desc'); + if (desc && desc.includes('lbl_txt=')) { + desc.split('\n').forEach((pair) => { + const sep = pair.indexOf('='); + if (sep > 0) { + const key = pair.substring(0, sep); + const val = pair.substring(sep + 1); + if (key === 'lbl_txt') labelText = val; + else if (key === 'lbl_font') labelFont = val; + else if (key === 'lbl_clr') labelColor = val; + } + }); + } + } + if (labelText) { + feature.setStyle(new IDEE.style.Point({ + radius: 0, + label: { text: labelText, font: labelFont, color: labelColor }, + })); + } + }); LoadFilesImpl.centerFeatures(features, map); } }; diff --git a/api-idee-js/src/plugins/vectorsmanagement/src/facade/js/downloadcontrol.js b/api-idee-js/src/plugins/vectorsmanagement/src/facade/js/downloadcontrol.js index 29f63dbd5..891bbd04b 100644 --- a/api-idee-js/src/plugins/vectorsmanagement/src/facade/js/downloadcontrol.js +++ b/api-idee-js/src/plugins/vectorsmanagement/src/facade/js/downloadcontrol.js @@ -97,12 +97,24 @@ export default class DownloadControl extends IDEE.Control { * @api */ downloadLayer() { - const noTextLayer = this.newNoTextLayer(); - if (noTextLayer.getFeatures().length > 0) { - noTextLayer.setVisible(false); - this.map_.addLayers(noTextLayer); - const geojsonLayer = noTextLayer.toGeoJSON(); - this.map_.removeLayers(noTextLayer); + const featuresGeoJson = []; + const textFeatures = []; + this.layer_.getFeatures().forEach((feature) => { + const style = feature.getStyle(); + if (!style || style.get('label') === undefined) { + featuresGeoJson.push(feature.getGeoJSON()); + } else { + textFeatures.push(feature); + } + }); + + if (featuresGeoJson.length > 0 || textFeatures.length > 0) { + const nonTextConverted = IDEE.impl.utils.geojsonTo4326( + featuresGeoJson, + this.map_.getProjection().code, + ); + const textConverted = this.getTextFeaturesGeoJSON(textFeatures); + const geojsonLayer = { type: 'FeatureCollection', features: [...nonTextConverted, ...textConverted] }; this.download(geojsonLayer); } else { IDEE.dialog.info(getValue('exception.emptylayer')); @@ -119,19 +131,48 @@ export default class DownloadControl extends IDEE.Control { const features = this.managementControl_.getSelectedFeatures(); if (features.length > 0) { const featuresGeoJson = []; + const textFeatures = []; features.forEach((f) => { const style = f.getStyle(); if (!style || style.get('label') === undefined) { featuresGeoJson.push(f.getGeoJSON()); + } else { + textFeatures.push(f); } }); - const geojsonLayer = { type: 'FeatureCollection', features: IDEE.impl.utils.geojsonTo4326(featuresGeoJson, this.map_.getProjection().code) }; + const nonTextConverted = IDEE.impl.utils.geojsonTo4326( + featuresGeoJson, + this.map_.getProjection().code, + ); + const textConverted = this.getTextFeaturesGeoJSON(textFeatures); + const geojsonLayer = { type: 'FeatureCollection', features: [...nonTextConverted, ...textConverted] }; this.download(geojsonLayer); } else { IDEE.dialog.info(getValue('exception.featuresel')); } } + /** + * Converts text features to GeoJSON Point features with label properties. + * @public + * @function + * @api + * @param {Array} features + * @returns {Array} + */ + getTextFeaturesGeoJSON(features) { + const textFeaturesGeoJson = features.map((feature) => { + const featureGeoJson = feature.getGeoJSON(); + const style = feature.getStyle(); + featureGeoJson.properties = featureGeoJson.properties || {}; + featureGeoJson.properties.lbl_txt = style.get('label.text') || ''; + featureGeoJson.properties.lbl_font = style.get('label.font') || ''; + featureGeoJson.properties.lbl_clr = style.get('label.color') || ''; + return featureGeoJson; + }); + return IDEE.impl.utils.geojsonTo4326(textFeaturesGeoJson, this.map_.getProjection().code); + } + /** * Create geojson, kml or shp file download link * @public @@ -193,22 +234,6 @@ export default class DownloadControl extends IDEE.Control { this.managementControl_.deactive(document, 'm-vectorsmanagement-download'); } - /** - * Creates vector layer copy of selectedLayer excluding text features. - * @public - * @function - * @api - * @returns {IDEE.layer.Vector} - */ - newNoTextLayer() { - const newLayer = new IDEE.layer.Vector({ name: 'copia' }); - const noTextFeatures = this.layer_.getFeatures().filter((feature) => { - return feature.getStyle() === null || feature.getStyle().get('label') === undefined; - }); - newLayer.addFeatures(noTextFeatures, false, true); - return newLayer; - } - /** * Parses geojson before shp download. * Changes geometry type to simple when necessary and removes one pair of brackets. diff --git a/api-idee-js/src/plugins/vectorsmanagement/test/test.js b/api-idee-js/src/plugins/vectorsmanagement/test/test.js index 8796b679b..ea0748221 100644 --- a/api-idee-js/src/plugins/vectorsmanagement/test/test.js +++ b/api-idee-js/src/plugins/vectorsmanagement/test/test.js @@ -81,7 +81,7 @@ const mp = new VectorsManagement({ // Herramientas help: true, addlayer: true, - selection: false, // Automaticamente desactiva "edition" y "analysis" + selection: true, // Si es false automaticamente desactiva "edition" y "analysis" creation: true, edition: true, style: true, From 5eb2c2b66f94e960f23e5e0ae8155aafead16b7d Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Wed, 17 Jun 2026 13:41:18 +0200 Subject: [PATCH 8/9] feat: rebuild of rotate cotrol as 1.1 version --- .../src/facade/assets/css/controls/rotate.css | 26 ++++----- api-idee-js/src/facade/assets/css/fonts.css | 11 +++- .../development/CP-0003-controls/CP-008.html | 54 ++++++++++++++++--- .../development/CP-0003-controls/CP-008.js | 10 ++-- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/api-idee-js/src/facade/assets/css/controls/rotate.css b/api-idee-js/src/facade/assets/css/controls/rotate.css index 1a531bf3e..fe8893cc6 100644 --- a/api-idee-js/src/facade/assets/css/controls/rotate.css +++ b/api-idee-js/src/facade/assets/css/controls/rotate.css @@ -10,7 +10,7 @@ background-color: var(--idee-color-links) !important; } -.m-areas > div.m-area > div.m-panel.opened.m-rotate { +.m-areas>div.m-area>div.m-panel.opened.m-rotate { background-color: transparent !important; box-shadow: none !important; border: none !important; @@ -77,14 +77,18 @@ div.m-control.m-rotate-container { .m-rotate-exterior-svg { position: absolute; - width: 120px; - height: 120px; + width: 105px; overflow: hidden; } +.m-rotate-exterior-svg.g-cartografia-exterior:before { + margin-bottom: 5px; +} + +.m-rotate-exterior-svg.g-cartografia-exterior:before, .m-rotate-exterior-svg.g-cartografia-exterior-1:before { color: #fff; - font-size: 100px; + width: inherit; } .m-rotate-giroscopio-svg { @@ -92,12 +96,15 @@ div.m-control.m-rotate-container { width: 40px; height: 40px; box-sizing: content-box; + background-color: var(--idee-color-white); + border-radius: 40px; } .m-rotate-giroscopio-svg.g-cartografia-giroscopio:before { - font-size: 40px; - background-color: white; - border-radius: 40px; + margin: 0; + width: 26px; + margin: calc((40px - 26px) / 2); + border-radius: inherit; color: var(--idee-color-primary); } @@ -209,11 +216,6 @@ button.m-rotate-help-close.g-cartografia-cancelar:before { color: #000; } -.m-rotate-exterior-svg.g-cartografia-exterior:before { - color: #fff; - font-size: 120px; -} - .m-rotate-help-option { display: flex; gap: 8px; diff --git a/api-idee-js/src/facade/assets/css/fonts.css b/api-idee-js/src/facade/assets/css/fonts.css index 19e7d4149..bf974424a 100644 --- a/api-idee-js/src/facade/assets/css/fonts.css +++ b/api-idee-js/src/facade/assets/css/fonts.css @@ -337,5 +337,14 @@ -webkit-mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/pinterest.svg'); mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/pinterest.svg'); } - + + .g-cartografia-exterior::before { + -webkit-mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/rotate-exterior.svg'); + mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/rotate-exterior.svg'); + } + + .g-cartografia-giroscopio::before { + -webkit-mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/rotate-giroscopio.svg'); + mask-image: url('https://componentes.idee.es/estaticos/Simbologia/svg/icons_cota/rotate-giroscopio.svg'); + } } \ No newline at end of file diff --git a/api-idee-js/test/development/CP-0003-controls/CP-008.html b/api-idee-js/test/development/CP-0003-controls/CP-008.html index 462fa479c..6dabbe663 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-008.html +++ b/api-idee-js/test/development/CP-0003-controls/CP-008.html @@ -8,19 +8,57 @@ PRUEBA DESARROLLO - - +
      +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      +
      + +
      +
      diff --git a/api-idee-js/test/development/CP-0003-controls/CP-008.js b/api-idee-js/test/development/CP-0003-controls/CP-008.js index a9c2d4943..690dc8ba8 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-008.js +++ b/api-idee-js/test/development/CP-0003-controls/CP-008.js @@ -1,15 +1,17 @@ import { map as Mmap } from 'IDEE/api-idee'; -import Plugin from 'IDEE/Plugin'; // import Control from 'IDEE/control/Control'; import Rotate from 'IDEE/control/Rotate'; import * as Position from 'IDEE/ui/position'; const map = Mmap({ container: 'map', - projection: 'EPSG:3857', controls: ['rotate'], - center: [-443273.10081370454, 4757481.749296248], - zoom: 6, + projection: 'EPSG:4326', + zoom: 8, + center: [ + -7.68, + 43.084999999999994, + ], }); const rotate = new Rotate({ From 71c6c841af44d1f4d620e658d10d528d4d0bba0f Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Wed, 17 Jun 2026 17:37:10 +0200 Subject: [PATCH 9/9] feat: Implementation of colors with tokens, size reduction for better layout --- .../src/facade/assets/css/controls/rotate.css | 62 ++++++++----------- api-idee-js/src/templates/rotateCesium.html | 2 +- .../development/CP-0003-controls/CP-008.html | 5 +- api-idee-rest/src/main/webapp/rotate.jsp | 2 +- 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/api-idee-js/src/facade/assets/css/controls/rotate.css b/api-idee-js/src/facade/assets/css/controls/rotate.css index fe8893cc6..c52d24b75 100644 --- a/api-idee-js/src/facade/assets/css/controls/rotate.css +++ b/api-idee-js/src/facade/assets/css/controls/rotate.css @@ -10,16 +10,10 @@ background-color: var(--idee-color-links) !important; } -.m-areas>div.m-area>div.m-panel.opened.m-rotate { - background-color: transparent !important; - box-shadow: none !important; - border: none !important; -} - .m-rotate-compass { position: relative; - width: 150px; - height: 150px; + width: 90px; + height: 90px; display: flex; justify-content: center; align-items: center; @@ -51,15 +45,6 @@ div.m-control.m-rotate-container { opacity: 1; } -.m-rotate-compass { - position: relative; - width: 150px; - height: 150px; - display: flex; - justify-content: center; - align-items: center; -} - .m-rotate-background-exterior { fill: var(--idee-color-primary); } @@ -67,22 +52,24 @@ div.m-control.m-rotate-container { .m-rotate-background-exterior, .m-rotate-svgPath-svg { position: absolute; - width: 99px; - height: 99px; + /* width: 99px; + height: 99px; */ + width: 80px; + height: 84px; } .m-rotate-svgPath-svg { - width: 100px; + width: 85px; } .m-rotate-exterior-svg { position: absolute; - width: 105px; + width: 85px; overflow: hidden; } .m-rotate-exterior-svg.g-cartografia-exterior:before { - margin-bottom: 5px; + margin-bottom: 4px; } .m-rotate-exterior-svg.g-cartografia-exterior:before, @@ -93,24 +80,24 @@ div.m-control.m-rotate-container { .m-rotate-giroscopio-svg { position: absolute; - width: 40px; - height: 40px; + width: 26px; + height: 26px; box-sizing: content-box; background-color: var(--idee-color-white); - border-radius: 40px; + border-radius: 26px; } .m-rotate-giroscopio-svg.g-cartografia-giroscopio:before { margin: 0; - width: 26px; - margin: calc((40px - 26px) / 2); + width: 16px; + margin: calc((26px - 16px) / 2); border-radius: inherit; color: var(--idee-color-primary); } .m-rotate-help-button { position: absolute; - top: 50%; + top: 12px; right: 0; transform: translateY(-50%); background-color: var(--idee-color-primary); @@ -118,13 +105,13 @@ div.m-control.m-rotate-container { font-weight: bold; border: 2px solid white; cursor: pointer; - width: 30px; - height: 30px; - border-top-right-radius: 8px; - border-bottom-right-radius: 8px; + height: 19px; + width: 19px; + border-radius: 50%; text-align: center; font-family: 'Muli', 'sans-serif'; - font-size: 18px; + font-size: 10px; + z-index: 1; } #m-rotate-slider-container { @@ -155,15 +142,16 @@ div.m-control.m-rotate-container { } .m-rotate-help-button:hover { - background-color: var(--idee-color-primary); + background-color: var(--idee-color-links); } .m-rotate-rotation-maker { position: absolute; - width: 99px; - height: 99px; - fill: var(--idee-color-primary); + width: 88px; + height: 80px; + fill: var(--idee-color-links); display: none; + overflow: hidden; } .m-rotate-help-container { diff --git a/api-idee-js/src/templates/rotateCesium.html b/api-idee-js/src/templates/rotateCesium.html index 139f311b7..18510e4b9 100644 --- a/api-idee-js/src/templates/rotateCesium.html +++ b/api-idee-js/src/templates/rotateCesium.html @@ -15,7 +15,7 @@ - + PRUEBA DESARROLLO diff --git a/api-idee-rest/src/main/webapp/rotate.jsp b/api-idee-rest/src/main/webapp/rotate.jsp index 4da48afca..8e8c27983 100644 --- a/api-idee-rest/src/main/webapp/rotate.jsp +++ b/api-idee-rest/src/main/webapp/rotate.jsp @@ -87,7 +87,7 @@ maxZoom: 20, minZoom: 4, center: [-467062.8225, 4683459.6216], - controls: ['location'] + controls: ['implementationswitcher'] }); const layerinicial = new IDEE.layer.WMS({