From 86abfa892804d73fc181eedd7d533a1d0bfc64ec Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Fri, 5 Jun 2026 08:16:08 +0200 Subject: [PATCH 01/10] feat: Restore part of funcionallity to implementation seitcher control --- .../facade/assets/css/overview_map_button.css | 6 +- api-idee-js/src/facade/js/Map.js | 39 ++++++ .../js/control/ImplementationSwitcher.js | 105 +++++++++++----- api-idee-js/src/facade/js/i18n/en.json | 1 + api-idee-js/src/facade/js/i18n/es.json | 1 + api-idee-js/src/impl/cesium/js/Map.js | 118 ++++++++++++------ api-idee-js/src/impl/ol/js/Map.js | 76 ++++++----- api-idee-js/test/configuration_filtered.js | 84 +++++++++---- .../development/CP-0003-controls/CP-011.html | 2 +- .../development/CP-0003-controls/CP-011.js | 14 ++- .../development/CP-0003-controls/README.md | 2 +- 11 files changed, 317 insertions(+), 131 deletions(-) diff --git a/api-idee-js/src/facade/assets/css/overview_map_button.css b/api-idee-js/src/facade/assets/css/overview_map_button.css index a03d7820f..d8aca43f1 100644 --- a/api-idee-js/src/facade/assets/css/overview_map_button.css +++ b/api-idee-js/src/facade/assets/css/overview_map_button.css @@ -8,7 +8,7 @@ aspect-ratio: 1; color: var(--idee-color-white); background-color: var(--idee-color-primary); - border: 2px solid var(--idee-color-white); + border: 2px solid var(--button-border-color); border-radius: 50%; display: flex; align-items: center; @@ -20,7 +20,7 @@ .m-api-idee-container .m-overviewmap-button:hover, .m-api-idee-container .m-control-button:hover { - border: 2px solid var(--idee-color-white) !important; + border: 2px solid var(--button-border-color) !important; background-color: var(--idee-color-links); transition: all 0.3s ease; } @@ -32,7 +32,7 @@ .m-api-idee-container .m-overviewmap-button.active, .m-api-idee-container .m-control-button.active { - border: 2px solid var(--idee-color-white); + border: 2px solid var(--button-border-color); color: var(--idee-color-white); background-color: var(--idee-color-primary-dark); transition: all 0.3s ease; diff --git a/api-idee-js/src/facade/js/Map.js b/api-idee-js/src/facade/js/Map.js index 34d18f29d..f9ae19891 100644 --- a/api-idee-js/src/facade/js/Map.js +++ b/api-idee-js/src/facade/js/Map.js @@ -3323,6 +3323,32 @@ class Map extends Base { return this; } + /** + * Hace un vuelo a una extensión dada con opciones de animación. + * + * @public + * @function + * @param {String|Array|Array|Mx.Extent} extent Extensión [minX, minY, maxX, maxY]. + * @param {Object} vendorOpts Opciones de proveedores. + * @returns {Map} + * @api + */ + flyTo(extent, vendorOpts = {}) { + if (isUndefined(MapImpl.prototype.flyTo)) { + Exception(getValue('exception').flyTo_method); + } + + try { + // parses the parameter + const maxExtent = parameter.maxExtent(extent); + this.getImpl().flyTo(maxExtent, vendorOpts); + } catch (err) { + Dialog.error(getValue('exception').flyTo_method); + throw err; + } + return this; + } + /** * Este método proporciona el zoom actual de esta * instancia del mapa. @@ -4681,6 +4707,8 @@ class Map extends Base { this.toolPanelsContainer.style.setProperty('--up-height', `${newHeight}px`); } }); + + this.mapFrameContainer_ = container; } /** @@ -4865,6 +4893,17 @@ class Map extends Base { this.toolPanelsContainer.style.setProperty('--up-height', `${this.openUpPanelHeight}px`); } + /** + * Este método devuelve el contenedor del marco del mapa. + * + * @function + * @api + * @returns {Object} Devuelve el contenedor. + */ + getFrameContainer() { + return this.mapFrameContainer_; + } + /** * Este método proporciona el contenedor. * diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 90f83bef1..eb456e4f9 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -55,7 +55,7 @@ class ImplementationSwitcher extends ControlBase { */ constructor(options = {}) { if (isUndefined(ImplementationSwitcherImpl) || (isObject(ImplementationSwitcherImpl) - && isNullOrEmpty(Object.keys(ImplementationSwitcherImpl)))) { + && isNullOrEmpty(Object.keys(ImplementationSwitcherImpl)))) { Exception(getValue('exception').implementationswitcher_method); } @@ -129,20 +129,39 @@ class ImplementationSwitcher extends ControlBase { * @api */ loadImplementation(implementation) { - const API_IDEE_URL = IDEE.config.API_IDEE_URL; + const API_IDEE_URL = IDEE.config.API_IDEE_URL || ''; + const baseUrl = API_IDEE_URL.endsWith('/') ? API_IDEE_URL : `${API_IDEE_URL}/`; + const resolveUrl = (value) => { + if (!value) return ''; + if (/^(?:https?:)?\/\//i.test(value)) { + return value; + } + try { + return new URL(value.replace(/^\/+/, ''), baseUrl || document.baseURI).href; + } catch (error) { + return `${baseUrl}${value.replace(/^\/+/, '')}`; + } + }; + + const implementationUrl = resolveUrl(implementation.js); + // const implementationCssUrl = resolveUrl(implementation.css); + // detect existing configuration script (dev server emits /config.js) + const existingConfigScript = document.querySelector('script[src$="/config.js"], script[src$="config.js"]'); + const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); + window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign delete impl.selected; const scripts = Array.from(document.querySelectorAll('script')) - .filter((script) => script.src === `${API_IDEE_URL}${impl.js}`); + .filter((script) => script.src === resolveUrl(impl.js)); if (scripts.length > 0) { scripts[0].remove(); } const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"]')) - .filter((style) => style.href === `${API_IDEE_URL}${impl.css}`); + .filter((style) => style.href === resolveUrl(impl.css)); if (styles.length > 0) { styles[0].remove(); @@ -153,22 +172,24 @@ class ImplementationSwitcher extends ControlBase { implementation.selected = true; const configurations = Array.from(document.querySelectorAll('script')) - .filter((configuration) => configuration.src === `${API_IDEE_URL}js/configuration.js`); + .filter((configuration) => configuration.src === configurationUrl); const script = document.createElement('script'); script.type = 'text/javascript'; - script.src = `${API_IDEE_URL}${implementation.js}`; + script.src = implementationUrl; script.onload = () => { if (configurations.length > 0) { - fetch(`${API_IDEE_URL}js/configuration.js`) - .then((response) => response.text()) - .then((scriptContent) => { - // eslint-disable-next-line no-eval - const scriptFn = eval(scriptContent); - scriptFn.call(window); - - this.loadMap(implementation); - }); + const configScript = document.createElement('script'); + configScript.type = 'text/javascript'; + configScript.src = configurationUrl; + configScript.onload = () => { + this.loadMap(implementation); + }; + configScript.onerror = (error) => { + console.error('CONFIGURATION LOAD ERROR', configurationUrl, error); + this.loadMap(implementation); + }; + document.body.appendChild(configScript); } else { this.loadMap(implementation); } @@ -182,29 +203,55 @@ class ImplementationSwitcher extends ControlBase { const style = document.createElement('link'); style.type = 'text/css'; - style.href = `${API_IDEE_URL}${implementation.css}`; + style.href = resolveUrl(implementation.css); style.rel = 'stylesheet'; document.head.appendChild(style); } loadMap(implementation) { - const div = this.map.getContainer().id === '' - ? this.map.getContainer().parentElement.parentElement : this.map.getContainer(); - div.innerHTML = ''; + /** @type {HTMLDivElement} */ + const mapFrameContainer = this.map.getFrameContainer(); + const rootContainer = mapFrameContainer || this.map.getContainer(); - const center = [this.map.getCenter().x, this.map.getCenter().y]; + const zoom = this.map.getZoom(); + const projection = implementation.epsg; const sourceProjection = this.map.getProjection().code; - const destProjection = implementation.epsg; + + const currentCenter = [this.map.getCenter().x, this.map.getCenter().y]; + const center = (typeof ol !== 'undefined' && ol !== null) + ? ol.proj.transform(currentCenter, sourceProjection, projection) + : transform(currentCenter, sourceProjection, projection); + + const controls = Array.from(this.map.getControls()).map( + (control) => control.name ?? control.NAME, + ); + const plugins = this.map.getPlugins(); + const layers = this.map.getLayers(); + + // try { + // if (this.map && typeof this.map.destroy === 'function') { + // this.map.destroy(); + // } + // } catch (e) { + // // eslint-disable-next-line no-console + // console.warn('Error destroying previous map', e); + // } + + if (!rootContainer.id) { + rootContainer.id = `map-replace-${Date.now()}`; + } + rootContainer.innerHTML = ''; + + this.map.removeControls(this); IDEE.map({ - container: div.id, - zoom: this.map.getZoom(), - center: (typeof ol !== 'undefined' && ol !== null) - ? ol.proj.transform(center, sourceProjection, destProjection) - : transform(center, sourceProjection, destProjection), - controls: Array.from(this.map.getControls()).map((control) => control.name), - plugins: this.map.getPlugins(), - layers: this.map.getLayers(), + container: rootContainer.id, + zoom, + projection, + center, + controls, + plugins, + layers, }); } diff --git a/api-idee-js/src/facade/js/i18n/en.json b/api-idee-js/src/facade/js/i18n/en.json index 4622e6ca5..a124e63e1 100644 --- a/api-idee-js/src/facade/js/i18n/en.json +++ b/api-idee-js/src/facade/js/i18n/en.json @@ -263,6 +263,7 @@ "invalidJsonFormat": "Invalid JSON format", "removecontrol_method": "The used implementation does not have the removeControls method.", "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.", "no_bbox": "You have not specified any bbox.", "setbbox_method": "The used implementation does not have the setBbox method.", diff --git a/api-idee-js/src/facade/js/i18n/es.json b/api-idee-js/src/facade/js/i18n/es.json index ef6678b0e..e40106273 100644 --- a/api-idee-js/src/facade/js/i18n/es.json +++ b/api-idee-js/src/facade/js/i18n/es.json @@ -264,6 +264,7 @@ "invalidJsonFormat": "Formato JSON inválido", "removecontrol_method": "La implementación usada no posee el método removeControls.", "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.", "no_bbox": "No ha especificado ningún bbox.", "setbbox_method": "La implementación usada no posee el método setBbox.", diff --git a/api-idee-js/src/impl/cesium/js/Map.js b/api-idee-js/src/impl/cesium/js/Map.js index c4cac28b3..5a22f8397 100755 --- a/api-idee-js/src/impl/cesium/js/Map.js +++ b/api-idee-js/src/impl/cesium/js/Map.js @@ -1867,8 +1867,9 @@ class Map extends MObject { const response = await getRemote(getCapabilitiesUrl); const getCapabilitiesDocument = response.xml; const parser = (type === 'WMS') ? new FormatWMS() : new CesiumFormatWMTSCapabilities(); - const parsedCapabilities = (type === 'WMS') ? await - parser.customRead(getCapabilitiesDocument) : await parser.read(getCapabilitiesDocument); + const parsedCapabilities = (type === 'WMS') + ? await parser.customRead(getCapabilitiesDocument) + : await parser.read(getCapabilitiesDocument); if (type === 'WMS') { const getCapabilitiesUtils = await new GetCapabilities( @@ -1888,23 +1889,58 @@ class Map extends MObject { }); }); /* eslint-disable no-empty */ - } catch (err) {} + } catch (err) { } this.getCapabilitiesPromise = parsedCapabilities; } return this.getCapabilitiesPromise; } /** - * Este método establece el encuadre de visualización del mapa. + * Este método obtiene el encuadre de visualización del mapa. * * @function - * @param {Mx.Extent} bbox Nuevo encuadre de visualización del mapa. - * @param {Object} vendorOpts Opciones para la biblioteca base. - * @returns {Map} Mapa. + * @returns {Mx.Extent} Encuadre de visualización del mapa. * @public * @api */ - setBbox(bbox, vendorOpts) { + getBbox() { + let bbox = null; + + const cesiumMap = this.getMapImpl(); + // Obtiene el bounding box de la vista actual + bbox = cesiumMap.camera.computeViewRectangle(); + + // EPSG:4979 + // Convertir el bbox de radianes a grados + const west = CesiumMath.toDegrees(bbox.west); + const south = CesiumMath.toDegrees(bbox.south); + const east = CesiumMath.toDegrees(bbox.east); + const north = CesiumMath.toDegrees(bbox.north); + + bbox = { + x: { + min: west, + max: east, + }, + y: { + min: south, + max: north, + }, + }; + + return bbox; + } + + /** + * Construye una extensión mínima para el encuadre de visualización del mapa. + * - ⚠️ Advertencia: Este método no debe ser llamado por el usuario. + * @function + * @param {Mx.Extent} bbox + * @returns {Map} El objeto de extensión mínimo a partir de un objeto bbox dado. + * @private + * @api + */ + buildRectangle(bbox) { // checks if the param is null or empty if (isNullOrEmpty(bbox)) { Exception(getValue('exception').no_bbox); @@ -1920,55 +1956,59 @@ class Map extends MObject { extent = [bbox.x.min, bbox.y.min, bbox.x.max, bbox.y.max]; } - const cesiumMap = this.getMapImpl(); // chaeck if bbox is out range if (extent[0] < -180 || extent[1] < -90 || extent[2] > 180 || extent[3] > 90) { extent = [-180, -90, 180, 90]; } - const options = { - destination: new Rectangle.fromDegrees(extent[0], extent[1], extent[2], extent[3]), - ...vendorOpts, - }; - cesiumMap.camera.setView(options); - - return this; + return new Rectangle.fromDegrees(extent[0], extent[1], extent[2], extent[3]); } /** - * Este método obtiene el encuadre de visualización del mapa. + * Este método establece el encuadre de visualización del mapa. * * @function - * @returns {Mx.Extent} Encuadre de visualización del mapa. + * @param {Mx.Extent} bbox Nuevo encuadre de visualización del mapa. + * @param {Object} vendorOpts Opciones para la biblioteca base. + * @returns {Map} Mapa. * @public * @api */ - getBbox() { - let bbox = null; + setBbox(bbox, vendorOpts) { + // checks if the param is null or empty + const rectangle = this.buildRectangle(bbox); + const options = { + destination: rectangle, + ...vendorOpts, + }; const cesiumMap = this.getMapImpl(); - // Obtiene el bounding box de la vista actual - bbox = cesiumMap.camera.computeViewRectangle(); + cesiumMap.camera.setView(options); - // EPSG:4979 - // Convertir el bbox de radianes a grados - const west = CesiumMath.toDegrees(bbox.west); - const south = CesiumMath.toDegrees(bbox.south); - const east = CesiumMath.toDegrees(bbox.east); - const north = CesiumMath.toDegrees(bbox.north); + return this; + } - bbox = { - x: { - min: west, - max: east, - }, - y: { - min: south, - max: north, - }, - }; + /** + * Centra el mapa en una extensión con animación de cámara. + * El extent debe estar en grados (lon/lat) dado que Cesium + * trabaja siempre en EPSG:4979. + * + * @function + * @param {Mx.Extent} extent [minLon, minLat, maxLon, maxLat] en grados. + * @param {Object} vendorOpts Opciones para la biblioteca base. + * @returns {Map} + * @public + * @api + */ + fitToExtent(extent, vendorOpts = {}) { + const rectangle = this.buildRectangle(extent); - return bbox; + this.getMapImpl().camera.flyTo({ + destination: rectangle, + ...vendorOpts, + }); + + return this; } /** diff --git a/api-idee-js/src/impl/ol/js/Map.js b/api-idee-js/src/impl/ol/js/Map.js index 13c8b02bc..70e3881e7 100644 --- a/api-idee-js/src/impl/ol/js/Map.js +++ b/api-idee-js/src/impl/ol/js/Map.js @@ -1423,7 +1423,7 @@ class Map extends MObject { if (filterLayer instanceof FacadeGeoTIFF) { layerMatched = (filterLayer === geotiffLayer); } else { - // type + // type if (!isNullOrEmpty(filterLayer.type)) { layerMatched = (layerMatched && (filterLayer.type === geotiffLayer.type)); } @@ -2783,6 +2783,38 @@ class Map extends MObject { return this.getCapabilitiesPromise; } + /** + * Este método obtiene el encuadre de visualización del mapa. + * + * @function + * @returns {Mx.Extent} Encuadre de visualización del mapa. + * @public + * @api + */ + getBbox() { + let bbox = null; + + const olMap = this.getMapImpl(); + const view = olMap.getView(); + if (!isNullOrEmpty(view.getCenter())) { + const olExtent = view.calculateExtent(olMap.getSize()); + + if (!isNullOrEmpty(olExtent)) { + bbox = { + x: { + min: olExtent[0], + max: olExtent[2], + }, + y: { + min: olExtent[1], + max: olExtent[3], + }, + }; + } + } + return bbox; + } + /** * Este método establece el encuadre de visualización del mapa. * @@ -2815,35 +2847,19 @@ class Map extends MObject { } /** - * Este método obtiene el encuadre de visualización del mapa. - * - * @function - * @returns {Mx.Extent} Encuadre de visualización del mapa. - * @public - * @api - */ - getBbox() { - let bbox = null; - - const olMap = this.getMapImpl(); - const view = olMap.getView(); - if (!isNullOrEmpty(view.getCenter())) { - const olExtent = view.calculateExtent(olMap.getSize()); - - if (!isNullOrEmpty(olExtent)) { - bbox = { - x: { - min: olExtent[0], - max: olExtent[2], - }, - y: { - min: olExtent[1], - max: olExtent[3], - }, - }; - } - } - return bbox; + * Hace un vuelo a una extensión dada con opciones de animación. + * Las opciones se pasan directamente a ol/View.fit(), por lo que admite cualquier + * + * @function + * @param {Mx.Extent} extent Nuevo encuadre de visualización del mapa. + * @param {Object} vendorOpts Opciones para la biblioteca base. + * @returns {Map} + * @public + * @api + */ + flyTo(extent, vendorOpts = {}) { + this.setBbox(extent, vendorOpts); + return this; } /** diff --git a/api-idee-js/test/configuration_filtered.js b/api-idee-js/test/configuration_filtered.js index c48b0f723..129e6498e 100644 --- a/api-idee-js/test/configuration_filtered.js +++ b/api-idee-js/test/configuration_filtered.js @@ -9,37 +9,59 @@ const backgroundlayersOpts = backgroundlayersIds.map((id, index) => { }; }); -const implementationSwitcherOpts = [ +const { protocol, host } = window.location; +let PROTOCOL_BASE = 'https:'; +const IDEE_PATH = 'api-idee'; +let HOST_BASE = 'api-ideedes.grupotecopy.es'; +// const isLocalhost = host !== IDEE_PATH; +const isLocalhost = false; +let API_IDEE_URL = ''; + +if (isLocalhost) { + HOST_BASE = host; + PROTOCOL_BASE = protocol; + API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/`; +} else { + API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/${IDEE_PATH}/`; +} + +let implementationSwitcherOpts = [ { id: 'OL', type: 'ol', title: 'Open Layers', - js: '../../../api-idee/js/apiidee.ol.min.js', - css: '../../../api-idee/assets/css/apiidee.ol.min.css', + js: `${API_IDEE_URL}js/apiidee.ol.min.js`, + css: `${API_IDEE_URL}assets/css/apiidee.ol.min.css`, }, { id: 'CS', type: 'cesium', title: 'Cesium', - js: '../../../api-idee/js/apiidee.cesium.min.js', - css: '../../../api-idee/assets/css/apiidee.cesium.min.css', + js: `${API_IDEE_URL}js/apiidee.cesium.min.js`, + css: `${API_IDEE_URL}assets/css/apiidee.cesium.min.css`, }, - // { - // id: 'OL', - // type: 'ol', - // title: 'Open Layers', - // js: 'js/apiidee.ol.min.js', - // css: 'assets/css/apiidee.ol.min.css', - // }, - // { - // id: 'CS', - // type: 'cesium', - // title: 'Cesium', - // js: 'js/apiidee.cesium.min.js', - // css: 'assets/css/apiidee.cesium.min.css', - // }, ]; +if (isLocalhost) { + const localPath = 'dist/'; + implementationSwitcherOpts = [ + { + id: 'OL', + type: 'ol', + title: 'Open Layers', + js: `${localPath}js/apiidee.ol.min.js`, + css: `${localPath}assets/css/apiidee.ol.min.css`, + }, + { + id: 'CS', + type: 'cesium', + title: 'Cesium', + js: `${localPath}js/apiidee.cesium.min.js`, + css: `${localPath}assets/css/apiidee.cesium.min.css`, + }, + ]; +} + const config = (configKey, configValue) => { config[configKey] = configValue; }; @@ -67,7 +89,7 @@ function fun(IDEE_) { * @public * @api stable */ - IDEE_.config('API_IDEE_URL', 'https://api-ideedes.grupotecopy.es/api-idee/'); + IDEE_.config('API_IDEE_URL', `${API_IDEE_URL}`); /** * The path to the API-IDEE proxy to send @@ -77,7 +99,7 @@ function fun(IDEE_) { * @public * @api stable */ - IDEE_.config('PROXY_URL', `${location.protocol}//api-ideedes.grupotecopy.es/api-idee/api/proxy`); + IDEE_.config('PROXY_URL', `${API_IDEE_URL}api/proxy`); /** * The path to the API-IDEE proxy to send @@ -87,7 +109,7 @@ function fun(IDEE_) { * @public * @api stable */ - IDEE_.config('PROXY_POST_URL', `${location.protocol}//api-ideedes.grupotecopy.es/api-idee/proxyPost`); + IDEE_.config('PROXY_POST_URL', `${API_IDEE_URL}proxyPost`); /** * The static resources URL @@ -105,7 +127,15 @@ function fun(IDEE_) { * @public * @api stable */ - IDEE_.config('THEME_URL', `${location.protocol}//api-ideedes.grupotecopy.es/api-idee/assets/`); + IDEE_.config('THEME_URL', `${API_IDEE_URL}assets/`); + + /** + * Cesium path to static resources + * + * @private + * @type {string} + */ + IDEE_.config('CESIUM_BASE_URL', `${API_IDEE_URL}cesium/`); /** * Predefined WMC files. It is composed of URL, @@ -122,7 +152,7 @@ function fun(IDEE_) { * @public * @api stable */ - 'urls': 'https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_callejero.xml,https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_hibrido.xml,https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_satelite.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextCallejeroCache.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextCallejero.xml,https://componentes.idee.es/estaticos/Datos/WMC/callejero2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/ortofoto2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/hibrido2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextOrtofoto.xml'.split(',').map(e => e), + 'urls': 'https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_callejero.xml,https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_hibrido.xml,https://componentes.idee.es/estaticos/Datos/WMC/context_cdau_satelite.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextCallejeroCache.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextCallejero.xml,https://componentes.idee.es/estaticos/Datos/WMC/callejero2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/ortofoto2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/hibrido2011cache.xml,https://componentes.idee.es/estaticos/Datos/WMC/contextOrtofoto.xml'.split(',').map((e) => e), /** * WMC predefined names @@ -140,7 +170,7 @@ function fun(IDEE_) { * @public * @api stable */ - 'names': 'Callejero,Hibrido,Satelite,mapa callejero cache,mapa del callejero,Callejero,Ortofoto,Híbrido,mapa ortofoto'.split(',') + 'names': 'Callejero,Hibrido,Satelite,mapa callejero cache,mapa del callejero,Callejero,Ortofoto,Híbrido,mapa ortofoto'.split(','), }); /** @@ -280,7 +310,7 @@ function fun(IDEE_) { /** * MAP Viewer - DPI (Dots per inch) - * + * * @private * @type {Number} */ @@ -288,7 +318,7 @@ function fun(IDEE_) { /** * MAP Viewer - DPI OGC (Dots per inch for OGC services) - * + * * @private * @type {Number} */ diff --git a/api-idee-js/test/development/CP-0003-controls/CP-011.html b/api-idee-js/test/development/CP-0003-controls/CP-011.html index eea4d701d..58fbbd277 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-011.html +++ b/api-idee-js/test/development/CP-0003-controls/CP-011.html @@ -55,7 +55,7 @@
- + \ No newline at end of file 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 c10b1d523..0f65437d2 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,14 +2,26 @@ 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:3857', - controls: ['location'], + // controls: ['rotate'], center: [-443273.10081370454, 4757481.749296248], zoom: 6, }); +map.flyTo([ + -1060000, + 5150000, + -650000, + 5450000, +], { + duration: 500, + padding: [20, 20, 20, 20], +}); + const selectPosition = document.getElementById('selectPosicion'); const inputTooltip = document.getElementById('inputTooltip'); const inputOrder = document.getElementById('inputOrder'); diff --git a/api-idee-js/test/development/CP-0003-controls/README.md b/api-idee-js/test/development/CP-0003-controls/README.md index ddd5ee052..6ffcd85f9 100644 --- a/api-idee-js/test/development/CP-0003-controls/README.md +++ b/api-idee-js/test/development/CP-0003-controls/README.md @@ -32,7 +32,7 @@ CP-011 Control Implementation Switcher CP-012 -Control rotate +Control Rotate & Implementation Switcher CP-013 Control parametro de ordenación (Plugins + Controles) From 6feb2f5c449edb364ef065b1ee620b40d9af5a5f Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 8 Jun 2026 11:34:35 +0200 Subject: [PATCH 02/10] fix: tests of flyTo method on 3d view --- .../js/control/ImplementationSwitcher.js | 55 ++++++++++--------- api-idee-js/src/impl/cesium/js/Map.js | 8 +-- api-idee-js/src/impl/ol/js/Map.js | 1 - api-idee-js/test/configuration_filtered.js | 50 +++++++---------- .../development/CP-0003-controls/CP-011.html | 4 +- .../development/CP-0003-controls/CP-011.js | 20 +++---- 6 files changed, 62 insertions(+), 76 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index eb456e4f9..70ef6e444 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -144,8 +144,8 @@ class ImplementationSwitcher extends ControlBase { }; const implementationUrl = resolveUrl(implementation.js); - // const implementationCssUrl = resolveUrl(implementation.css); - // detect existing configuration script (dev server emits /config.js) + const implementationCssUrl = resolveUrl(implementation.css); + const existingConfigScript = document.querySelector('script[src$="/config.js"], script[src$="config.js"]'); const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); @@ -153,32 +153,31 @@ class ImplementationSwitcher extends ControlBase { // eslint-disable-next-line no-param-reassign delete impl.selected; - const scripts = Array.from(document.querySelectorAll('script')) - .filter((script) => script.src === resolveUrl(impl.js)); - - if (scripts.length > 0) { - scripts[0].remove(); - } - - const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"]')) - .filter((style) => style.href === resolveUrl(impl.css)); + Array.from(document.querySelectorAll('script')) + .filter((script) => script.src.endsWith(impl.js)) + .forEach((script) => script.remove()); - if (styles.length > 0) { - styles[0].remove(); - } + Array.from(document.querySelectorAll('link[rel="stylesheet"]')) + .filter((style) => style.href.endsWith(impl.css)) + .forEach((style) => style.remove()); }); + // remove duplicate configuration scripts, keep only the first one + if (configurationUrl) { + const configurationScripts = Array.from(document.querySelectorAll('script')) + .filter((configuration) => configuration.src === configurationUrl); + configurationScripts.slice(1).forEach((configuration) => configuration.remove()); + } + // eslint-disable-next-line no-param-reassign implementation.selected = true; - const configurations = Array.from(document.querySelectorAll('script')) - .filter((configuration) => configuration.src === configurationUrl); - const script = document.createElement('script'); script.type = 'text/javascript'; script.src = implementationUrl; script.onload = () => { - if (configurations.length > 0) { + const existingConfig = configurationUrl && document.querySelector(`script[src="${configurationUrl}"]`); + if (configurationUrl && !existingConfig) { const configScript = document.createElement('script'); configScript.type = 'text/javascript'; configScript.src = configurationUrl; @@ -201,11 +200,13 @@ class ImplementationSwitcher extends ControlBase { }; document.body.appendChild(script); - const style = document.createElement('link'); - style.type = 'text/css'; - style.href = resolveUrl(implementation.css); - style.rel = 'stylesheet'; - document.head.appendChild(style); + if (implementationCssUrl) { + const style = document.createElement('link'); + style.type = 'text/css'; + style.href = implementationCssUrl; + style.rel = 'stylesheet'; + document.head.appendChild(style); + } } loadMap(implementation) { @@ -237,10 +238,10 @@ class ImplementationSwitcher extends ControlBase { // console.warn('Error destroying previous map', e); // } - if (!rootContainer.id) { - rootContainer.id = `map-replace-${Date.now()}`; - } - rootContainer.innerHTML = ''; + // if (!rootContainer.id) { + // rootContainer.id = `map-replace-${Date.now()}`; + // } + // rootContainer.innerHTML = ''; this.map.removeControls(this); diff --git a/api-idee-js/src/impl/cesium/js/Map.js b/api-idee-js/src/impl/cesium/js/Map.js index 5a22f8397..b8f3e9c61 100755 --- a/api-idee-js/src/impl/cesium/js/Map.js +++ b/api-idee-js/src/impl/cesium/js/Map.js @@ -1778,7 +1778,7 @@ class Map extends MObject { */ addControls(controls) { controls.forEach((control) => { - if (!includes(this.controls_, control) && control) { + if (!includes(this.controls_, control)) { this.controls_.push(control); } }); @@ -1936,7 +1936,7 @@ class Map extends MObject { * - ⚠️ Advertencia: Este método no debe ser llamado por el usuario. * @function * @param {Mx.Extent} bbox - * @returns {Map} El objeto de extensión mínimo a partir de un objeto bbox dado. + * @returns {Mx.Extent} El objeto de extensión mínimo a partir de un objeto bbox dado. * @private * @api */ @@ -1989,7 +1989,7 @@ class Map extends MObject { } /** - * Centra el mapa en una extensión con animación de cámara. + * Hace un vuelo a una extensión con animación de cámara. * El extent debe estar en grados (lon/lat) dado que Cesium * trabaja siempre en EPSG:4979. * @@ -2000,7 +2000,7 @@ class Map extends MObject { * @public * @api */ - fitToExtent(extent, vendorOpts = {}) { + flyTo(extent, vendorOpts = {}) { const rectangle = this.buildRectangle(extent); this.getMapImpl().camera.flyTo({ diff --git a/api-idee-js/src/impl/ol/js/Map.js b/api-idee-js/src/impl/ol/js/Map.js index 70e3881e7..c2ddc9896 100644 --- a/api-idee-js/src/impl/ol/js/Map.js +++ b/api-idee-js/src/impl/ol/js/Map.js @@ -2848,7 +2848,6 @@ class Map extends MObject { /** * Hace un vuelo a una extensión dada con opciones de animación. - * Las opciones se pasan directamente a ol/View.fit(), por lo que admite cualquier * * @function * @param {Mx.Extent} extent Nuevo encuadre de visualización del mapa. diff --git a/api-idee-js/test/configuration_filtered.js b/api-idee-js/test/configuration_filtered.js index 129e6498e..c0a042071 100644 --- a/api-idee-js/test/configuration_filtered.js +++ b/api-idee-js/test/configuration_filtered.js @@ -17,32 +17,13 @@ let HOST_BASE = 'api-ideedes.grupotecopy.es'; const isLocalhost = false; let API_IDEE_URL = ''; +let implementationSwitcherOpts = []; + if (isLocalhost) { HOST_BASE = host; PROTOCOL_BASE = protocol; API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/`; -} else { - API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/${IDEE_PATH}/`; -} -let implementationSwitcherOpts = [ - { - id: 'OL', - type: 'ol', - title: 'Open Layers', - js: `${API_IDEE_URL}js/apiidee.ol.min.js`, - css: `${API_IDEE_URL}assets/css/apiidee.ol.min.css`, - }, - { - id: 'CS', - type: 'cesium', - title: 'Cesium', - js: `${API_IDEE_URL}js/apiidee.cesium.min.js`, - css: `${API_IDEE_URL}assets/css/apiidee.cesium.min.css`, - }, -]; - -if (isLocalhost) { const localPath = 'dist/'; implementationSwitcherOpts = [ { @@ -60,6 +41,25 @@ if (isLocalhost) { css: `${localPath}assets/css/apiidee.cesium.min.css`, }, ]; +} else { + API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/${IDEE_PATH}/`; + + implementationSwitcherOpts = [ + { + id: 'OL', + type: 'ol', + title: 'Open Layers', + js: `${API_IDEE_URL}js/apiidee.ol.min.js`, + css: `${API_IDEE_URL}assets/css/apiidee.ol.min.css`, + }, + { + id: 'CS', + type: 'cesium', + title: 'Cesium', + js: `${API_IDEE_URL}js/apiidee.cesium.min.js`, + css: `${API_IDEE_URL}assets/css/apiidee.cesium.min.css`, + }, + ]; } const config = (configKey, configValue) => { @@ -129,14 +129,6 @@ function fun(IDEE_) { */ IDEE_.config('THEME_URL', `${API_IDEE_URL}assets/`); - /** - * Cesium path to static resources - * - * @private - * @type {string} - */ - IDEE_.config('CESIUM_BASE_URL', `${API_IDEE_URL}cesium/`); - /** * Predefined WMC files. It is composed of URL, * predefined name and context name. diff --git a/api-idee-js/test/development/CP-0003-controls/CP-011.html b/api-idee-js/test/development/CP-0003-controls/CP-011.html index 58fbbd277..ceee1e4f8 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-011.html +++ b/api-idee-js/test/development/CP-0003-controls/CP-011.html @@ -48,8 +48,8 @@ -
- +
+
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 0f65437d2..1f0b9481e 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 @@ -6,20 +6,14 @@ IDEE.config('PROXY_URL', 'https://mapea4-sigc.juntadeandalucia.es/mapea/api/prox const map = Mmap({ container: 'map', - projection: 'EPSG:3857', + projection: 'EPSG:4326', // controls: ['rotate'], - center: [-443273.10081370454, 4757481.749296248], zoom: 6, -}); - -map.flyTo([ - -1060000, - 5150000, - -650000, - 5450000, -], { - duration: 500, - padding: [20, 20, 20, 20], + layers: ['OSM'], + center: [ + -7.68, + 43.084999999999994, + ], }); const selectPosition = document.getElementById('selectPosicion'); @@ -30,7 +24,7 @@ const selectCollapsed = document.getElementById('selectCollapsed'); const create = (options) => { if (!map.hasControl(ImplementationSwitcher.NAME)) { - map.addControls(new ImplementationSwitcher(options)); + // map.addControls(new ImplementationSwitcher(options)); } }; From acfa212ac21028b87742918ebd76781fbb94291e Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 8 Jun 2026 13:04:35 +0200 Subject: [PATCH 03/10] fix: destroy method of cesium control contains errors --- .../src/impl/cesium/js/control/Control.js | 45 +++++++++++++------ api-idee-js/src/impl/ol/js/control/Control.js | 28 ++++++------ .../development/CP-0003-controls/CP-011.js | 4 +- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/api-idee-js/src/impl/cesium/js/control/Control.js b/api-idee-js/src/impl/cesium/js/control/Control.js index 454744dff..899f5a3e9 100755 --- a/api-idee-js/src/impl/cesium/js/control/Control.js +++ b/api-idee-js/src/impl/cesium/js/control/Control.js @@ -36,28 +36,32 @@ class Control { */ addTo(map, element) { this.facadeMap_ = map; - this.element = element; - // eslint-disable-next-line no-underscore-dangle - map.getMapImpl()._element.prepend(this.element); + this.setElement(element); } /** - * Este método destruye este control, limpiando el HTML - * y anulando el registro de todos los eventos. + * Devuelve la vista de implementación. + * + * @public + * @function + * @return {HTMLElement} vista de implementación + * @api stable + */ + getView() { + return this.panel ?? this.element; + } + + /** + * Este método establece los elementos a usar. * * @public * @function + * @param {HTMLElement} element Elemento HTML del control. * @api stable * @export */ - destroy() { - if (this.element.parentElement - // eslint-disable-next-line no-underscore-dangle - && this.element.parentElement === this.facadeMap_.getMapImpl()._element) { - // eslint-disable-next-line no-underscore-dangle - this.facadeMap_.getMapImpl()._element.removeChild(this.element); - } - this.facadeMap_ = null; + setElement(element) { + this.element = element; } /** @@ -72,6 +76,21 @@ class Control { getElement() { return this.element; } + + /** + * Este método destruye este control, limpiando el HTML + * y anulando el registro de todos los eventos. + * + * @public + * @function + * @api stable + * @export + */ + destroy() { + if (this.element) this.element.remove(); + this.setElement(null); + this.facadeMap_ = null; + } } export default Control; diff --git a/api-idee-js/src/impl/ol/js/control/Control.js b/api-idee-js/src/impl/ol/js/control/Control.js index b88478730..72bdef093 100644 --- a/api-idee-js/src/impl/ol/js/control/Control.js +++ b/api-idee-js/src/impl/ol/js/control/Control.js @@ -87,44 +87,44 @@ class Control extends OLControl { } /** - * Este método destruye este control, limpiando el HTML - * y anulando el registro de todos los eventos. + * Este método establece los elementos a usar. * * @public * @function + * @@param {HTMLElement} element * @api stable * @export */ - destroy() { - this.facadeMap_.getMapImpl().removeControl(this); - this.facadeMap_ = null; - this.setElement(null); + setElement(element) { + this.element = element; } /** - * Este método establece los elementos a usar. + * Este método retorna los elementos. * * @public * @function - * @@param {HTMLElement} element + * @returns {HTMLElement} Elementos. * @api stable * @export */ - setElement(element) { - this.element = element; + getElement() { + return this.element; } /** - * Este método retorna los elementos. + * Este método destruye este control, limpiando el HTML + * y anulando el registro de todos los eventos. * * @public * @function - * @returns {HTMLElement} Elementos. * @api stable * @export */ - getElement() { - return this.element; + destroy() { + this.facadeMap_.getMapImpl().removeControl(this); + this.facadeMap_ = null; + this.setElement(null); } } 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 1f0b9481e..28650be9f 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 @@ -8,7 +8,7 @@ const map = Mmap({ container: 'map', projection: 'EPSG:4326', // controls: ['rotate'], - zoom: 6, + zoom: 10, layers: ['OSM'], center: [ -7.68, @@ -24,7 +24,7 @@ const selectCollapsed = document.getElementById('selectCollapsed'); const create = (options) => { if (!map.hasControl(ImplementationSwitcher.NAME)) { - // map.addControls(new ImplementationSwitcher(options)); + map.addControls(new ImplementationSwitcher(options)); } }; From d62b43dc1a9dbc4c19e0fb69e2ed93294b0debc9 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 8 Jun 2026 14:04:48 +0200 Subject: [PATCH 04/10] feat: preselect current map implementation on first load --- .../js/control/ImplementationSwitcher.js | 63 +++++++++++++------ .../development/CP-0003-controls/CP-011.js | 2 +- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 70ef6e444..6cc21210c 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -64,16 +64,10 @@ class ImplementationSwitcher extends ControlBase { super(ImplementationSwitcher.NAME, implementationSwitcherImpl, options); if (!window.implementations) { - window.implementations = IDEE.config.implementationswitcher; - - if (window.implementations?.length > 0) { - window.implementations[0].selected = true; - - window.implementations = window.implementations.map((impl) => ({ - ...impl, - epsg: impl.type === 'cesium' ? 'EPSG:4979' : impl.epsg, - })); - } + window.implementations = (IDEE.config.implementationswitcher || []).map((impl) => ({ + ...impl, + epsg: impl.type === 'cesium' ? 'EPSG:4979' : impl.epsg, + })); } this.collapsible = isBoolean(options.collapsible) ? options.collapsible : true; @@ -90,8 +84,10 @@ class ImplementationSwitcher extends ControlBase { * @returns {Promise} HTML generado, promesa. * @api */ - createView() { + createView(map) { return new Promise((resolve) => { + this.selectCurrentImplementation(map); + this.html = compileTemplate(template, { vars: { title: this.tooltip ?? getValue(ImplementationSwitcher.NAME).title, @@ -106,6 +102,35 @@ class ImplementationSwitcher extends ControlBase { }); } + /** + * Selecciona en el desplegable la implementación del mapa actual. + * + * @public + * @function + * @param {IDEE.Map} map Mapa actual. + * @api + */ + selectCurrentImplementation(map) { + const currentImplementation = map?.getImplementation?.(); + const implementations = window.implementations || []; + + if (implementations.length > 0) { + const currentIndex = implementations.findIndex((impl) => impl.type === currentImplementation); + const selectedIndex = implementations.findIndex((impl) => impl.selected); + const targetIndex = currentIndex >= 0 ? currentIndex : Math.max(selectedIndex, 0); + + window.implementations = implementations.map((impl, index) => { + const nextImpl = { ...impl }; + if (index === targetIndex) { + nextImpl.selected = true; + } else { + delete nextImpl.selected; + } + return nextImpl; + }); + } + } + /** * Esta función agrega el detector de eventos en el desplegable de implementaciones. * @param {HTMLElement} html Elemento desplegable. @@ -229,14 +254,14 @@ class ImplementationSwitcher extends ControlBase { const plugins = this.map.getPlugins(); const layers = this.map.getLayers(); - // try { - // if (this.map && typeof this.map.destroy === 'function') { - // this.map.destroy(); - // } - // } catch (e) { - // // eslint-disable-next-line no-console - // console.warn('Error destroying previous map', e); - // } + try { + if (this.map && typeof this.map.destroy === 'function') { + this.map.destroy(); + } + } catch (e) { + // eslint-disable-next-line no-console + console.warn('Error destroying previous map', e); + } // if (!rootContainer.id) { // rootContainer.id = `map-replace-${Date.now()}`; 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 28650be9f..58a151d5c 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 @@ -9,7 +9,7 @@ const map = Mmap({ projection: 'EPSG:4326', // controls: ['rotate'], zoom: 10, - layers: ['OSM'], + // layers: ['OSM'], center: [ -7.68, 43.084999999999994, From 97550a7351e6a38969b209edf883b6d412dbe2a2 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Mon, 8 Jun 2026 17:44:24 +0200 Subject: [PATCH 05/10] feat: updates destroy method uncompleted --- api-idee-js/src/facade/js/Map.js | 18 ++++++++++++++++- .../js/control/ImplementationSwitcher.js | 20 +++++++++---------- .../development/CP-0003-controls/CP-011.js | 2 +- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/api-idee-js/src/facade/js/Map.js b/api-idee-js/src/facade/js/Map.js index f9ae19891..972d186d5 100644 --- a/api-idee-js/src/facade/js/Map.js +++ b/api-idee-js/src/facade/js/Map.js @@ -4249,6 +4249,17 @@ class Map extends Base { }); } + /** + * Este método elimina todas las capas del mapa. + * + * @public + * @function + * @api + */ + removeAllLayers() { + this.removeLayers(this.getLayers()); + } + /** * Este método destruye el mapa, limpiando el HTML * y anular el registro de todos los eventos. @@ -4263,9 +4274,14 @@ class Map extends Base { if (isUndefined(MapImpl.prototype.destroy)) { Exception(getValue('exception').destroy_method); } - + this.removeAllLayers(); this.getImpl().destroy(); + const mapFrameContainer = this.getFrameContainer(); + if (mapFrameContainer.children.length > 0) { + Array.from(mapFrameContainer.children).forEach((child) => child.remove()); + } + return this; } diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 6cc21210c..6c603ff01 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -172,7 +172,12 @@ class ImplementationSwitcher extends ControlBase { const implementationCssUrl = resolveUrl(implementation.css); const existingConfigScript = document.querySelector('script[src$="/config.js"], script[src$="config.js"]'); - const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); + + if (existingConfigScript) { + existingConfigScript.remove(); + } + + const configurationUrl = resolveUrl('js/configuration.js'); window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign @@ -254,22 +259,15 @@ class ImplementationSwitcher extends ControlBase { const plugins = this.map.getPlugins(); const layers = this.map.getLayers(); + this.map.removeControls(this); + try { - if (this.map && typeof this.map.destroy === 'function') { - this.map.destroy(); - } + this.map.destroy(); } catch (e) { // eslint-disable-next-line no-console console.warn('Error destroying previous map', e); } - // if (!rootContainer.id) { - // rootContainer.id = `map-replace-${Date.now()}`; - // } - // rootContainer.innerHTML = ''; - - this.map.removeControls(this); - IDEE.map({ container: rootContainer.id, zoom, 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 58a151d5c..00a72f72b 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,7 +2,7 @@ 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'); +// IDEE.config('PROXY_URL', 'https://mapea4-sigc.juntadeandalucia.es/mapea/api/proxy'); const map = Mmap({ container: 'map', From 8c7b78ef5dc75e393d6e5cf479681cb1dbad42e7 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 9 Jun 2026 09:28:50 +0200 Subject: [PATCH 06/10] fix: fix cesium origin --- api-idee-js/src/facade/js/control/ImplementationSwitcher.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 6c603ff01..a7f4af30d 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -170,14 +170,13 @@ class ImplementationSwitcher extends ControlBase { const implementationUrl = resolveUrl(implementation.js); const implementationCssUrl = resolveUrl(implementation.css); - const existingConfigScript = document.querySelector('script[src$="/config.js"], script[src$="config.js"]'); if (existingConfigScript) { existingConfigScript.remove(); } - const configurationUrl = resolveUrl('js/configuration.js'); + const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign From e5d1f363ec59c121610c8541dfbda98dbcf6cbe0 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 9 Jun 2026 11:23:03 +0200 Subject: [PATCH 07/10] fix: restore implementation switcher src path --- api-idee-js/src/facade/js/control/ImplementationSwitcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index a7f4af30d..5b0c3d9dc 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -172,12 +172,12 @@ class ImplementationSwitcher extends ControlBase { const implementationCssUrl = resolveUrl(implementation.css); const existingConfigScript = document.querySelector('script[src$="/config.js"], script[src$="config.js"]'); + const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); + if (existingConfigScript) { existingConfigScript.remove(); } - const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); - window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign delete impl.selected; From 9c1ab06e323457fb0743fb619abd80b54ec86ee4 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 9 Jun 2026 12:28:25 +0200 Subject: [PATCH 08/10] feat: try to dont remove configuration file to restore the path --- api-idee-js/src/facade/js/control/ImplementationSwitcher.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 5b0c3d9dc..21b02b016 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -174,9 +174,9 @@ class ImplementationSwitcher extends ControlBase { const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); - if (existingConfigScript) { - existingConfigScript.remove(); - } + // if (existingConfigScript) { + // existingConfigScript.remove(); + // } window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign From 2ea87e8f8152594f267aabf5e3b476430110bf62 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 9 Jun 2026 16:56:12 +0200 Subject: [PATCH 09/10] fix: an error ocurred on destroy one side panel nd all side active side panels are collapsed this panel need destroy the button correctly and also deactivate the panel --- api-idee-js/src/facade/js/Map.js | 5 +- .../src/facade/js/ui/panels/SidePanel.js | 6 ++- .../development/CP-0003-controls/CP-013.html | 26 ++++++++++ .../development/CP-0003-controls/CP-013.js | 52 +++++++++++++++---- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/api-idee-js/src/facade/js/Map.js b/api-idee-js/src/facade/js/Map.js index 972d186d5..36c38ea0c 100644 --- a/api-idee-js/src/facade/js/Map.js +++ b/api-idee-js/src/facade/js/Map.js @@ -4061,8 +4061,9 @@ class Map extends Base { plugin.destroy(); this.plugins = this.plugins.filter((plugin2) => plugin2.name !== plugin.name); - const plugins = this.plugins.filter((plugin2) => plugin2.position === plugin.position); - if (plugins.length === 0) { + const positionPlugins = this.plugins + .filter((plugin2) => plugin2.position === plugin.position); + if (positionPlugins.length === 0) { this.closeSidePanels(plugin.position); } } catch (e) { diff --git a/api-idee-js/src/facade/js/ui/panels/SidePanel.js b/api-idee-js/src/facade/js/ui/panels/SidePanel.js index cf54a1394..15658ac68 100644 --- a/api-idee-js/src/facade/js/ui/panels/SidePanel.js +++ b/api-idee-js/src/facade/js/ui/panels/SidePanel.js @@ -221,8 +221,10 @@ class SidePanel extends Panel { } destroy() { - if (this.element != null) { - this.element.remove(); + super.destroy(); + if (this.button && this.button.pressed) { + this.button.deactivate(); + this.button = null; } } diff --git a/api-idee-js/test/development/CP-0003-controls/CP-013.html b/api-idee-js/test/development/CP-0003-controls/CP-013.html index 5f9d318aa..e07c5e9a3 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-013.html +++ b/api-idee-js/test/development/CP-0003-controls/CP-013.html @@ -22,6 +22,32 @@ +
+
+
+ + +
+
+ + +
+
+
+ +
+
diff --git a/api-idee-js/test/development/CP-0003-controls/CP-013.js b/api-idee-js/test/development/CP-0003-controls/CP-013.js index 642281e70..5db6bc099 100644 --- a/api-idee-js/test/development/CP-0003-controls/CP-013.js +++ b/api-idee-js/test/development/CP-0003-controls/CP-013.js @@ -52,14 +52,6 @@ const controlsDown = [ measurebar, ]; -const githubPlugin = new Plugin('github-1', { - tooltip: 'Githuh 1', - position: Position.LEFT, - svgPath: 'https://componentes.idee.es/estaticos/imagenes/logos/logo-github.svg', - order: 2, - collapsed: false, -}); - const githubPlugin2 = new Plugin('github-2', { tooltip: 'Github 2', position: Position.RIGHT, @@ -77,7 +69,6 @@ const githubPlugin3 = new Plugin('github-3', { const tools = [ ...controlsDown, - githubPlugin, githubPlugin2, githubPlugin3, ]; @@ -103,3 +94,46 @@ tools.forEach((tool) => { map.removePlugin(githubPlugin2); // map.closeSidePanels(githubPlugin2.position); map.addPlugin(githubPlugin2); + +const selectPosition = document.getElementById('selectPosicion'); +const selectCollapsed = document.getElementById('selectCollapsed'); + +let plugin1; + +const create = (options) => { + plugin1 = new Plugin('github-1', { + tooltip: 'Githuh 1', + svgPath: 'https://componentes.idee.es/estaticos/imagenes/logos/logo-github.svg', + order: 2, + ...options, + }); + map.addPlugin(plugin1); +}; + +const remove = () => { + if (plugin1) map.removePlugin(plugin1); +}; + +const recreate = () => { + remove(); + + const options = {}; + options.position = selectPosition.options[selectPosition.selectedIndex].value; + options.collapsed = (selectCollapsed.options[selectCollapsed.selectedIndex].value === 'true'); + + create(options); +}; + +[ + selectPosition, + selectCollapsed, +].forEach((ctrl) => { + ctrl.addEventListener('change', recreate); +}); + +const removeButton = document.getElementById('removeButton'); +removeButton.addEventListener('click', () => { + remove(); +}); + +recreate(); From 662f5b6931ef1f99b0d4ac11c0f3d2ae893d8587 Mon Sep 17 00:00:00 2001 From: andrestcpy Date: Tue, 9 Jun 2026 17:42:58 +0200 Subject: [PATCH 10/10] feat: use corredt default projection on control implementation switcher --- .../js/control/ImplementationSwitcher.js | 17 +++--- api-idee-js/src/impl/cesium/js/Map.js | 2 +- api-idee-js/test/configuration_filtered.js | 55 +++++++------------ .../development/CP-0003-controls/CP-011.js | 3 +- 4 files changed, 30 insertions(+), 47 deletions(-) diff --git a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js index 21b02b016..cfb8b2961 100644 --- a/api-idee-js/src/facade/js/control/ImplementationSwitcher.js +++ b/api-idee-js/src/facade/js/control/ImplementationSwitcher.js @@ -174,9 +174,9 @@ class ImplementationSwitcher extends ControlBase { const configurationUrl = existingConfigScript ? existingConfigScript.src : resolveUrl('js/configuration.js'); - // if (existingConfigScript) { - // existingConfigScript.remove(); - // } + if (existingConfigScript) { + existingConfigScript.remove(); + } window.implementations.forEach((impl) => { // eslint-disable-next-line no-param-reassign @@ -244,19 +244,18 @@ class ImplementationSwitcher extends ControlBase { const rootContainer = mapFrameContainer || this.map.getContainer(); const zoom = this.map.getZoom(); - const projection = implementation.epsg; const sourceProjection = this.map.getProjection().code; - - const currentCenter = [this.map.getCenter().x, this.map.getCenter().y]; + const projection = implementation.epsg ?? IDEE.config.DEFAULT_PROJ; + const { x, y } = this.map.getCenter(); const center = (typeof ol !== 'undefined' && ol !== null) - ? ol.proj.transform(currentCenter, sourceProjection, projection) - : transform(currentCenter, sourceProjection, projection); + ? ol.proj.transform([x, y], sourceProjection, projection) + : transform([x, y], sourceProjection, projection); const controls = Array.from(this.map.getControls()).map( (control) => control.name ?? control.NAME, ); const plugins = this.map.getPlugins(); - const layers = this.map.getLayers(); + const layers = [...this.map.getBaseLayers(), ...this.map.getRootLayers()]; this.map.removeControls(this); diff --git a/api-idee-js/src/impl/cesium/js/Map.js b/api-idee-js/src/impl/cesium/js/Map.js index b8f3e9c61..28a58b4fa 100755 --- a/api-idee-js/src/impl/cesium/js/Map.js +++ b/api-idee-js/src/impl/cesium/js/Map.js @@ -108,7 +108,7 @@ class Map extends MObject { * @api */ constructor(div, facadeMap, dpi, options = {}, viewVendorOptions = {}) { - buildModuleUrl.setBaseUrl(`${IDEE.config.API_IDEE_URL}/cesium/`); + buildModuleUrl.setBaseUrl(`${IDEE.config.API_IDEE_URL}cesium/`); Ion.defaultAccessToken = ''; super(); diff --git a/api-idee-js/test/configuration_filtered.js b/api-idee-js/test/configuration_filtered.js index c0a042071..449c8a31c 100644 --- a/api-idee-js/test/configuration_filtered.js +++ b/api-idee-js/test/configuration_filtered.js @@ -17,49 +17,34 @@ let HOST_BASE = 'api-ideedes.grupotecopy.es'; const isLocalhost = false; let API_IDEE_URL = ''; -let implementationSwitcherOpts = []; +const implementationSwitcherOpts = [ + { + id: 'OL', + type: 'ol', + title: 'Open Layers', + }, + { + id: 'CS', + type: 'cesium', + title: 'Cesium', + }, +]; if (isLocalhost) { HOST_BASE = host; PROTOCOL_BASE = protocol; API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/`; - const localPath = 'dist/'; - implementationSwitcherOpts = [ - { - id: 'OL', - type: 'ol', - title: 'Open Layers', - js: `${localPath}js/apiidee.ol.min.js`, - css: `${localPath}assets/css/apiidee.ol.min.css`, - }, - { - id: 'CS', - type: 'cesium', - title: 'Cesium', - js: `${localPath}js/apiidee.cesium.min.js`, - css: `${localPath}assets/css/apiidee.cesium.min.css`, - }, - ]; + implementationSwitcherOpts[0].js = `${localPath}js/apiidee.ol.min.js`; + implementationSwitcherOpts[0].css = `${localPath}assets/css/apiidee.ol.min.css`; + implementationSwitcherOpts[1].js = `${localPath}js/apiidee.cesium.min.js`; + implementationSwitcherOpts[1].css = `${localPath}assets/css/apiidee.cesium.min.css`; } else { API_IDEE_URL = `${PROTOCOL_BASE}//${HOST_BASE}/${IDEE_PATH}/`; - - implementationSwitcherOpts = [ - { - id: 'OL', - type: 'ol', - title: 'Open Layers', - js: `${API_IDEE_URL}js/apiidee.ol.min.js`, - css: `${API_IDEE_URL}assets/css/apiidee.ol.min.css`, - }, - { - id: 'CS', - type: 'cesium', - title: 'Cesium', - js: `${API_IDEE_URL}js/apiidee.cesium.min.js`, - css: `${API_IDEE_URL}assets/css/apiidee.cesium.min.css`, - }, - ]; + implementationSwitcherOpts[0].js = `${API_IDEE_URL}js/apiidee.ol.min.js`; + implementationSwitcherOpts[0].css = `${API_IDEE_URL}assets/css/apiidee.ol.min.css`; + implementationSwitcherOpts[1].js = `${API_IDEE_URL}js/apiidee.cesium.min.js`; + implementationSwitcherOpts[1].css = `${API_IDEE_URL}assets/css/apiidee.cesium.min.css`; } const config = (configKey, configValue) => { 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 00a72f72b..5c69f5b9c 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 @@ -8,8 +8,7 @@ const map = Mmap({ container: 'map', projection: 'EPSG:4326', // controls: ['rotate'], - zoom: 10, - // layers: ['OSM'], + zoom: 8, center: [ -7.68, 43.084999999999994,