@geogirafe/lib-geoportal 1.2.0-dev.2763713312 → 1.2.0-dev.2766641185
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/i18n/de.json +3 -0
- package/assets/i18n/en.json +3 -0
- package/assets/i18n/fr.json +3 -0
- package/assets/i18n/it.json +3 -0
- package/components/extlayers/component.d.ts +1 -1
- package/components/extlayers/component.js +7 -9
- package/components/extlayers/style.css +44 -20
- package/components/treeview/treeviewitem/component.d.ts +1 -0
- package/components/treeview/treeviewitem/component.js +8 -1
- package/package.json +1 -1
- package/templates/public/about.json +1 -1
- package/tools/auth/gmfconnectmanager.d.ts +18 -3
- package/tools/auth/gmfconnectmanager.js +20 -4
- package/tools/configuration/girafeconfig.d.ts +1 -0
- package/tools/configuration/girafeconfig.js +6 -0
- package/tools/drawing/drawingFeature.d.ts +4 -0
- package/tools/drawing/drawingFeature.js +9 -0
- package/tools/localfile/localfilemanager.d.ts +2 -0
- package/tools/localfile/localfilemanager.js +44 -2
package/assets/i18n/de.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"Connecting to the print server, please wait.": "Verbindung zum Druckserver wird hergestellt, bitte warten.",
|
|
55
55
|
"contact-panel": "Kontaktformular",
|
|
56
56
|
"Control opacity": "Deckkraft steuern",
|
|
57
|
+
"Convert to drawing": "In Zeichnung umwandeln",
|
|
57
58
|
"Copy ShortUrl": "Kurz-URL kopieren",
|
|
58
59
|
"Copy to Clipboard": "In die Zwischenablage kopieren",
|
|
59
60
|
"Copy value": "Wert kopieren",
|
|
@@ -188,6 +189,7 @@
|
|
|
188
189
|
"feedback-thankyou-default": "Vielen Dank für Ihr Feedback!",
|
|
189
190
|
"File _fileName_ is not supported": "Datei _fileName_ wird nicht unterstützt",
|
|
190
191
|
"File from": "Datei vom",
|
|
192
|
+
"File loaded in the layer tree": "✓ Die Datei wurde in den Layerbaum geladen",
|
|
191
193
|
"Files _fileNames_ are not supported": "Dateien '_fileNames_' werden nicht unterstützt",
|
|
192
194
|
"Fill color": "Füllfarbe",
|
|
193
195
|
"Filter": "Filter",
|
|
@@ -352,6 +354,7 @@
|
|
|
352
354
|
"onboarding-treeview-filter-selection-description": "Sie können die in der Baumansicht vorhandenen Layer filtern",
|
|
353
355
|
"onboarding-treeview-filter-selection-title": "Layer filtern",
|
|
354
356
|
"Only WMTS layers are saved": "Nur WMTS-Layer werden gespeichert",
|
|
357
|
+
"only-n-features-loaded": "Nur _loadedCount_ von _totalCount_ Objekten konnten geladen werden. Überprüfen Sie, ob diese Objekte innerhalb des in Ihrer Anwendung konfigurierten maximalen Ausschnitts angezeigt werden können.",
|
|
355
358
|
"Open advanced filter tool": "Erweiterte Filter öffnen",
|
|
356
359
|
"open-buttonbar-menu": "Menü öffnen",
|
|
357
360
|
"OpenStreetMap": "OpenStreetMap",
|
package/assets/i18n/en.json
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"contact-panel": "Contact us",
|
|
56
56
|
"contains": "contains",
|
|
57
57
|
"Control opacity": "Control opacity",
|
|
58
|
+
"Convert to drawing": "Convert to drawing",
|
|
58
59
|
"Copy ShortUrl": "Copy ShortUrl",
|
|
59
60
|
"Copy to Clipboard": "Copy to Clipboard",
|
|
60
61
|
"Copy value": "Copier value",
|
|
@@ -190,6 +191,7 @@
|
|
|
190
191
|
"feedback-thankyou-default": "Thank you for your feedback!",
|
|
191
192
|
"File _fileName_ is not supported": "File _fileName_ is not supported",
|
|
192
193
|
"File from": "File from",
|
|
194
|
+
"File loaded in the layer tree": "✓ File loaded in the layer tree",
|
|
193
195
|
"Files _fileNames_ are not supported": "Files _fileNames_ are not supported",
|
|
194
196
|
"Fill color": "Fill color",
|
|
195
197
|
"Filter": "Filter",
|
|
@@ -354,6 +356,7 @@
|
|
|
354
356
|
"onboarding-treeview-filter-selection-description": "You have the possibility to filter the layers present in the treeview",
|
|
355
357
|
"onboarding-treeview-filter-selection-title": "Filter layer",
|
|
356
358
|
"Only WMTS layers are saved": "Only WMTS layers are saved",
|
|
359
|
+
"only-n-features-loaded": "Only _loadedCount_ features among _totalCount_ could be loaded. Verify that those features can be displayed within the maximal extent configured in your application.",
|
|
357
360
|
"Open advanced filter tool": "Open advanced filter tool",
|
|
358
361
|
"open-buttonbar-menu": "Open Menu",
|
|
359
362
|
"OpenStreetMap": "OpenStreetMap",
|
package/assets/i18n/fr.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"Connecting to the print server, please wait.": "Connexion au serveur d'impression, veuillez patienter.",
|
|
55
55
|
"contact-panel": "Nous contacter",
|
|
56
56
|
"Control opacity": "Contrôler l'opacité",
|
|
57
|
+
"Convert to drawing": "Convertir en dessin",
|
|
57
58
|
"Copy ShortUrl": "Copier le lien court",
|
|
58
59
|
"Copy to Clipboard": "Copier dans le presse-papiers",
|
|
59
60
|
"Copy value": "Copier la valeur",
|
|
@@ -188,6 +189,7 @@
|
|
|
188
189
|
"feedback-thankyou-default": "Merci pour votre commentaire !",
|
|
189
190
|
"File _fileName_ is not supported": "Le fichier _fileName_ n'est pas supporté",
|
|
190
191
|
"File from": "Fichier de",
|
|
192
|
+
"File loaded in the layer tree": "✓ Le fichier a été chargé dans l'arbre des couches",
|
|
191
193
|
"Files _fileNames_ are not supported": "Les fichiers _fileNames_ ne sont pas supportés",
|
|
192
194
|
"Fill color": "Couleur de remplissage",
|
|
193
195
|
"Filter": "Filtrer",
|
|
@@ -352,6 +354,7 @@
|
|
|
352
354
|
"onboarding-treeview-filter-selection-description": "Vous pouvez filtrer les couches présentes dans l'arborescence",
|
|
353
355
|
"onboarding-treeview-filter-selection-title": "Filtrer les couches",
|
|
354
356
|
"Only WMTS layers are saved": "Seules les couches WMTS sont sauvegardées",
|
|
357
|
+
"only-n-features-loaded": "Seules _loadedCount_ entités sur _totalCount_ ont pu être chargées. Vérifiez que ces entités peuvent être affichées dans l'étendue maximale configurée dans votre application.",
|
|
355
358
|
"Open advanced filter tool": "Ouvrir l'outil de filtre avancé",
|
|
356
359
|
"open-buttonbar-menu": "Ouvrir le menu",
|
|
357
360
|
"OpenStreetMap": "OpenStreetMap",
|
package/assets/i18n/it.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"Connecting to the print server, please wait.": "Connessione al server di stampa, attendere prego.",
|
|
55
55
|
"contact-panel": "Contattaci",
|
|
56
56
|
"Control opacity": "Controlla opacità",
|
|
57
|
+
"Convert to drawing": "Converti in disegno",
|
|
57
58
|
"Copy ShortUrl": "Copia ShortUrl",
|
|
58
59
|
"Copy to Clipboard": "Copia negli appunti",
|
|
59
60
|
"Copy value": "Copia il valore",
|
|
@@ -188,6 +189,7 @@
|
|
|
188
189
|
"feedback-thankyou-default": "Grazie per il tuo feedback!",
|
|
189
190
|
"File _fileName_ is not supported": "Il file _fileName_ non è supportato",
|
|
190
191
|
"File from": "File da",
|
|
192
|
+
"File loaded in the layer tree": "✓ Il file è stato caricato nell'albero dei layer",
|
|
191
193
|
"Files _fileNames_ are not supported": "I file _fileNames_ non sono supportati",
|
|
192
194
|
"Fill color": "Colore di riempimento",
|
|
193
195
|
"Filter": "Filtra",
|
|
@@ -352,6 +354,7 @@
|
|
|
352
354
|
"onboarding-treeview-filter-selection-description": "Puoi filtrare i livelli presenti nella struttura ad albero",
|
|
353
355
|
"onboarding-treeview-filter-selection-title": "Filtra i livelli",
|
|
354
356
|
"Only WMTS layers are saved": "Solo i layer WMTS vengono salvati",
|
|
357
|
+
"only-n-features-loaded": "Solo _loadedCount_ elementi su _totalCount_ hanno potuto essere caricati. Verificare che questi elementi possano essere visualizzati all'interno dell'estensione massima configurata nella tua applicazione.",
|
|
355
358
|
"Open advanced filter tool": "Apri lo strumento di filtro avanzato",
|
|
356
359
|
"open-buttonbar-menu": "Apri menu",
|
|
357
360
|
"OpenStreetMap": "OpenStreetMap",
|
|
@@ -17,6 +17,7 @@ declare class ExternalLayersComponent extends GirafeHTMLElement implements IGira
|
|
|
17
17
|
panelTitle: string;
|
|
18
18
|
panelTogglePath: string;
|
|
19
19
|
loading: boolean;
|
|
20
|
+
fileLoaded: boolean;
|
|
20
21
|
predefinedSources: PredefinedSource[];
|
|
21
22
|
get predefinedWmsWmtsSources(): PredefinedSource[];
|
|
22
23
|
get hasPredefinedWmsWmtsSources(): boolean;
|
|
@@ -43,7 +44,6 @@ declare class ExternalLayersComponent extends GirafeHTMLElement implements IGira
|
|
|
43
44
|
deselectVisible(): void;
|
|
44
45
|
deselectAll(): void;
|
|
45
46
|
addSelectedLayers(): void;
|
|
46
|
-
get fileDescription(): string;
|
|
47
47
|
loadFile(): Promise<void>;
|
|
48
48
|
protected connectedCallback(): void;
|
|
49
49
|
}
|
|
@@ -16,16 +16,17 @@ class ExternalLayersComponent extends GirafeHTMLElement {
|
|
|
16
16
|
return uHtml `<style>
|
|
17
17
|
.hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
|
|
18
18
|
</style><style>
|
|
19
|
-
#panel{background:var(--bkg-color);height:100%;color:var(--text-color);flex-direction:column;padding:0 1rem;display:flex}#content{flex-direction:column;flex-grow:1;margin:0;display:flex}header{background-color:var(--bkg-color);margin:0;position:sticky;top:-5px}h4{margin-bottom:.5rem}ul{margin-top:0;margin-left:0;padding-left:2rem;line-height:1.3rem;list-style-type:disc}.clearable-input{background-color:var(--bkg-color);border:1px solid #cfd6dd;border-radius:4px;flex-direction:row;margin-bottom:.1rem;display:flex}.scan-button{float:right}.layers{border-top:1px solid #ccc;flex-direction:column;flex-grow:1;margin-top:1rem;padding-top:.5rem;display:flex;overflow:hidden}ul.results{border:none;margin-bottom:.5rem;padding:0;list-style:none;overflow-y:auto}.result{height:1.6rem;display:flex}.result>button>span{text-align:left;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}#panel>div{padding:1rem}#layers li{margin-top:.3rem;margin-bottom:.3rem}#layers li button{text-align:left;overflow-wrap:anywhere;text-wrap-style:balance}#
|
|
19
|
+
#panel{background:var(--bkg-color);height:100%;color:var(--text-color);flex-direction:column;padding:0 1rem;display:flex}#content{flex-direction:column;flex-grow:1;margin:0;display:flex}header{background-color:var(--bkg-color);margin:0;position:sticky;top:-5px}h4{margin-bottom:.5rem}ul{margin-top:0;margin-left:0;padding-left:2rem;line-height:1.3rem;list-style-type:disc}.clearable-input{background-color:var(--bkg-color);border:1px solid #cfd6dd;border-radius:4px;flex-direction:row;margin-bottom:.1rem;display:flex}.scan-button{float:right}.layers{border-top:1px solid #ccc;flex-direction:column;flex-grow:1;margin-top:1rem;padding-top:.5rem;display:flex;overflow:hidden}ul.results{border:none;margin-bottom:.5rem;padding:0;list-style:none;overflow-y:auto}.result{height:1.6rem;display:flex}.result>button>span{text-align:left;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}#panel>div{padding:1rem}#layers li{margin-top:.3rem;margin-bottom:.3rem}#layers li button{text-align:left;overflow-wrap:anywhere;text-wrap-style:balance}#loading-icon{flex-grow:1;align-self:center;width:3rem}.title{margin-top:0}.filter-buttons{flex-direction:row;margin:2px;display:flex}.filter-buttons span{flex-grow:1;align-self:center}.clearable-input-field{width:90%;color:var(--text-color);background:0 0;border:0;outline:none;flex:auto;padding:0;font-size:1rem}.filter-icon,.delete-icon{width:16px;color:var(--text-color);padding:0 .5rem}.delete-button{cursor:pointer;background-color:#0000;border:none;padding:0}#file-selector{flex-direction:column;display:flex;position:relative}#file{color:var(--text-color);cursor:pointer}#file::file-selector-button{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);min-height:calc(var(--app-standard-height) / 1.5);font:inherit;cursor:pointer;border-radius:.25em;outline:0;margin:0 .5rem 0 0;padding:0 .5rem}#file::file-selector-button:hover{background-color:var(--bkg-color-grad1)}.file-loaded-message{box-sizing:border-box;background-color:var(--success-color);border-radius:.25em;width:100%;margin:.5rem 0 0;padding:.5em;position:absolute;top:100%;left:0}
|
|
20
20
|
</style>
|
|
21
21
|
<style>${this.customStyle}</style>
|
|
22
|
-
<div id="panel"><header class="gg-tabs"><button class="${this.selectedTab === 'wms_wmts' ? 'gg-tab active' : 'gg-tab'}" onclick="${() => this.setSelectedTab('wms_wmts')}" i18n="wms_wmts"></button> <button class="${this.selectedTab === 'file' ? 'gg-tab active' : 'gg-tab'}" onclick="${() => this.setSelectedTab('file')}" i18n="local file"></button></header><section class="${(this.selectedTab === 'wms_wmts') ? 'group' : 'hidden'}"><h4 class="title" i18n="Suggestions" ?hidden="${!this.hasPredefinedWmsWmtsSources}">Suggestions</h4><ul class="suggestions" ?hidden="${!this.hasPredefinedWmsWmtsSources}">${this.predefinedWmsWmtsSources.map(source => uHtmlFor(source, source.label) `<li><a href="javascript:void(0)" onclick="${() => this.scanSource(source.url, source.type)}">${source.label}</a></li>`)}</ul><h4 class="title" i18n="Custom service URL">Custom service URL</h4><div class="clearable-input"><input id="url" class="gg-input clearable-input-field" oninput="${() => this.typeUrl()}"><br><button id="clear-url" class="delete-button hidden" onclick="${() => this.clearUrl()}"><img class="delete-icon" alt="delete-button-icon" tip="Reset filter" i18n="Reset filter" src="icons/trash.svg"></button></div><button i18n="Scan Source" class="gg-button scan-button" onclick="${() => this.scanSource()}">Scan Source</button></section><section class="${(this.selectedTab === 'file') ? 'group' : 'hidden'}"><
|
|
22
|
+
<div id="panel"><header class="gg-tabs"><button class="${this.selectedTab === 'wms_wmts' ? 'gg-tab active' : 'gg-tab'}" onclick="${() => this.setSelectedTab('wms_wmts')}" i18n="wms_wmts"></button> <button class="${this.selectedTab === 'file' ? 'gg-tab active' : 'gg-tab'}" onclick="${() => this.setSelectedTab('file')}" i18n="local file"></button></header><section class="${(this.selectedTab === 'wms_wmts') ? 'group' : 'hidden'}"><h4 class="title" i18n="Suggestions" ?hidden="${!this.hasPredefinedWmsWmtsSources}">Suggestions</h4><ul class="suggestions" ?hidden="${!this.hasPredefinedWmsWmtsSources}">${this.predefinedWmsWmtsSources.map(source => uHtmlFor(source, source.label) `<li><a href="javascript:void(0)" onclick="${() => this.scanSource(source.url, source.type)}">${source.label}</a></li>`)}</ul><h4 class="title" i18n="Custom service URL">Custom service URL</h4><div class="clearable-input"><input id="url" class="gg-input clearable-input-field" oninput="${() => this.typeUrl()}"><br><button id="clear-url" class="delete-button hidden" onclick="${() => this.clearUrl()}"><img class="delete-icon" alt="delete-button-icon" tip="Reset filter" i18n="Reset filter" src="icons/trash.svg"></button></div><button i18n="Scan Source" class="gg-button scan-button" onclick="${() => this.scanSource()}">Scan Source</button></section><section class="${(this.selectedTab === 'file') ? 'group' : 'hidden'}"><div id="file-selector"><label for="file" class="gg-label" i18n="Choose local file">Choose local file</label> <input type="file" accept=".gpx,.kml,.geojson,.json" id="file" onchange="${() => this.loadFile()}"><p class="${this.fileLoaded ? 'file-loaded-message' : 'hidden'}" i18n="File loaded in the layer tree">File loaded in the layer tree</div></section><section class="${(this.selectedTab === 'wms_wmts' && this.externalLayers.length > 0) || this.loading ? 'layers' : 'hidden'}"><h4 class="title" i18n="Layers (from Capabilities)">Layers (from Capabilities)</h4><div class="clearable-input"><img class="filter-icon" alt="filter-button-icon" src="icons/filter.svg"> <input id="layer-search-field" class="gg-input clearable-input-field" placeholder="${this.context.i18nManager.getTranslation('Filter external layers...')}" i18n="Filter external layers..." autocomplete="off" autocorrect="off" oninput="${(e) => this.filterLayers(e.target.value)}"> <button class="${this.isFiltered ? 'delete-button' : 'hidden'}" onclick="${() => this.clearFilter()}"><img class="delete-icon" alt="delete-button-icon" tip="Reset filter" i18n="Reset filter" src="icons/trash.svg"></button></div><div class="filter-buttons"><span>${Object.values(this.externalLayers).filter(layer => layer.isSelected).length.toString()} ${this.context.i18nManager.getTranslation('layers selected')} </span><button class="gg-button" tip="Select all filtered layers" onclick="${() => this.selectVisible()}">+</button> <button class="gg-button" tip="Unselect all filtered layers" onclick="${() => this.deselectVisible()}">-</button> <button class="gg-button" tip="Select none" onclick="${() => this.deselectAll()}">--</button></div><img id="loading-icon" alt="loading-icon" src="icons/loading.svg" class="${this.loading ? 'gg-spin' : 'hidden'}"><ul class="results">${this.filteredLayers.map(layer => uHtmlFor(layer, layer.treeItemId) `<li class="result"><button class="${layer.isSelected ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" tip="Activate / Deactivate" onclick="${() => this.selectLayer(layer)}"><img class="${layer.isSelected ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="icons/checked-full.svg"> <img class="${!layer.isSelected ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="icons/checked-no.svg"></button> <button class="${layer.isSelected ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" onclick="${() => this.selectLayer(layer)}"><span i18n="${layer.name}" class="gg-tree-label">${layer.name}</span></button></li>`)}</ul><button id="add" tip="Add selected" i18n="Add selected" class="gg-button" onclick="${() => this.addSelectedLayers()}">Add selected</button></section></div>
|
|
23
23
|
${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
24
24
|
};
|
|
25
25
|
isPanelVisible = false;
|
|
26
26
|
panelTitle = 'ext-layer-panel';
|
|
27
27
|
panelTogglePath = 'interface.extLayerPanelVisible';
|
|
28
28
|
loading = false;
|
|
29
|
+
fileLoaded = false;
|
|
29
30
|
predefinedSources = [];
|
|
30
31
|
get predefinedWmsWmtsSources() {
|
|
31
32
|
return this.predefinedSources.filter((source) => source.type === 'WMS' || source.type === 'WMTS');
|
|
@@ -211,18 +212,15 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
|
211
212
|
this.state.layers.layersList.push(theme);
|
|
212
213
|
});
|
|
213
214
|
}
|
|
214
|
-
get fileDescription() {
|
|
215
|
-
const selectedFiles = this.getById('file')?.files;
|
|
216
|
-
if (!selectedFiles || selectedFiles.length == 0) {
|
|
217
|
-
return '';
|
|
218
|
-
}
|
|
219
|
-
return `${selectedFiles[0].name} (${selectedFiles[0].size}b)`;
|
|
220
|
-
}
|
|
221
215
|
async loadFile() {
|
|
222
216
|
const selectedFiles = this.getById('file').files;
|
|
223
217
|
if (selectedFiles && selectedFiles.length > 0) {
|
|
224
218
|
const selectedFile = selectedFiles[0];
|
|
219
|
+
this.fileLoaded = false;
|
|
220
|
+
this.refreshRender();
|
|
225
221
|
await this.context.localFileManager.loadLocalFile(selectedFile);
|
|
222
|
+
this.fileLoaded = true;
|
|
223
|
+
this.refreshRender();
|
|
226
224
|
}
|
|
227
225
|
}
|
|
228
226
|
connectedCallback() {
|
|
@@ -92,26 +92,6 @@ ul.results {
|
|
|
92
92
|
text-wrap-style: balance;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
#file-selector {
|
|
96
|
-
display: grid;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
#file,
|
|
100
|
-
#file-label {
|
|
101
|
-
grid-row: 1;
|
|
102
|
-
grid-column: 1;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
#file {
|
|
106
|
-
opacity: 0;
|
|
107
|
-
z-index: 2;
|
|
108
|
-
cursor: pointer;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
#file::file-selector-button {
|
|
112
|
-
cursor: pointer;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
95
|
#loading-icon {
|
|
116
96
|
width: 3rem;
|
|
117
97
|
flex-grow: 1;
|
|
@@ -157,3 +137,47 @@ ul.results {
|
|
|
157
137
|
background-color: transparent;
|
|
158
138
|
cursor: pointer;
|
|
159
139
|
}
|
|
140
|
+
|
|
141
|
+
#file-selector {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
/* Positioned so the message below can be taken out of flow without
|
|
145
|
+
growing #panel, which is sized with width: fit-content (see .panel-right) */
|
|
146
|
+
position: relative;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#file {
|
|
150
|
+
color: var(--text-color);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#file::file-selector-button {
|
|
155
|
+
background-color: var(--bkg-color);
|
|
156
|
+
color: var(--text-color);
|
|
157
|
+
outline: 0;
|
|
158
|
+
border-radius: 0.25em;
|
|
159
|
+
border: var(--app-standard-border);
|
|
160
|
+
padding: 0 0.5rem;
|
|
161
|
+
margin: 0 0.5rem 0 0;
|
|
162
|
+
min-height: calc(var(--app-standard-height) / 1.5);
|
|
163
|
+
font: inherit;
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#file::file-selector-button:hover {
|
|
168
|
+
background-color: var(--bkg-color-grad1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.file-loaded-message {
|
|
172
|
+
/* Absolutely positioned so its width doesn't feed into the ancestor
|
|
173
|
+
panel's fit-content sizing and grow/shrink the whole right panel */
|
|
174
|
+
position: absolute;
|
|
175
|
+
top: 100%;
|
|
176
|
+
left: 0;
|
|
177
|
+
width: 100%;
|
|
178
|
+
box-sizing: border-box;
|
|
179
|
+
padding: 0.5em;
|
|
180
|
+
border-radius: 0.25em;
|
|
181
|
+
background-color: var(--success-color);
|
|
182
|
+
margin: 0.5rem 0 0;
|
|
183
|
+
}
|
|
@@ -26,6 +26,7 @@ declare class TreeViewItemComponent extends TreeViewElement {
|
|
|
26
26
|
zoomToVisibleResolution(): void;
|
|
27
27
|
isVisibleInCurrentResolution(): boolean;
|
|
28
28
|
zoomToFullExtent(): void;
|
|
29
|
+
convertToDrawing(): void;
|
|
29
30
|
deleteLayer(): void;
|
|
30
31
|
protected connectedCallback(): void;
|
|
31
32
|
}
|
|
@@ -19,7 +19,7 @@ header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.to
|
|
|
19
19
|
.hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
|
|
20
20
|
</style>
|
|
21
21
|
<style>${this.customStyle}</style>
|
|
22
|
-
<link rel="stylesheet" href="lib/tippy.js/tippy.css"><link rel="stylesheet" href="lib/tippy.js/backdrop.css"><link rel="stylesheet" href="lib/tippy.js/border.css"><link rel="stylesheet" href="lib/tippy.js/svg-arrow.css"><div id="container"><header><div class="gg-spacer gg-small"><img class="${this.layer.restricted ? (this.layer.active ? 'gg-small gg-lock gg-selected' : 'gg-small gg-lock gg-opacity') : 'hidden'}" alt="Protected layer" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8bWFzayBpZD0ibWFzazBfMzdfNDUxNyIgc3R5bGU9Im1hc2stdHlwZTphbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij4KICA8cmVjdCB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9IiNEOUQ5RDkiLz4KICA8L21hc2s+CiAgPGcgbWFzaz0idXJsKCNtYXNrMF8zN180NTE3KSI+CiAgPHBhdGggZD0iTTYgMjJDNS40NSAyMiA0Ljk3OTE3IDIxLjgwNDIgNC41ODc1IDIxLjQxMjVDNC4xOTU4MyAyMS4wMjA4IDQgMjAuNTUgNCAyMFYxMEM0IDkuNDUgNC4xOTU4MyA4Ljk3OTE3IDQuNTg3NSA4LjU4NzVDNC45NzkxNyA4LjE5NTgzIDUuNDUgOCA2IDhIN1Y2QzcgNC42MTY2NyA3LjQ4NzUgMy40Mzc1IDguNDYyNSAyLjQ2MjVDOS40Mzc1IDEuNDg3NSAxMC42MTY3IDEgMTIgMUMxMy4zODMzIDEgMTQuNTYyNSAxLjQ4NzUgMTUuNTM3NSAyLjQ2MjVDMTYuNTEyNSAzLjQzNzUgMTcgNC42MTY2NyAxNyA2VjhIMThDMTguNTUgOCAxOS4wMjA4IDguMTk1ODMgMTkuNDEyNSA4LjU4NzVDMTkuODA0MiA4Ljk3OTE3IDIwIDkuNDUgMjAgMTBWMjBDMjAgMjAuNTUgMTkuODA0MiAyMS4wMjA4IDE5LjQxMjUgMjEuNDEyNUMxOS4wMjA4IDIxLjgwNDIgMTguNTUgMjIgMTggMjJINlpNNiAyMEgxOFYxMEg2VjIwWk0xMiAxN0MxMi41NSAxNyAxMy4wMjA4IDE2LjgwNDIgMTMuNDEyNSAxNi40MTI1QzEzLjgwNDIgMTYuMDIwOCAxNCAxNS41NSAxNCAxNUMxNCAxNC40NSAxMy44MDQyIDEzLjk3OTIgMTMuNDEyNSAxMy41ODc1QzEzLjAyMDggMTMuMTk1OCAxMi41NSAxMyAxMiAxM0MxMS40NSAxMyAxMC45NzkyIDEzLjE5NTggMTAuNTg3NSAxMy41ODc1QzEwLjE5NTggMTMuOTc5MiAxMCAxNC40NSAxMCAxNUMxMCAxNS41NSAxMC4xOTU4IDE2LjAyMDggMTAuNTg3NSAxNi40MTI1QzEwLjk3OTIgMTYuODA0MiAxMS40NSAxNyAxMiAxN1pNOSA4SDE1VjZDMTUgNS4xNjY2NyAxNC43MDgzIDQuNDU4MzMgMTQuMTI1IDMuODc1QzEzLjU0MTcgMy4yOTE2NyAxMi44MzMzIDMgMTIgM0MxMS4xNjY3IDMgMTAuNDU4MyAzLjI5MTY3IDkuODc1IDMuODc1QzkuMjkxNjcgNC40NTgzMyA5IDUuMTY2NjcgOSA2VjhaIiBmaWxsPSIjMUMxQjFGIi8+CiAgPC9nPgo8L3N2Zz4KICA="></div><button class="${this.layer.active ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" tip="Activate / Deactivate" onclick="${() => this.toggle()}"><img class="${this.layer.active ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTY0IDMyQzI4LjcgMzIgMCA2MC43IDAgOTZMMCA0MTZjMCAzNS4zIDI4LjcgNjQgNjQgNjRsMzIwIDBjMzUuMyAwIDY0LTI4LjcgNjQtNjRsMC0zMjBjMC0zNS4zLTI4LjctNjQtNjQtNjRMNjQgMzJ6TTMzNyAyMDlMMjA5IDMzN2MtOS40IDkuNC0yNC42IDkuNC0zMy45IDBsLTY0LTY0Yy05LjQtOS40LTkuNC0yNC42IDAtMzMuOXMyNC42LTkuNCAzMy45IDBsNDcgNDdMMzAzIDE3NWM5LjQtOS40IDI0LjYtOS40IDMzLjkgMHM5LjQgMjQuNiAwIDMzLjl6Ii8+PC9zdmc+"> <img class="${!this.layer.active ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4NCA4MGM4LjggMCAxNiA3LjIgMTYgMTZsMCAzMjBjMCA4LjgtNy4yIDE2LTE2IDE2TDY0IDQzMmMtOC44IDAtMTYtNy4yLTE2LTE2TDQ4IDk2YzAtOC44IDcuMi0xNiAxNi0xNmwzMjAgMHpNNjQgMzJDMjguNyAzMiAwIDYwLjcgMCA5NkwwIDQxNmMwIDM1LjMgMjguNyA2NCA2NCA2NGwzMjAgMGMzNS4zIDAgNjQtMjguNyA2NC02NGwwLTMyMGMwLTM1LjMtMjguNy02NC02NC02NEw2NCAzMnoiLz48L3N2Zz4="></button><div class="${this.layer.hasError ? 'gg-icon gg-small gg-opacity' : 'hidden'}" tip="${this.layer.errorMessage ? this.layer.errorMessage : 'Error on layer'}"><img alt="Error on layer" src="icons/error.svg"></div><div class="${this.isVisibleInCurrentResolution() ? 'label-container' : 'label-container label-out-resolution'}"><button class="${this.layer.active ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" onclick="${() => this.toggle()}"><span i18n="${this.layer.name}" class="gg-tree-label">${this.layer.name}</span></button> <button class="${this.layer.active && this.layer instanceof LayerWms && !this.isVisibleInCurrentResolution() ? 'gg-icon-button gg-small resolution-tool' : 'hidden'}" tip="Zoom to visible resolution" onclick="${() => this.zoomToVisibleResolution()}"><img alt="Zoom to visible resolution" src="icons/zoom.svg"></button> <img class="${this.layer.active && this.iconUrl ? 'legend-icon' : 'hidden'}" alt="${'Icon for ' + this.layer.name}" src="${this.iconUrl}" loading="lazy" crossorigin="${this.getCrossOrigin(this.iconUrl)}"></div><button class="${this.layer.active && this.layer instanceof LayerLocalFile ? 'gg-icon-button gg-small gg-opacity tool' : 'hidden'}" tip="Zoom to full extent" onclick="${() => this.zoomToFullExtent()}"><img alt="Zoom to full extent" src="icons/zoom.svg"></button> <button class="${this.getButtonClass('swipedLeft')}" tip="Swipe layer to the left" onclick="${() => this.layer.swiped = (this.layer.swiped === 'left') ? 'no' : 'left'}"><img alt="Swipe layer to the left" src="icons/swipe-left.svg"></button> <button class="${this.getButtonClass('swipedRight')}" tip="Swipe layer to the right" onclick="${() => this.layer.swiped = (this.layer.swiped === 'right') ? 'no' : 'right'}"><img alt="Swipe layer to the right" src="icons/swipe-right.svg"></button> <button id="opacity" class="${this.getButtonClass('opacity')}" tip="Control opacity"><img alt="Control opacity" src="icons/opacity.svg"></button> <button id="filter" class="${this.getButtonClass('filter')}" tip="Filter layer"><img alt="Filter layer" src="icons/filter.svg"></button> <button id="advanced-filter" class="${this.getButtonClass('advanced-filter')}" tip="Open advanced filter tool" onclick="${() => this.state.interface.advancedFilterPanelVisible = !this.state.interface.advancedFilterPanelVisible}"><img alt="Advanced filter" src="icons/advanced-filter.svg"></button> <button id="timeRestriction" class="${this.getButtonClass('timeRestriction')}" tip="Define time restriction on layer"><img alt="Define time restriction on layer" src="icons/clock.svg"></button> <button class="${this.getButtonClass('snappable')}" onclick="${() => this.toggleSnap()}" tip="Snap to layer features"><img alt="Enable snapping" src="icons/snapping.svg"></button> <button class="${this.hasLegend ? (this.layer.isLegendExpanded ? 'gg-icon-button gg-small tool' : 'gg-icon-button gg-small gg-opacity tool') : 'hidden'}" tip="Toggle legend" onclick="${() => this.toggleLegend()}"><img alt="Toggle legend" src="icons/legend.svg" loading="lazy"></button> <button class="${this.layer.hasMetadata ? 'metadata gg-icon-button gg-small gg-opacity tool' : 'hidden'}" onclick="${() => this.showMetadata()}"><img alt="Metadata" src="icons/info.svg"></button> <button id="drag-button" class="${this.getButtonClass('draggable')}" tip="Move"><img alt="Move layer" src="icons/drag.svg"></button> <button class="${this.getButtonClass('removable')}" tip="Remove layer" onclick="${() => this.deleteLayer()}"><img alt="Remove layer" src="icons/trash.svg"></button></header>${Object.keys(this.legendUrls).map((l) => uHtml `<div class="${this.isLegendExpanded ? 'legend' : 'hidden'}"><div class="gg-spacer gg-small"></div><img alt="${'Legend for ' + l}" src="${this.legendUrls[l]}" loading="lazy" crossorigin="${this.getCrossOrigin(this.legendUrls[l])}"></div>`)}<div class="${this.layer instanceof LayerLocalFile && this.isLegendExpanded ? 'legend-file' : 'legend-file hidden'}"><div class="gg-spacer gg-small"></div><div class="gg-spacer gg-small"></div><div><span i18n="File from">File from</span> <span>${this.layer.lastModifiedDate}</span> <span>[</span> <span>${this.layer._features?.length}</span> <span i18n="features">features</span> <span>]</span></div></div></div>
|
|
22
|
+
<link rel="stylesheet" href="lib/tippy.js/tippy.css"><link rel="stylesheet" href="lib/tippy.js/backdrop.css"><link rel="stylesheet" href="lib/tippy.js/border.css"><link rel="stylesheet" href="lib/tippy.js/svg-arrow.css"><div id="container"><header><div class="gg-spacer gg-small"><img class="${this.layer.restricted ? (this.layer.active ? 'gg-small gg-lock gg-selected' : 'gg-small gg-lock gg-opacity') : 'hidden'}" alt="Protected layer" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8bWFzayBpZD0ibWFzazBfMzdfNDUxNyIgc3R5bGU9Im1hc2stdHlwZTphbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij4KICA8cmVjdCB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9IiNEOUQ5RDkiLz4KICA8L21hc2s+CiAgPGcgbWFzaz0idXJsKCNtYXNrMF8zN180NTE3KSI+CiAgPHBhdGggZD0iTTYgMjJDNS40NSAyMiA0Ljk3OTE3IDIxLjgwNDIgNC41ODc1IDIxLjQxMjVDNC4xOTU4MyAyMS4wMjA4IDQgMjAuNTUgNCAyMFYxMEM0IDkuNDUgNC4xOTU4MyA4Ljk3OTE3IDQuNTg3NSA4LjU4NzVDNC45NzkxNyA4LjE5NTgzIDUuNDUgOCA2IDhIN1Y2QzcgNC42MTY2NyA3LjQ4NzUgMy40Mzc1IDguNDYyNSAyLjQ2MjVDOS40Mzc1IDEuNDg3NSAxMC42MTY3IDEgMTIgMUMxMy4zODMzIDEgMTQuNTYyNSAxLjQ4NzUgMTUuNTM3NSAyLjQ2MjVDMTYuNTEyNSAzLjQzNzUgMTcgNC42MTY2NyAxNyA2VjhIMThDMTguNTUgOCAxOS4wMjA4IDguMTk1ODMgMTkuNDEyNSA4LjU4NzVDMTkuODA0MiA4Ljk3OTE3IDIwIDkuNDUgMjAgMTBWMjBDMjAgMjAuNTUgMTkuODA0MiAyMS4wMjA4IDE5LjQxMjUgMjEuNDEyNUMxOS4wMjA4IDIxLjgwNDIgMTguNTUgMjIgMTggMjJINlpNNiAyMEgxOFYxMEg2VjIwWk0xMiAxN0MxMi41NSAxNyAxMy4wMjA4IDE2LjgwNDIgMTMuNDEyNSAxNi40MTI1QzEzLjgwNDIgMTYuMDIwOCAxNCAxNS41NSAxNCAxNUMxNCAxNC40NSAxMy44MDQyIDEzLjk3OTIgMTMuNDEyNSAxMy41ODc1QzEzLjAyMDggMTMuMTk1OCAxMi41NSAxMyAxMiAxM0MxMS40NSAxMyAxMC45NzkyIDEzLjE5NTggMTAuNTg3NSAxMy41ODc1QzEwLjE5NTggMTMuOTc5MiAxMCAxNC40NSAxMCAxNUMxMCAxNS41NSAxMC4xOTU4IDE2LjAyMDggMTAuNTg3NSAxNi40MTI1QzEwLjk3OTIgMTYuODA0MiAxMS40NSAxNyAxMiAxN1pNOSA4SDE1VjZDMTUgNS4xNjY2NyAxNC43MDgzIDQuNDU4MzMgMTQuMTI1IDMuODc1QzEzLjU0MTcgMy4yOTE2NyAxMi44MzMzIDMgMTIgM0MxMS4xNjY3IDMgMTAuNDU4MyAzLjI5MTY3IDkuODc1IDMuODc1QzkuMjkxNjcgNC40NTgzMyA5IDUuMTY2NjcgOSA2VjhaIiBmaWxsPSIjMUMxQjFGIi8+CiAgPC9nPgo8L3N2Zz4KICA="></div><button class="${this.layer.active ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" tip="Activate / Deactivate" onclick="${() => this.toggle()}"><img class="${this.layer.active ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTY0IDMyQzI4LjcgMzIgMCA2MC43IDAgOTZMMCA0MTZjMCAzNS4zIDI4LjcgNjQgNjQgNjRsMzIwIDBjMzUuMyAwIDY0LTI4LjcgNjQtNjRsMC0zMjBjMC0zNS4zLTI4LjctNjQtNjQtNjRMNjQgMzJ6TTMzNyAyMDlMMjA5IDMzN2MtOS40IDkuNC0yNC42IDkuNC0zMy45IDBsLTY0LTY0Yy05LjQtOS40LTkuNC0yNC42IDAtMzMuOXMyNC42LTkuNCAzMy45IDBsNDcgNDdMMzAzIDE3NWM5LjQtOS40IDI0LjYtOS40IDMzLjkgMHM5LjQgMjQuNiAwIDMzLjl6Ii8+PC9zdmc+"> <img class="${!this.layer.active ? 'gg-checkbox' : 'hidden'}" alt="Expand/Collapse button" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4NCA4MGM4LjggMCAxNiA3LjIgMTYgMTZsMCAzMjBjMCA4LjgtNy4yIDE2LTE2IDE2TDY0IDQzMmMtOC44IDAtMTYtNy4yLTE2LTE2TDQ4IDk2YzAtOC44IDcuMi0xNiAxNi0xNmwzMjAgMHpNNjQgMzJDMjguNyAzMiAwIDYwLjcgMCA5NkwwIDQxNmMwIDM1LjMgMjguNyA2NCA2NCA2NGwzMjAgMGMzNS4zIDAgNjQtMjguNyA2NC02NGwwLTMyMGMwLTM1LjMtMjguNy02NC02NC02NEw2NCAzMnoiLz48L3N2Zz4="></button><div class="${this.layer.hasError ? 'gg-icon gg-small gg-opacity' : 'hidden'}" tip="${this.layer.errorMessage ? this.layer.errorMessage : 'Error on layer'}"><img alt="Error on layer" src="icons/error.svg"></div><div class="${this.isVisibleInCurrentResolution() ? 'label-container' : 'label-container label-out-resolution'}"><button class="${this.layer.active ? 'gg-icon-button gg-small gg-selected' : 'gg-icon-button gg-small gg-opacity'}" onclick="${() => this.toggle()}"><span i18n="${this.layer.name}" class="gg-tree-label">${this.layer.name}</span></button> <button class="${this.layer.active && this.layer instanceof LayerWms && !this.isVisibleInCurrentResolution() ? 'gg-icon-button gg-small resolution-tool' : 'hidden'}" tip="Zoom to visible resolution" onclick="${() => this.zoomToVisibleResolution()}"><img alt="Zoom to visible resolution" src="icons/zoom.svg"></button> <img class="${this.layer.active && this.iconUrl ? 'legend-icon' : 'hidden'}" alt="${'Icon for ' + this.layer.name}" src="${this.iconUrl}" loading="lazy" crossorigin="${this.getCrossOrigin(this.iconUrl)}"></div><button class="${this.layer.active && this.layer instanceof LayerLocalFile ? 'gg-icon-button gg-small gg-opacity tool' : 'hidden'}" tip="Zoom to full extent" onclick="${() => this.zoomToFullExtent()}"><img alt="Zoom to full extent" src="icons/zoom.svg"></button> <button class="${this.layer.active && this.layer instanceof LayerLocalFile ? 'gg-icon-button gg-small gg-opacity tool' : 'hidden'}" tip="Convert to drawing" onclick="${() => this.convertToDrawing()}"><img alt="draw-icon" src="icons/draw.svg"></button> <button class="${this.getButtonClass('swipedLeft')}" tip="Swipe layer to the left" onclick="${() => this.layer.swiped = (this.layer.swiped === 'left') ? 'no' : 'left'}"><img alt="Swipe layer to the left" src="icons/swipe-left.svg"></button> <button class="${this.getButtonClass('swipedRight')}" tip="Swipe layer to the right" onclick="${() => this.layer.swiped = (this.layer.swiped === 'right') ? 'no' : 'right'}"><img alt="Swipe layer to the right" src="icons/swipe-right.svg"></button> <button id="opacity" class="${this.getButtonClass('opacity')}" tip="Control opacity"><img alt="Control opacity" src="icons/opacity.svg"></button> <button id="filter" class="${this.getButtonClass('filter')}" tip="Filter layer"><img alt="Filter layer" src="icons/filter.svg"></button> <button id="advanced-filter" class="${this.getButtonClass('advanced-filter')}" tip="Open advanced filter tool" onclick="${() => this.state.interface.advancedFilterPanelVisible = !this.state.interface.advancedFilterPanelVisible}"><img alt="Advanced filter" src="icons/advanced-filter.svg"></button> <button id="timeRestriction" class="${this.getButtonClass('timeRestriction')}" tip="Define time restriction on layer"><img alt="Define time restriction on layer" src="icons/clock.svg"></button> <button class="${this.getButtonClass('snappable')}" onclick="${() => this.toggleSnap()}" tip="Snap to layer features"><img alt="Enable snapping" src="icons/snapping.svg"></button> <button class="${this.hasLegend ? (this.layer.isLegendExpanded ? 'gg-icon-button gg-small tool' : 'gg-icon-button gg-small gg-opacity tool') : 'hidden'}" tip="Toggle legend" onclick="${() => this.toggleLegend()}"><img alt="Toggle legend" src="icons/legend.svg" loading="lazy"></button> <button class="${this.layer.hasMetadata ? 'metadata gg-icon-button gg-small gg-opacity tool' : 'hidden'}" onclick="${() => this.showMetadata()}"><img alt="Metadata" src="icons/info.svg"></button> <button id="drag-button" class="${this.getButtonClass('draggable')}" tip="Move"><img alt="Move layer" src="icons/drag.svg"></button> <button class="${this.getButtonClass('removable')}" tip="Remove layer" onclick="${() => this.deleteLayer()}"><img alt="Remove layer" src="icons/trash.svg"></button></header>${Object.keys(this.legendUrls).map((l) => uHtml `<div class="${this.isLegendExpanded ? 'legend' : 'hidden'}"><div class="gg-spacer gg-small"></div><img alt="${'Legend for ' + l}" src="${this.legendUrls[l]}" loading="lazy" crossorigin="${this.getCrossOrigin(this.legendUrls[l])}"></div>`)}<div class="${this.layer instanceof LayerLocalFile && this.isLegendExpanded ? 'legend-file' : 'legend-file hidden'}"><div class="gg-spacer gg-small"></div><div class="gg-spacer gg-small"></div><div><span i18n="File from">File from</span> <span>${this.layer.lastModifiedDate}</span> <span>[</span> <span>${this.layer._features?.length}</span> <span i18n="features">features</span> <span>]</span></div></div></div>
|
|
23
23
|
${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
24
24
|
};
|
|
25
25
|
iconUrl = null;
|
|
@@ -224,6 +224,13 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
|
224
224
|
}
|
|
225
225
|
this.context.mapManager.zoomToExtent(this.layer.extent);
|
|
226
226
|
}
|
|
227
|
+
convertToDrawing() {
|
|
228
|
+
if (!(this.layer instanceof LayerLocalFile)) {
|
|
229
|
+
throw new Error(`${this.layer.name} is not a LocalFile layer, this method should not be called here.`);
|
|
230
|
+
}
|
|
231
|
+
this.context.localFileManager.convertToDrawing(this.layer);
|
|
232
|
+
this.context.stateManager.state.interface.drawingPanelVisible = true;
|
|
233
|
+
}
|
|
227
234
|
deleteLayer() {
|
|
228
235
|
if (this.layer instanceof LayerDrawing) {
|
|
229
236
|
this.layer.removalRequested = true;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.2.0-dev.
|
|
1
|
+
{"version":"1.2.0-dev.2766641185", "build":"2766641185", "date":"17/08/2026"}
|
|
@@ -12,15 +12,30 @@ import GMFManager from './gmfmanager.js';
|
|
|
12
12
|
* But you do not get any valid cookie for the GMF Backend.
|
|
13
13
|
*
|
|
14
14
|
* For all those reasons, we cannot use the geomapfish oAuth process
|
|
15
|
-
* Instead we
|
|
16
|
-
*
|
|
15
|
+
* Instead we delegate the login to the backend, which is a Backend-For-Frontend (BFF) pattern:
|
|
16
|
+
* the frontend only ever holds a session cookie, and never needs any OIDC-specific configuration
|
|
17
|
+
* (no clientId, no issuer url) -- the identity provider is only known to the backend.
|
|
18
|
+
*
|
|
19
|
+
* Two `gmfauth.loginMode` flavors are supported, both delegating entirely to the backend:
|
|
20
|
+
* - 'form' (default): the backend's login.html page, a standard GMF username/password login form.
|
|
21
|
+
* There is no oAuth process at all in this mode.
|
|
22
|
+
* - 'oidc': the backend's oidc/login route (c2cgeoportal_geoportal's OpenID Connect integration).
|
|
23
|
+
* The browser is redirected to oidc/login, which itself redirects to the identity provider;
|
|
24
|
+
* after the user authenticates there, the backend's oidc/callback exchanges the code for tokens
|
|
25
|
+
* (server-side, using the client_secret) and redirects back here with the auth_tkt session cookie
|
|
26
|
+
* set. This is exactly how ngeo authenticates against a GMF backend with
|
|
27
|
+
* `authentication.openid_connect` enabled: the browser never talks to the identity provider
|
|
28
|
+
* directly, and gg-viewer needs no more configuration for this mode than for 'form'.
|
|
29
|
+
*
|
|
30
|
+
* Once the redirect comes back (either flavor), everything below is identical: we only ever
|
|
31
|
+
* trust the session cookie and poll the backend's loginuser endpoint to know who is logged in.
|
|
17
32
|
*
|
|
18
33
|
* NOTE: If the geogirafe client is not running on the same domain as the GMF backend,
|
|
19
34
|
* the GMF Backend needs to be configured with :
|
|
20
35
|
* - CORS with credentials for specific domain (this can be done for example with an lua script at the in the haproxy configuration)
|
|
21
36
|
* - The frontend domain has to be allowed as referer in the vars.yaml file.
|
|
22
37
|
* - The variable AUTHTKT_SAMESITE has to be set to None, to allow authentication cookies to be sent to the backend from another domain
|
|
23
|
-
*
|
|
38
|
+
* These constraints apply identically to both loginMode flavors.
|
|
24
39
|
*/
|
|
25
40
|
export default class GMFConnectManager extends AbstractConnectManager {
|
|
26
41
|
private readonly gmfManager;
|
|
@@ -10,15 +10,30 @@ import AbstractConnectManager from './abstractconnectmanager.js';
|
|
|
10
10
|
* But you do not get any valid cookie for the GMF Backend.
|
|
11
11
|
*
|
|
12
12
|
* For all those reasons, we cannot use the geomapfish oAuth process
|
|
13
|
-
* Instead we
|
|
14
|
-
*
|
|
13
|
+
* Instead we delegate the login to the backend, which is a Backend-For-Frontend (BFF) pattern:
|
|
14
|
+
* the frontend only ever holds a session cookie, and never needs any OIDC-specific configuration
|
|
15
|
+
* (no clientId, no issuer url) -- the identity provider is only known to the backend.
|
|
16
|
+
*
|
|
17
|
+
* Two `gmfauth.loginMode` flavors are supported, both delegating entirely to the backend:
|
|
18
|
+
* - 'form' (default): the backend's login.html page, a standard GMF username/password login form.
|
|
19
|
+
* There is no oAuth process at all in this mode.
|
|
20
|
+
* - 'oidc': the backend's oidc/login route (c2cgeoportal_geoportal's OpenID Connect integration).
|
|
21
|
+
* The browser is redirected to oidc/login, which itself redirects to the identity provider;
|
|
22
|
+
* after the user authenticates there, the backend's oidc/callback exchanges the code for tokens
|
|
23
|
+
* (server-side, using the client_secret) and redirects back here with the auth_tkt session cookie
|
|
24
|
+
* set. This is exactly how ngeo authenticates against a GMF backend with
|
|
25
|
+
* `authentication.openid_connect` enabled: the browser never talks to the identity provider
|
|
26
|
+
* directly, and gg-viewer needs no more configuration for this mode than for 'form'.
|
|
27
|
+
*
|
|
28
|
+
* Once the redirect comes back (either flavor), everything below is identical: we only ever
|
|
29
|
+
* trust the session cookie and poll the backend's loginuser endpoint to know who is logged in.
|
|
15
30
|
*
|
|
16
31
|
* NOTE: If the geogirafe client is not running on the same domain as the GMF backend,
|
|
17
32
|
* the GMF Backend needs to be configured with :
|
|
18
33
|
* - CORS with credentials for specific domain (this can be done for example with an lua script at the in the haproxy configuration)
|
|
19
34
|
* - The frontend domain has to be allowed as referer in the vars.yaml file.
|
|
20
35
|
* - The variable AUTHTKT_SAMESITE has to be set to None, to allow authentication cookies to be sent to the backend from another domain
|
|
21
|
-
*
|
|
36
|
+
* These constraints apply identically to both loginMode flavors.
|
|
22
37
|
*/
|
|
23
38
|
export default class GMFConnectManager extends AbstractConnectManager {
|
|
24
39
|
gmfManager;
|
|
@@ -56,7 +71,8 @@ export default class GMFConnectManager extends AbstractConnectManager {
|
|
|
56
71
|
redirectToIssuerLogin() {
|
|
57
72
|
this.context.sessionManager.saveStateToSession();
|
|
58
73
|
this.redirectUrl = this.getLoginRedirectUrl(false);
|
|
59
|
-
const
|
|
74
|
+
const loginPath = this.authConfig.loginMode === 'oidc' ? 'oidc/login' : 'login.html';
|
|
75
|
+
const authorizationUrl = new URL(`${this.authConfig.url}${loginPath}`);
|
|
60
76
|
authorizationUrl.searchParams.set('came_from', this.redirectUrl);
|
|
61
77
|
window.open(authorizationUrl, '_self');
|
|
62
78
|
}
|
|
@@ -376,12 +376,18 @@ class GirafeConfig {
|
|
|
376
376
|
if (!config.gmfauth.audience) {
|
|
377
377
|
throw new Error(`Configuration for gmfauth.audience is required. See https://doc.geogirafe.org/docs/configuration.`);
|
|
378
378
|
}
|
|
379
|
+
if (config.gmfauth.loginMode !== undefined &&
|
|
380
|
+
config.gmfauth.loginMode !== 'form' &&
|
|
381
|
+
config.gmfauth.loginMode !== 'oidc') {
|
|
382
|
+
throw new Error(`Configuration for gmfauth.loginMode must be 'form' or 'oidc'. See https://doc.geogirafe.org/docs/configuration.`);
|
|
383
|
+
}
|
|
379
384
|
return {
|
|
380
385
|
url: gmfauthUrl,
|
|
381
386
|
audience: config.gmfauth.audience,
|
|
382
387
|
loginRequired: config.gmfauth.loginRequired ?? false,
|
|
383
388
|
checkSessionOnLoad: config.gmfauth.checkSessionOnLoad ?? true,
|
|
384
389
|
authMode: 'cookie',
|
|
390
|
+
loginMode: config.gmfauth.loginMode ?? 'form',
|
|
385
391
|
alwaysSendCookies: config.gmfauth.alwaysSendCookies ?? false,
|
|
386
392
|
refererPolicy: config.gmfauth.refererPolicy ?? 'strict-origin-when-cross-origin',
|
|
387
393
|
audienceExcludedPaths: config.gmfauth.audienceExcludedPaths ?? []
|
|
@@ -103,6 +103,10 @@ export default class DrawingFeature {
|
|
|
103
103
|
getAzimuthText(circle: CircleGeom): string;
|
|
104
104
|
isPointPolylineOrText(): boolean;
|
|
105
105
|
getVertexStyle(activeNode?: boolean): Style;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a new DrawingFeature from an ol feature
|
|
108
|
+
*/
|
|
109
|
+
static createFromOlFeature(shape: DrawingShape, olFeature: Feature<Geometry>, context: IGirafeContext, drawingState: DrawingState): DrawingFeature;
|
|
106
110
|
static deserialize(serializedFeature: SerializedFeature, context: IGirafeContext, drawingState: DrawingState): DrawingFeature;
|
|
107
111
|
static circleToPolygon(center: number[], radius: number, nbEdges?: number): number[][];
|
|
108
112
|
static geojsonFromOlFeature(olFeature: Feature<Geometry>, shapeType: DrawingShape): object;
|
|
@@ -233,6 +233,15 @@ export default class DrawingFeature {
|
|
|
233
233
|
})
|
|
234
234
|
});
|
|
235
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Creates a new DrawingFeature from an ol feature
|
|
238
|
+
*/
|
|
239
|
+
static createFromOlFeature(shape, olFeature, context, drawingState) {
|
|
240
|
+
const newFeature = new DrawingFeature(shape, drawingState, context.configManager.Config.drawing);
|
|
241
|
+
newFeature.geojson = DrawingFeature.geojsonFromOlFeature(olFeature, shape);
|
|
242
|
+
newFeature.addToState();
|
|
243
|
+
return newFeature;
|
|
244
|
+
}
|
|
236
245
|
static deserialize(serializedFeature, context, drawingState) {
|
|
237
246
|
const newFeature = new DrawingFeature(serializedFeature.t, drawingState, context.configManager.Config.drawing);
|
|
238
247
|
newFeature.geojson = serializedFeature.g;
|
|
@@ -17,6 +17,8 @@ declare class LocalFileManager extends GirafeSingleton {
|
|
|
17
17
|
private createInteraction;
|
|
18
18
|
loadLocalFile(localFile: File): Promise<void>;
|
|
19
19
|
private loadLocalFileFeatures;
|
|
20
|
+
convertToDrawing(layer: LayerLocalFile): void;
|
|
21
|
+
private splitIntoSimpleGeometries;
|
|
20
22
|
private handleUnsupportedFiles;
|
|
21
23
|
private validateAndCompleteFeatures;
|
|
22
24
|
addLayer(layerFile: LayerLocalFile): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
3
|
+
import { Feature } from 'ol';
|
|
3
4
|
import DragAndDrop from 'ol/interaction/DragAndDrop.js';
|
|
4
5
|
import { GPX, GeoJSON, IGC, KML, TopoJSON } from 'ol/format.js';
|
|
5
6
|
import { Vector as VectorLayer } from 'ol/layer.js';
|
|
@@ -7,6 +8,8 @@ import { Vector as VectorSource } from 'ol/source.js';
|
|
|
7
8
|
import LayerLocalFile from '../../models/layers/layerlocalfile.js';
|
|
8
9
|
import { extend, intersects } from 'ol/extent.js';
|
|
9
10
|
import { applyFeaturesToSelection } from '../utils/utils.js';
|
|
11
|
+
import DrawingFeature, { DrawingShape } from '../drawing/drawingFeature.js';
|
|
12
|
+
const DrawingStateLocation = 'drawing';
|
|
10
13
|
class LocalFileManager extends GirafeSingleton {
|
|
11
14
|
map;
|
|
12
15
|
name;
|
|
@@ -76,11 +79,50 @@ class LocalFileManager extends GirafeSingleton {
|
|
|
76
79
|
if (features && features.length > acceptableFeatures.features.length) {
|
|
77
80
|
// Some features are outer extent
|
|
78
81
|
layer.hasError = true;
|
|
79
|
-
layer.errorMessage =
|
|
80
|
-
|
|
82
|
+
layer.errorMessage = this.context.i18nManager
|
|
83
|
+
.getTranslation('only-n-features-loaded')
|
|
84
|
+
.replace('_loadedCount_', String(acceptableFeatures.features.length))
|
|
85
|
+
.replace('_totalCount_', String(features.length));
|
|
81
86
|
}
|
|
82
87
|
this.context.userLayerManager.addUserLayerToTree(layer);
|
|
83
88
|
}
|
|
89
|
+
convertToDrawing(layer) {
|
|
90
|
+
const drawingState = this.context.stateManager.state.extendedState[DrawingStateLocation];
|
|
91
|
+
for (const feature of layer._features) {
|
|
92
|
+
const geometry = feature.getGeometry();
|
|
93
|
+
if (!geometry)
|
|
94
|
+
continue;
|
|
95
|
+
for (const { geometry: simpleGeometry, shape } of this.splitIntoSimpleGeometries(geometry)) {
|
|
96
|
+
const drawingFeature = DrawingFeature.createFromOlFeature(shape, new Feature(simpleGeometry), this.context, drawingState);
|
|
97
|
+
drawingFeature.displayName = false;
|
|
98
|
+
drawingFeature.measureInformation = 'none';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.context.userLayerManager.removeUserLayerFromTree(layer);
|
|
102
|
+
}
|
|
103
|
+
splitIntoSimpleGeometries(geometry) {
|
|
104
|
+
switch (geometry.getType()) {
|
|
105
|
+
case 'Point':
|
|
106
|
+
return [{ geometry, shape: DrawingShape.Point }];
|
|
107
|
+
case 'LineString':
|
|
108
|
+
return [{ geometry, shape: DrawingShape.Polyline }];
|
|
109
|
+
case 'Polygon':
|
|
110
|
+
return [{ geometry, shape: DrawingShape.Polygon }];
|
|
111
|
+
case 'MultiPoint':
|
|
112
|
+
return geometry.getPoints().map((g) => ({ geometry: g, shape: DrawingShape.Point }));
|
|
113
|
+
case 'MultiLineString':
|
|
114
|
+
return geometry
|
|
115
|
+
.getLineStrings()
|
|
116
|
+
.map((g) => ({ geometry: g, shape: DrawingShape.Polyline }));
|
|
117
|
+
case 'MultiPolygon':
|
|
118
|
+
return geometry.getPolygons().map((g) => ({ geometry: g, shape: DrawingShape.Polygon }));
|
|
119
|
+
case 'GeometryCollection':
|
|
120
|
+
return geometry.getGeometries().flatMap((g) => this.splitIntoSimpleGeometries(g));
|
|
121
|
+
default:
|
|
122
|
+
// Unsupported geometry type
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
84
126
|
handleUnsupportedFiles(dropEvent) {
|
|
85
127
|
if (!this.context.userInteractionManager.canListenerExecute('map.drop', this.name))
|
|
86
128
|
return;
|