@geogirafe/lib-geoportal 1.2.0-dev.2736532702 → 1.2.0-dev.2751114005
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/LICENSE +1 -0
- 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/assets/icons/share-native.svg +1 -0
- package/assets/icons/text-font.svg +1 -0
- package/components/drawing/component-mobile.js +1 -1
- package/components/drawing/component.d.ts +8 -1
- package/components/drawing/component.js +61 -6
- package/components/main.d.ts +1 -0
- package/components/main.js +1 -0
- package/components/menu-buttons-mobile/MenuMobile3dButton.js +1 -1
- package/components/menu-buttons-mobile/MenuMobileDrawingButton.js +1 -1
- package/components/menu-buttons-mobile/MenuMobileOfflineButton.js +1 -1
- package/components/menu-buttons-mobile/MenuMobileShareButton.d.ts +11 -0
- package/components/menu-buttons-mobile/MenuMobileShareButton.js +32 -0
- package/components/menu-buttons-mobile/style.css +3 -0
- package/components/menu-mobile/component.js +1 -1
- package/components/menu-mobile/style.css +4 -0
- package/components/share/component.js +4 -4
- package/components/share-mobile/component.d.ts +28 -0
- package/components/share-mobile/component.js +121 -0
- package/components/share-mobile/style.css +56 -0
- package/components/treeview/treeviewitem/component.js +9 -2
- package/models/layers/layerdrawing.d.ts +1 -0
- package/models/layers/layerdrawing.js +2 -1
- package/package.json +1 -1
- package/templates/mobile.html +2 -0
- package/templates/public/about.json +1 -1
- package/tools/app/geogirafeapp-mobile.js +4 -0
- package/tools/drawing/cesiumDrawing.d.ts +7 -0
- package/tools/drawing/cesiumDrawing.js +24 -1
- package/tools/drawing/drawingFeature.d.ts +3 -2
- package/tools/drawing/drawingFeature.js +5 -2
- package/tools/drawing/olDrawing.d.ts +41 -0
- package/tools/drawing/olDrawing.js +170 -15
- package/tools/state/graphicalInterface.d.ts +1 -1
- /package/{components/share/images → assets/icons}/facebook.svg +0 -0
- /package/{components/share/images → assets/icons}/linkedin.svg +0 -0
- /package/{components/share/images/mail.svg → assets/icons/share-mail.svg} +0 -0
- /package/{components/share/images → assets/icons}/twitter.svg +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { html as uHtml } from 'uhtml';
|
|
2
|
+
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
3
|
+
import FacebookLogo from '../../assets/icons/facebook.svg';
|
|
4
|
+
import TwitterLogo from '../../assets/icons/twitter.svg';
|
|
5
|
+
import LinkedInLogo from '../../assets/icons/linkedin.svg';
|
|
6
|
+
import MailLogo from '../../assets/icons/share-mail.svg';
|
|
7
|
+
import ShareNativeLogo from '../../assets/icons/share-native.svg';
|
|
8
|
+
async function shareUsingNavigator(url, title) {
|
|
9
|
+
const shareData = {
|
|
10
|
+
title: title ?? document.title,
|
|
11
|
+
text: 'Schau dir diese Seite an:',
|
|
12
|
+
url: url
|
|
13
|
+
};
|
|
14
|
+
if (!navigator.share) {
|
|
15
|
+
await fallbackShare(shareData.url ?? window.location.href);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await navigator.share(shareData);
|
|
20
|
+
console.log('Inhalt wurde geteilt.');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
24
|
+
// Benutzer hat den Share-Dialog geschlossen.
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.error('Teilen fehlgeschlagen:', error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function fallbackShare(url) {
|
|
31
|
+
try {
|
|
32
|
+
await navigator.clipboard.writeText(url);
|
|
33
|
+
alert('Der Link wurde in die Zwischenablage kopiert.');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Einfachster Fallback für ältere Browser.
|
|
37
|
+
window.prompt('Link kopieren:', url);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
class ShareMobileComponent extends GirafeHTMLElement {
|
|
41
|
+
templateUrl = null;
|
|
42
|
+
styleUrls = null;
|
|
43
|
+
template = () => {
|
|
44
|
+
return uHtml `<style>
|
|
45
|
+
.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}}
|
|
46
|
+
</style><style>
|
|
47
|
+
#container{width:calc(100% - 50px);margin:25px}#title{color:#3f68a7;text-transform:uppercase;text-align:center;margin:.5em 0;font-size:1.8em;font-weight:600}.qrcode-container{flex-direction:column;align-items:center;display:flex}.qrcode{width:calc(100% - 50px - 2rem);display:block}.qrcode-placeholder{opacity:.7;content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=);width:calc(100% - 50px - 2rem);height:calc(100% - 50px - 2rem);display:block}.actions-container{flex-direction:row;flex-grow:1;justify-content:center;column-gap:.5rem;width:stretch;height:3.25rem;margin-top:1rem;display:flex;& button{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:4px;flex-direction:column;padding:.25rem;display:flex;& img{width:2.5rem;height:2.5rem}}}
|
|
48
|
+
</style>
|
|
49
|
+
<style>${this.customStyle}</style>
|
|
50
|
+
<div id="container" style="${this.state.interface.swipeupPanelContent === 'share' ? '' : 'display: none;'}"><div id="title" i18n="Share this map">Share this map</div><div id="share-map" class="${this.urlShortener ? 'group' : 'hidden'}"><div class="${this.awaitingShareLink ? 'disabled qrcode-container' : 'enabled qrcode-container'}"><img alt="share-qrcode" class="${(this.qrCode ? 'qrcode' : 'qrcode-placeholder')}" src="${this.qrCode}"><div class="${(this.success ? 'hidden' : 'qrcode error')}"><img alt="error-icon" src="icons/error.svg"> <span i18n="Error, can not fetch the shortener service. Please try again later.">Error, can not fetch the shortener service. Please try again later.</span></div><div class="${this.awaitingShareLink ? 'disabled actions-container' : 'enabled actions-container'}"><button onclick="${() => this.shareFacebook()}"><img alt="share-facebook" src="${this.facebookLogo}"></button> <button onclick="${() => this.shareTwitter()}"><img alt="share-twitter" src="${this.twitterLogo}"></button> <button onclick="${() => this.shareLinkedIn()}"><img alt="share-linkedin" src="${this.linkedInLogo}"></button> <button onclick="${() => this.shareMail()}"><img alt="share-mail" src="${this.mailLogo}"></button> <button onclick="${() => this.shareNative()}"><img alt="share-native" src="${this.shareNativeLogo}"></button></div></div></div></div>
|
|
51
|
+
${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
52
|
+
};
|
|
53
|
+
shareLink;
|
|
54
|
+
qrCode;
|
|
55
|
+
success = true;
|
|
56
|
+
twitterLogo = TwitterLogo;
|
|
57
|
+
facebookLogo = FacebookLogo;
|
|
58
|
+
linkedInLogo = LinkedInLogo;
|
|
59
|
+
mailLogo = MailLogo;
|
|
60
|
+
shareNativeLogo = ShareNativeLogo;
|
|
61
|
+
awaitingShareLink = false;
|
|
62
|
+
get urlShortener() {
|
|
63
|
+
return this.context.shareManager.UrlShortener;
|
|
64
|
+
}
|
|
65
|
+
constructor() {
|
|
66
|
+
super('share-mobile');
|
|
67
|
+
}
|
|
68
|
+
render() {
|
|
69
|
+
super.render();
|
|
70
|
+
}
|
|
71
|
+
connectedCallback() {
|
|
72
|
+
super.connectedCallback();
|
|
73
|
+
this.render();
|
|
74
|
+
super.girafeTranslate();
|
|
75
|
+
this.registerEvents();
|
|
76
|
+
// Generate the share link when the component is rendered (but don't copy to clipboard)
|
|
77
|
+
this.generateShareLink().then(() => this.refreshRender());
|
|
78
|
+
}
|
|
79
|
+
registerEvents() {
|
|
80
|
+
this.subscribe('interface.swipeupPanelContent', () => {
|
|
81
|
+
this.render();
|
|
82
|
+
});
|
|
83
|
+
this.subscribe('position', () => {
|
|
84
|
+
super.render();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async generateShareLink() {
|
|
88
|
+
if (!this.urlShortener) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.awaitingShareLink = true;
|
|
92
|
+
this.refreshRender();
|
|
93
|
+
const baseUrl = this.context.urlManager.getBaseUrlPath();
|
|
94
|
+
const hash = this.context.shareManager.getStateToShare();
|
|
95
|
+
// Get short URL
|
|
96
|
+
const longurl = `${baseUrl}#${hash}`;
|
|
97
|
+
const response = await this.urlShortener.shortenUrl(longurl);
|
|
98
|
+
this.shareLink = response.shorturl;
|
|
99
|
+
this.success = response.success;
|
|
100
|
+
this.qrCode = response.qrcode;
|
|
101
|
+
this.awaitingShareLink = false;
|
|
102
|
+
}
|
|
103
|
+
shareFacebook() {
|
|
104
|
+
window.open('https://www.facebook.com/sharer/sharer.php?u=' + this.shareLink, '_blank');
|
|
105
|
+
}
|
|
106
|
+
shareTwitter() {
|
|
107
|
+
window.open('https://twitter.com/intent/tweet?url=' + this.shareLink, '_blank');
|
|
108
|
+
}
|
|
109
|
+
shareLinkedIn() {
|
|
110
|
+
window.open('https://www.linkedin.com/shareArticle?url=' + encodeURIComponent(this.shareLink), '_blank');
|
|
111
|
+
}
|
|
112
|
+
shareMail() {
|
|
113
|
+
const subject = encodeURIComponent('Check out this link');
|
|
114
|
+
const body = encodeURIComponent('I thought you might be interested in this link: ' + this.shareLink);
|
|
115
|
+
window.location.href = 'mailto:?subject=' + subject + '&body=' + body;
|
|
116
|
+
}
|
|
117
|
+
async shareNative() {
|
|
118
|
+
await shareUsingNavigator(this.shareLink, 'Geogirafe');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export default ShareMobileComponent;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: Apache-2.0 */
|
|
2
|
+
#container {
|
|
3
|
+
width: calc(100% - 50px);
|
|
4
|
+
margin: 25px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
#title {
|
|
8
|
+
color: #3f68a7;
|
|
9
|
+
text-transform: uppercase;
|
|
10
|
+
text-align: center;
|
|
11
|
+
margin: 0.5em 0;
|
|
12
|
+
font-size: 1.8em;
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.qrcode-container {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.qrcode {
|
|
23
|
+
display: block;
|
|
24
|
+
width: calc(100% - 50px - 2rem);
|
|
25
|
+
}
|
|
26
|
+
.qrcode-placeholder {
|
|
27
|
+
display: block;
|
|
28
|
+
width: calc(100% - 50px - 2rem);
|
|
29
|
+
height: calc(100% - 50px - 2rem);
|
|
30
|
+
opacity: 0.7;
|
|
31
|
+
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.actions-container {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: row;
|
|
37
|
+
flex-grow: 1;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
width: stretch;
|
|
40
|
+
column-gap: 0.5rem;
|
|
41
|
+
margin-top: 1rem;
|
|
42
|
+
height: 3.25rem;
|
|
43
|
+
|
|
44
|
+
button {
|
|
45
|
+
background-color: white;
|
|
46
|
+
border-radius: 4px;
|
|
47
|
+
filter: drop-shadow(0px 4px 7px #0004382c);
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
padding: 0.25rem;
|
|
51
|
+
img {
|
|
52
|
+
width: 2.5rem;
|
|
53
|
+
height: 2.5rem;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -8,6 +8,7 @@ import LayerWmts from '../../../models/layers/layerwmts.js';
|
|
|
8
8
|
import TreeViewElement from '../tools/treeviewelement.js';
|
|
9
9
|
import { isSnappableLayer } from '../../../models/layers/snappablelayer.js';
|
|
10
10
|
import WmsLegendHelper from '../../../tools/wms/wmslegendhelper.js';
|
|
11
|
+
import LayerDrawing from '../../../models/layers/layerdrawing.js';
|
|
11
12
|
class TreeViewItemComponent extends TreeViewElement {
|
|
12
13
|
templateUrl = null;
|
|
13
14
|
styleUrls = null;
|
|
@@ -224,8 +225,14 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
|
224
225
|
this.context.mapManager.zoomToExtent(this.layer.extent);
|
|
225
226
|
}
|
|
226
227
|
deleteLayer() {
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
if (this.layer instanceof LayerDrawing) {
|
|
229
|
+
this.layer.removalRequested = true;
|
|
230
|
+
this.context.stateManager.state.interface.drawingPanelVisible = true;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
this.context.layerManager.toggleLayer(this.layer, 'off');
|
|
234
|
+
this.removeFromParent();
|
|
235
|
+
}
|
|
229
236
|
}
|
|
230
237
|
connectedCallback() {
|
|
231
238
|
super.connectedCallback();
|
|
@@ -9,9 +9,10 @@ import Layer from './layer.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export default class LayerDrawing extends Layer {
|
|
11
11
|
_oLayer;
|
|
12
|
+
removalRequested = false;
|
|
12
13
|
constructor(name, oLayer) {
|
|
13
14
|
super(0, name, 0, { isDefaultChecked: true });
|
|
14
|
-
this.isRemovable =
|
|
15
|
+
this.isRemovable = true;
|
|
15
16
|
this.isSwipeable = false;
|
|
16
17
|
this.isDraggable = false;
|
|
17
18
|
this._oLayer = oLayer;
|
package/package.json
CHANGED
package/templates/mobile.html
CHANGED
|
@@ -106,9 +106,11 @@
|
|
|
106
106
|
<girafe-menu-mobile-offline-button slot="mobile-menu-content"></girafe-menu-mobile-offline-button>
|
|
107
107
|
<girafe-menu-mobile-drawing-button slot="mobile-menu-content"></girafe-menu-mobile-drawing-button>
|
|
108
108
|
<girafe-oauth-mobile slot="mobile-menu-content"></girafe-oauth-mobile>
|
|
109
|
+
<girafe-menu-mobile-share-button slot="mobile-menu-content"></girafe-menu-mobile-share-button>
|
|
109
110
|
</girafe-menu-mobile>
|
|
110
111
|
|
|
111
112
|
<girafe-offline slot="main"></girafe-offline>
|
|
113
|
+
<girafe-share-mobile slot="main"></girafe-share-mobile>
|
|
112
114
|
</girafe-swipe-up-panel>
|
|
113
115
|
</girafe-app>
|
|
114
116
|
</body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.2.0-dev.
|
|
1
|
+
{"version":"1.2.0-dev.2751114005", "build":"2751114005", "date":"11/08/2026"}
|
|
@@ -24,6 +24,8 @@ import MobileGroupElementComponent from '../../components/themes-mobile/group/co
|
|
|
24
24
|
import MobileLayerElementComponent from '../../components/themes-mobile/layer/component.js';
|
|
25
25
|
import MobileSelectedLayerElementComponent from '../../components/themes-mobile/layer-selected/component.js';
|
|
26
26
|
import GetDirectionsArtifact from '../../components/getdirections/component.js';
|
|
27
|
+
import MenuMobileShareButton from '../../components/menu-buttons-mobile/MenuMobileShareButton.js';
|
|
28
|
+
import ShareMobileComponent from '../../components/share-mobile/component.js';
|
|
27
29
|
export default class GeoGirafeAppMobile extends GeoGirafeApp {
|
|
28
30
|
initializeInterface() {
|
|
29
31
|
if (window.cordova) {
|
|
@@ -57,6 +59,7 @@ export default class GeoGirafeAppMobile extends GeoGirafeApp {
|
|
|
57
59
|
customElements.define('girafe-drawing-mobile', DrawingComponentMobile);
|
|
58
60
|
customElements.define('girafe-drawing-container-mobile', DrawingContainerMobile);
|
|
59
61
|
customElements.define('girafe-menu-mobile-3d-button', MenuMobile3dButton);
|
|
62
|
+
customElements.define('girafe-menu-mobile-share-button', MenuMobileShareButton);
|
|
60
63
|
customElements.define('girafe-menu-mobile-offline-button', MenuMobileOfflineButton);
|
|
61
64
|
customElements.define('girafe-menu-mobile-drawing-button', MenuMobileDrawingButton);
|
|
62
65
|
customElements.define('girafe-oauth-mobile', MobileOauthComponent);
|
|
@@ -65,5 +68,6 @@ export default class GeoGirafeAppMobile extends GeoGirafeApp {
|
|
|
65
68
|
customElements.define('girafe-layer', MobileLayerElementComponent);
|
|
66
69
|
customElements.define('girafe-layer-selected', MobileSelectedLayerElementComponent);
|
|
67
70
|
customElements.define('girafe-get-directions', GetDirectionsArtifact);
|
|
71
|
+
customElements.define('girafe-share-mobile', ShareMobileComponent);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -9,12 +9,19 @@ export default class CesiumDrawing {
|
|
|
9
9
|
private handler;
|
|
10
10
|
private entities;
|
|
11
11
|
private fixedLineLength;
|
|
12
|
+
private drawingText;
|
|
12
13
|
private readonly context;
|
|
13
14
|
private get state();
|
|
14
15
|
private get drawingState();
|
|
15
16
|
private get config();
|
|
16
17
|
constructor(map: MapComponent, stateLocation: string, context: IGirafeContext);
|
|
17
18
|
setFixedLineLength(length: number): void;
|
|
19
|
+
setDrawingText(text: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* A text shape displays the text entered in the tool parameters instead of its generated default name.
|
|
22
|
+
* If no text has been entered, the default name is kept so that the shape stays visible on the globe.
|
|
23
|
+
*/
|
|
24
|
+
private getTextOf;
|
|
18
25
|
private activateTool;
|
|
19
26
|
private deactivateTool;
|
|
20
27
|
private pickOnGlobe;
|
|
@@ -15,6 +15,7 @@ export default class CesiumDrawing {
|
|
|
15
15
|
handler = undefined;
|
|
16
16
|
entities = undefined;
|
|
17
17
|
fixedLineLength = 0;
|
|
18
|
+
drawingText = '';
|
|
18
19
|
context;
|
|
19
20
|
get state() {
|
|
20
21
|
return this.context.stateManager.state;
|
|
@@ -40,6 +41,16 @@ export default class CesiumDrawing {
|
|
|
40
41
|
setFixedLineLength(length) {
|
|
41
42
|
this.fixedLineLength = Number.isNaN(length) ? 0 : length;
|
|
42
43
|
}
|
|
44
|
+
setDrawingText(text) {
|
|
45
|
+
this.drawingText = text;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A text shape displays the text entered in the tool parameters instead of its generated default name.
|
|
49
|
+
* If no text has been entered, the default name is kept so that the shape stays visible on the globe.
|
|
50
|
+
*/
|
|
51
|
+
getTextOf(feature) {
|
|
52
|
+
return this.drawingText.trim().length > 0 ? this.drawingText : feature.name;
|
|
53
|
+
}
|
|
43
54
|
activateTool(tool) {
|
|
44
55
|
if (!this.canExecute('globe.draw')) {
|
|
45
56
|
return;
|
|
@@ -83,6 +94,9 @@ export default class CesiumDrawing {
|
|
|
83
94
|
const newCesiumEntities = this.getShapes(tool, newCesiumEntityPoints, new DrawingFeature(tool, this.drawingState, this.config.drawing));
|
|
84
95
|
this.activeShapes.forEach((e) => this.entities.remove(e));
|
|
85
96
|
const newFeature = new DrawingFeature(tool, this.drawingState, this.config.drawing);
|
|
97
|
+
if (tool === DrawingShape.Text) {
|
|
98
|
+
newFeature.name = this.getTextOf(newFeature);
|
|
99
|
+
}
|
|
86
100
|
// The following code will be useful when we will remove OLCesium
|
|
87
101
|
/*newCesiumEntities.forEach((e) => this.entities!.add(e));
|
|
88
102
|
const updateStyle = (feature:DrawingFeature) => {
|
|
@@ -164,7 +178,7 @@ export default class CesiumDrawing {
|
|
|
164
178
|
this.activeShapes.forEach((e) => this.entities.add(e));
|
|
165
179
|
}
|
|
166
180
|
// Tools that automatically terminate the shape after a fixed number of points
|
|
167
|
-
if ((tool === DrawingShape.Point && this.activeShapePoints.length === 2) ||
|
|
181
|
+
if (((tool === DrawingShape.Point || tool === DrawingShape.Text) && this.activeShapePoints.length === 2) ||
|
|
168
182
|
(tool === DrawingShape.Disk && this.activeShapePoints.length === 3) ||
|
|
169
183
|
(tool === DrawingShape.Square && this.activeShapePoints.length === 3) ||
|
|
170
184
|
(tool === DrawingShape.Rectangle && this.activeShapePoints.length === 3)) {
|
|
@@ -221,6 +235,15 @@ export default class CesiumDrawing {
|
|
|
221
235
|
label: this.createLabel(feature.getCoordText(this.getPosition(pos[0])), font)
|
|
222
236
|
})
|
|
223
237
|
];
|
|
238
|
+
case DrawingShape.Text:
|
|
239
|
+
return [
|
|
240
|
+
new Entity({
|
|
241
|
+
position: pos[0],
|
|
242
|
+
// The anchor point is needed to export the shape as a KML placemark
|
|
243
|
+
point: this.createPoint(strokeColor),
|
|
244
|
+
label: this.createLabel(this.getTextOf(feature), font)
|
|
245
|
+
})
|
|
246
|
+
];
|
|
224
247
|
case DrawingShape.Polyline:
|
|
225
248
|
return [
|
|
226
249
|
new Entity({ polyline: this.getPolyline(pos, feature) }),
|
|
@@ -12,7 +12,8 @@ export declare enum DrawingShape {
|
|
|
12
12
|
Rectangle = 4,
|
|
13
13
|
Disk = 5,
|
|
14
14
|
FreehandPolyline = 6,
|
|
15
|
-
FreehandPolygon = 7
|
|
15
|
+
FreehandPolygon = 7,
|
|
16
|
+
Text = 8
|
|
16
17
|
}
|
|
17
18
|
export type ArrowStyle = 'none' | 'start' | 'end' | 'both';
|
|
18
19
|
export type ArrowPosition = 'whole' | 'each' | 'mid';
|
|
@@ -100,7 +101,7 @@ export default class DrawingFeature {
|
|
|
100
101
|
getAreaText(area: number): string;
|
|
101
102
|
getCoordText(coord: number[]): string;
|
|
102
103
|
getAzimuthText(circle: CircleGeom): string;
|
|
103
|
-
|
|
104
|
+
isPointPolylineOrText(): boolean;
|
|
104
105
|
getVertexStyle(activeNode?: boolean): Style;
|
|
105
106
|
static deserialize(serializedFeature: SerializedFeature, context: IGirafeContext, drawingState: DrawingState): DrawingFeature;
|
|
106
107
|
static circleToPolygon(center: number[], radius: number, nbEdges?: number): number[][];
|
|
@@ -8,6 +8,7 @@ import GeoJSON from 'ol/format/GeoJSON.js';
|
|
|
8
8
|
import { getAreaAsMetricText, getAzimuthAsText, getLengthAsMetricText } from '../utils/olutils.js';
|
|
9
9
|
import { fromCircle } from 'ol/geom/Polygon.js';
|
|
10
10
|
import LineString from 'ol/geom/LineString.js';
|
|
11
|
+
// New shapes must be appended at the end, as the numeric values are used to serialize features.
|
|
11
12
|
export var DrawingShape;
|
|
12
13
|
(function (DrawingShape) {
|
|
13
14
|
DrawingShape[DrawingShape["Point"] = 0] = "Point";
|
|
@@ -18,6 +19,7 @@ export var DrawingShape;
|
|
|
18
19
|
DrawingShape[DrawingShape["Disk"] = 5] = "Disk";
|
|
19
20
|
DrawingShape[DrawingShape["FreehandPolyline"] = 6] = "FreehandPolyline";
|
|
20
21
|
DrawingShape[DrawingShape["FreehandPolygon"] = 7] = "FreehandPolygon";
|
|
22
|
+
DrawingShape[DrawingShape["Text"] = 8] = "Text";
|
|
21
23
|
})(DrawingShape || (DrawingShape = {}));
|
|
22
24
|
export default class DrawingFeature {
|
|
23
25
|
static geoJSONFormat = new GeoJSON();
|
|
@@ -206,10 +208,11 @@ export default class DrawingFeature {
|
|
|
206
208
|
getAzimuthText(circle) {
|
|
207
209
|
return getAzimuthAsText(this.measureInformation === 'none' ? undefined : (circle.getProperties()['azimuth'] ?? undefined));
|
|
208
210
|
}
|
|
209
|
-
|
|
211
|
+
isPointPolylineOrText() {
|
|
210
212
|
return (this.type === DrawingShape.Point ||
|
|
211
213
|
this.type === DrawingShape.Polyline ||
|
|
212
|
-
this.type === DrawingShape.FreehandPolyline
|
|
214
|
+
this.type === DrawingShape.FreehandPolyline ||
|
|
215
|
+
this.type === DrawingShape.Text);
|
|
213
216
|
}
|
|
214
217
|
getVertexStyle(activeNode = false) {
|
|
215
218
|
const defaultConfig = this.defaultDrawingConfig;
|
|
@@ -21,12 +21,16 @@ export default class OlDrawing {
|
|
|
21
21
|
private fixedSquareSide;
|
|
22
22
|
private fixedRectangleWidth;
|
|
23
23
|
private fixedRectangleHeight;
|
|
24
|
+
private drawingText;
|
|
24
25
|
readonly drawingSource: VectorSource;
|
|
25
26
|
readonly drawingLayer: VectorLayer;
|
|
26
27
|
private lastClosestFeature;
|
|
27
28
|
private translate;
|
|
28
29
|
private transform;
|
|
29
30
|
private readonly modifyFeatureChangeListeners;
|
|
31
|
+
private hoverListener;
|
|
32
|
+
private selectListener;
|
|
33
|
+
private hoveredTextFeatureId;
|
|
30
34
|
defaultStyle: StyleFunction | undefined;
|
|
31
35
|
private readonly updateGeometryInState;
|
|
32
36
|
private get state();
|
|
@@ -46,6 +50,22 @@ export default class OlDrawing {
|
|
|
46
50
|
private addEditContextMenu;
|
|
47
51
|
private addEditInteractions;
|
|
48
52
|
private removeEditInteractions;
|
|
53
|
+
private addHoverListener;
|
|
54
|
+
private removeHoverListener;
|
|
55
|
+
private addSelectListener;
|
|
56
|
+
private removeSelectListener;
|
|
57
|
+
/**
|
|
58
|
+
* Lets the user select the shapes to edit directly in the map, as an alternative to the drawing list:
|
|
59
|
+
* a click selects the shape under the cursor exclusively, a click with the platform modifier key (ctrl / meta)
|
|
60
|
+
* adds the shape to the current selection or removes it from it. A click outside of any shape clears the selection.
|
|
61
|
+
*/
|
|
62
|
+
private selectClickedFeature;
|
|
63
|
+
/**
|
|
64
|
+
* Only writes to the state if the selection of the shape really changed, so that the map and the drawing
|
|
65
|
+
* panel are not refreshed for the shapes that are not concerned by the click.
|
|
66
|
+
*/
|
|
67
|
+
private setFeatureSelected;
|
|
68
|
+
private findFeatureIdAtPixel;
|
|
49
69
|
private readonly prepareMenuEntriesForVertex;
|
|
50
70
|
private readonly prepareMenuEntriesForShape;
|
|
51
71
|
/**
|
|
@@ -98,6 +118,12 @@ export default class OlDrawing {
|
|
|
98
118
|
setFixedSquareSide(length: number): void;
|
|
99
119
|
setFixedRectangleWidth(width: number): void;
|
|
100
120
|
setFixedRectangleHeight(height: number): void;
|
|
121
|
+
setDrawingText(text: string): void;
|
|
122
|
+
/**
|
|
123
|
+
* A text shape displays the text entered in the tool parameters instead of its generated default name.
|
|
124
|
+
* If no text has been entered, the default name is kept so that the shape stays visible on the map.
|
|
125
|
+
*/
|
|
126
|
+
private applyDrawingText;
|
|
101
127
|
private createLineStringFixedLength;
|
|
102
128
|
private createSquareFixedLength;
|
|
103
129
|
private createPolygonFixedLength;
|
|
@@ -106,11 +132,24 @@ export default class OlDrawing {
|
|
|
106
132
|
private addDrawInteraction;
|
|
107
133
|
centerViewOnFeature(drawingFeature: DrawingFeature): void;
|
|
108
134
|
private getStyle;
|
|
135
|
+
/**
|
|
136
|
+
* Vertices are rendered on the shapes selected for editing. A text shape is an exception: as its geometry is
|
|
137
|
+
* a single anchor point hidden behind the text, its vertex is only revealed while the cursor hovers the text.
|
|
138
|
+
*/
|
|
139
|
+
private shouldRenderVertices;
|
|
109
140
|
private removeDrawInteraction;
|
|
110
141
|
private removeModifyInteraction;
|
|
111
142
|
private getDrawingShapeType;
|
|
112
143
|
private isSquareOrRectangle;
|
|
144
|
+
private isSquareRectangleOrText;
|
|
113
145
|
private shouldAllowVertexInsertionAtEvent;
|
|
146
|
+
/**
|
|
147
|
+
* A text shape is only made of its text: its anchor point is hidden behind it. To let the user know where to
|
|
148
|
+
* grab the shape, the anchor vertex is revealed as long as the cursor hovers the text.
|
|
149
|
+
*/
|
|
150
|
+
private updateHoveredTextFeature;
|
|
151
|
+
private setHoveredTextFeature;
|
|
152
|
+
private findHoveredTextFeatureIdAtPixel;
|
|
114
153
|
private getHoveredModifiableFeatureAtPixel;
|
|
115
154
|
private applyScaleRotateOnModifyStyle;
|
|
116
155
|
private clearModifyFeatureChangeListeners;
|
|
@@ -119,5 +158,7 @@ export default class OlDrawing {
|
|
|
119
158
|
registerInteractions(): void;
|
|
120
159
|
unregisterInteractions(): void;
|
|
121
160
|
private canExecute;
|
|
161
|
+
private disableContextMenu;
|
|
162
|
+
private enableContextMenu;
|
|
122
163
|
private fixLastLength;
|
|
123
164
|
}
|