@geogirafe/lib-geoportal 1.2.0-dev.2729313771 → 1.2.0-dev.2750042516
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 +1 -0
- package/assets/i18n/en.json +1 -0
- package/assets/i18n/fr.json +1 -0
- package/assets/i18n/it.json +1 -0
- package/assets/icons/share-native.svg +1 -0
- package/components/drawing/component.js +12 -10
- 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/selectiontool/component.js +2 -2
- 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/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/drawingFeature.d.ts +9 -0
- package/tools/drawing/drawingFeature.js +44 -4
- package/tools/drawing/olDrawing.js +5 -4
- package/tools/state/graphicalInterface.d.ts +1 -1
- package/tools/utils/olutils.d.ts +2 -2
- package/tools/utils/olutils.js +3 -2
- /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
package/assets/i18n/de.json
CHANGED
|
@@ -488,6 +488,7 @@
|
|
|
488
488
|
"Send message": "Nachricht senden",
|
|
489
489
|
"Settings": "Einstellungen",
|
|
490
490
|
"Shadows": "Schatten",
|
|
491
|
+
"Share map": "Karte teilen",
|
|
491
492
|
"Share this map": "Diese Karte teilen",
|
|
492
493
|
"share-panel": "Karte teilen oder einbetten",
|
|
493
494
|
"Show metadata": "Metadaten anzeigen",
|
package/assets/i18n/en.json
CHANGED
package/assets/i18n/fr.json
CHANGED
|
@@ -488,6 +488,7 @@
|
|
|
488
488
|
"Send message": "Envoyer un message",
|
|
489
489
|
"Settings": "Paramètres",
|
|
490
490
|
"Shadows": "Ombres",
|
|
491
|
+
"Share map": "Partager la carte",
|
|
491
492
|
"Share this map": "Partager cette carte",
|
|
492
493
|
"share-panel": "Partager ou intégrer la carte",
|
|
493
494
|
"Show metadata": "Afficher les métadonnées",
|
package/assets/i18n/it.json
CHANGED
|
@@ -488,6 +488,7 @@
|
|
|
488
488
|
"Send message": "Invia messaggio",
|
|
489
489
|
"Settings": "Impostazioni",
|
|
490
490
|
"Shadows": "Ombre",
|
|
491
|
+
"Share map": "Condividi la mappa",
|
|
491
492
|
"Share this map": "Condividi questa mappa",
|
|
492
493
|
"share-panel": "Condividi o incorpora la mappa",
|
|
493
494
|
"Show metadata": "Mostra metadati",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><mask id="a" width="24" height="24" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#d9d9d9" d="M0 0h24v24H0z"/></mask><g mask="url(#a)"><path fill="#1c1b1f" d="M18 22a2.9 2.9 0 0 1-2.125-.875A2.9 2.9 0 0 1 15 19q0-.175.025-.363.025-.187.075-.337l-7.05-4.1q-.425.375-.95.588T6 15a2.9 2.9 0 0 1-2.125-.875A2.9 2.9 0 0 1 3 12q0-1.25.875-2.125A2.9 2.9 0 0 1 6 9q.575 0 1.1.213.525.212.95.587l7.05-4.1a2 2 0 0 1-.075-.338A3 3 0 0 1 15 5q0-1.25.875-2.125A2.9 2.9 0 0 1 18 2q1.25 0 2.125.875T21 5t-.875 2.125A2.9 2.9 0 0 1 18 8q-.575 0-1.1-.213a3.3 3.3 0 0 1-.95-.587L8.9 11.3q.05.15.075.337a2.7 2.7 0 0 1 0 .726 2 2 0 0 1-.075.337l7.05 4.1q.425-.375.95-.588T18 16q1.25 0 2.125.875T21 19t-.875 2.125A2.9 2.9 0 0 1 18 22m0-16q.424 0 .712-.287A.97.97 0 0 0 19 5a.97.97 0 0 0-.288-.713A.97.97 0 0 0 18 4a.97.97 0 0 0-.712.287A.97.97 0 0 0 17 5q0 .424.288.713Q17.575 6 18 6M6 13q.424 0 .713-.287A.97.97 0 0 0 7 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 6 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 5 12q0 .424.287.713Q5.576 13 6 13m12 7q.424 0 .712-.288A.97.97 0 0 0 19 19a.97.97 0 0 0-.288-.712A.97.97 0 0 0 18 18a.97.97 0 0 0-.712.288A.97.97 0 0 0 17 19q0 .424.288.712.287.288.712.288"/></g></svg>
|
|
@@ -319,17 +319,19 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
onProjectionChanged(oldProj, newProj) {
|
|
322
|
-
if (oldProj
|
|
323
|
-
|
|
324
|
-
const features = [...this.drawingState.features];
|
|
325
|
-
features.forEach((f) => {
|
|
326
|
-
// TODO Handle the case of disks
|
|
327
|
-
f.geojson = geoJson.writeFeatureObject(geoJson.readFeature(f.geojson, { dataProjection: oldProj, featureProjection: newProj }));
|
|
328
|
-
});
|
|
329
|
-
// Refresh all the listeners
|
|
330
|
-
this.drawingState.features = [];
|
|
331
|
-
this.drawingState.features = features;
|
|
322
|
+
if (oldProj === null || oldProj === newProj) {
|
|
323
|
+
return;
|
|
332
324
|
}
|
|
325
|
+
const features = [...this.drawingState.features];
|
|
326
|
+
features.forEach((f) => {
|
|
327
|
+
const transformedGeoJson = DrawingFeature.transformGeoJson(f.geojson, oldProj, newProj);
|
|
328
|
+
if (transformedGeoJson) {
|
|
329
|
+
f.geojson = transformedGeoJson;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
// Refresh all the listeners
|
|
333
|
+
this.drawingState.features = [];
|
|
334
|
+
this.drawingState.features = features;
|
|
333
335
|
}
|
|
334
336
|
onToggleBatchMode() {
|
|
335
337
|
this.batchCreateMode = !this.batchCreateMode;
|
package/components/main.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export { default as SelectionPanelMobile, getLinkFriendlyString } from './select
|
|
|
48
48
|
export { default as SelectionToolComponent } from './selectiontool/component.js';
|
|
49
49
|
export { default as SelectionWindowComponent } from './selectionwindow/component.js';
|
|
50
50
|
export { default as ShareComponent } from './share/component.js';
|
|
51
|
+
export { default as ShareMobileComponent } from './share-mobile/component.js';
|
|
51
52
|
export { default as SwipeUpPanelMobile } from './swipe-up-panel-mobile/component.js';
|
|
52
53
|
export { default as ThemeComponent } from './themes/component.js';
|
|
53
54
|
export { default as MobileGroupElementComponent } from './themes-mobile/group/component.js';
|
package/components/main.js
CHANGED
|
@@ -47,6 +47,7 @@ export { default as SelectionPanelMobile, getLinkFriendlyString } from './select
|
|
|
47
47
|
export { default as SelectionToolComponent } from './selectiontool/component.js';
|
|
48
48
|
export { default as SelectionWindowComponent } from './selectionwindow/component.js';
|
|
49
49
|
export { default as ShareComponent } from './share/component.js';
|
|
50
|
+
export { default as ShareMobileComponent } from './share-mobile/component.js';
|
|
50
51
|
export { default as SwipeUpPanelMobile } from './swipe-up-panel-mobile/component.js';
|
|
51
52
|
export { default as ThemeComponent } from './themes/component.js';
|
|
52
53
|
export { default as MobileGroupElementComponent } from './themes-mobile/group/component.js';
|
|
@@ -6,7 +6,7 @@ export default class MenuMobile3dButton extends GirafeHTMLElement {
|
|
|
6
6
|
styleUrls = null;
|
|
7
7
|
template = () => {
|
|
8
8
|
return uHtml `<style>
|
|
9
|
-
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}
|
|
9
|
+
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}.menu-item-title{text-align:center}
|
|
10
10
|
</style>
|
|
11
11
|
<style>${this.customStyle}</style>
|
|
12
12
|
<div onpointerup="${() => this.onPointerUp()}" class="menu-item"><div class="menu-item-image-container"><img alt="switch-icon" src="${this.icon}"></div><div class="menu-item-title-container"><div class="menu-item-title" i18n="${this.label}"></div></div></div>
|
|
@@ -6,7 +6,7 @@ export default class MenuMobileDrawingButton extends GirafeHTMLElement {
|
|
|
6
6
|
styleUrls = null;
|
|
7
7
|
template = () => {
|
|
8
8
|
return uHtml `<style>
|
|
9
|
-
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}
|
|
9
|
+
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}.menu-item-title{text-align:center}
|
|
10
10
|
</style>
|
|
11
11
|
<style>${this.customStyle}</style>
|
|
12
12
|
<div onpointerup="${() => this.onPointerUp()}" class="menu-item"><div class="menu-item-image-container"><img alt="switch-icon" src="${this.icon}"></div><div class="menu-item-title-container"><div class="menu-item-title" i18n="${this.label}"></div></div></div>
|
|
@@ -6,7 +6,7 @@ export default class MenuMobileOfflineButton extends GirafeHTMLElement {
|
|
|
6
6
|
styleUrls = null;
|
|
7
7
|
template = () => {
|
|
8
8
|
return uHtml `<style>
|
|
9
|
-
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}
|
|
9
|
+
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}.menu-item-title{text-align:center}
|
|
10
10
|
</style>
|
|
11
11
|
<style>${this.customStyle}</style>
|
|
12
12
|
<div onpointerup="${() => this.onPointerUp()}" class="menu-item"><div class="menu-item-image-container"><img alt="switch-icon" src="${this.icon}"></div><div class="menu-item-title-container"><div class="menu-item-title" i18n="${this.label}"></div></div></div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
2
|
+
export default class MenuMobileShareButton extends GirafeHTMLElement {
|
|
3
|
+
protected templateUrl: string | null;
|
|
4
|
+
protected styleUrls: string[] | null;
|
|
5
|
+
template: () => import("uhtml").Hole;
|
|
6
|
+
icon: string;
|
|
7
|
+
label: string;
|
|
8
|
+
constructor();
|
|
9
|
+
onPointerUp(): void;
|
|
10
|
+
protected connectedCallback(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { html as uHtml } from 'uhtml';
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
4
|
+
export default class MenuMobileShareButton extends GirafeHTMLElement {
|
|
5
|
+
templateUrl = null;
|
|
6
|
+
styleUrls = null;
|
|
7
|
+
template = () => {
|
|
8
|
+
return uHtml `<style>
|
|
9
|
+
.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}.menu-item-title{text-align:center}
|
|
10
|
+
</style>
|
|
11
|
+
<style>${this.customStyle}</style>
|
|
12
|
+
<div onpointerup="${() => this.onPointerUp()}" class="menu-item"><div class="menu-item-image-container"><img alt="switch-icon" src="${this.icon}"></div><div class="menu-item-title-container"><div class="menu-item-title" i18n="${this.label}"></div></div></div>
|
|
13
|
+
${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
14
|
+
};
|
|
15
|
+
icon = 'icons/share.svg';
|
|
16
|
+
label = 'Share map';
|
|
17
|
+
constructor() {
|
|
18
|
+
super('mobile-share-button');
|
|
19
|
+
}
|
|
20
|
+
onPointerUp() {
|
|
21
|
+
// If not delaying, the releasing of the click is also fired
|
|
22
|
+
// on the offline panel shown
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
this.state.interface.sharePanelVisible = true;
|
|
25
|
+
this.state.interface.swipeupPanelContent = 'share';
|
|
26
|
+
}, 50);
|
|
27
|
+
}
|
|
28
|
+
connectedCallback() {
|
|
29
|
+
super.connectedCallback();
|
|
30
|
+
this.render();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -10,7 +10,7 @@ export default class MenuMobile extends GirafeHTMLElement {
|
|
|
10
10
|
</style><style>
|
|
11
11
|
.hidden{display:none}.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)}button,input,select,textarea{font:inherit}.gg-icon-button,.gg-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-button{text-align:left}.gg-small{flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 2.5);margin:0}.gg-small span{margin-left:.5rem;font-size:1rem}.girafe-button-big{filter:drop-shadow(0 0 8px #0000004d);background-color:var(--bkg-color);width:3.5rem;height:3.5rem;color:var(--text-color);border:none;border-radius:4rem;flex-direction:column;align-items:center;padding:.5rem;display:flex}.girafe-button-big img{width:95%;height:auto;padding:5%;overflow:hidden}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color)}
|
|
12
12
|
</style><style>
|
|
13
|
-
.menu-title{color:#3f68a7;text-transform:uppercase;text-align:center;margin:.5em 0;font-size:1.8em;font-weight:600}.menu-item-container{box-sizing:border-box;user-select:none;margin-bottom:max(env(safe-area-inset-bottom), 20px);grid-template-columns:repeat(2,1fr);gap:20px;padding:0 25px;display:grid}.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}
|
|
13
|
+
.menu-title{color:#3f68a7;text-transform:uppercase;text-align:center;margin:.5em 0;font-size:1.8em;font-weight:600}.menu-item-container{box-sizing:border-box;user-select:none;margin-bottom:max(env(safe-area-inset-bottom), 20px);grid-template-columns:repeat(2,1fr);gap:20px;padding:0 25px;display:grid}.menu-item{filter:drop-shadow(0 4px 7px #0004382c);background-color:#fff;border-radius:8px;flex-direction:column;padding:1em;display:flex}.menu-item:active{background-color:#c6ceda}.menu-item-image-container{flex:1;justify-content:center;align-items:center;padding-bottom:1em;display:flex}.menu-item-image-container>img{opacity:.7;width:25%}.menu-item-title-container{color:#3f68a7;flex:1;justify-content:center;align-items:center;font-size:1.2em;font-weight:600;display:flex}.menu-item-title{text-align:center}
|
|
14
14
|
</style>
|
|
15
15
|
<style>${this.customStyle}</style>
|
|
16
16
|
<div style="${this.state.interface.swipeupPanelContent === 'menu' ? '' : 'display: none;'}"><div class="menu-title" i18n="Tools"></div><div class="menu-item-container"><slot name="mobile-menu-content"></slot></div></div>
|
|
@@ -65,8 +65,8 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
|
|
|
65
65
|
}
|
|
66
66
|
else if (featureLike.getGeometry()?.getType() == 'Circle') {
|
|
67
67
|
const circle = featureLike.getGeometry();
|
|
68
|
-
const radiusDataForCircle = getRadiusDataForCircle(circle, this.defaultStyle, new Stroke({ color: 'rgba(0, 0, 0, 0.4)', width: this.drawing.defaultStrokeWidth }));
|
|
69
|
-
styles.push(radiusDataForCircle.style, getLabelStyle(getHalfPoint(radiusDataForCircle.radiusLine), getLengthAsMetricText(radiusDataForCircle.
|
|
68
|
+
const radiusDataForCircle = getRadiusDataForCircle(circle, this.state.projection, this.defaultStyle, new Stroke({ color: 'rgba(0, 0, 0, 0.4)', width: this.drawing.defaultStrokeWidth }));
|
|
69
|
+
styles.push(radiusDataForCircle.style, getLabelStyle(getHalfPoint(radiusDataForCircle.radiusLine), getLengthAsMetricText(radiusDataForCircle.radiusMeter), this.labelStyle), getLabelStyle(new Point(circle.getCenter()), getAreaAsMetricText(getAreaOfCircle(circle, this.state.projection)), this.labelStyle));
|
|
70
70
|
}
|
|
71
71
|
return styles.filter((style) => !!style);
|
|
72
72
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { html as uHtml } from 'uhtml';
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import TwitterLogo from '
|
|
4
|
-
import FacebookLogo from '
|
|
5
|
-
import LinkedInLogo from '
|
|
6
|
-
import MailLogo from '
|
|
3
|
+
import TwitterLogo from '../../assets/icons/twitter.svg';
|
|
4
|
+
import FacebookLogo from '../../assets/icons/facebook.svg';
|
|
5
|
+
import LinkedInLogo from '../../assets/icons/linkedin.svg';
|
|
6
|
+
import MailLogo from '../../assets/icons/share-mail.svg';
|
|
7
7
|
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
8
8
|
import SimpleMaskManager from '../../tools/layers/simplemaskmanager.js';
|
|
9
9
|
class ShareComponent extends GirafeHTMLElement {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
2
|
+
import { IUrlShortener } from '../../tools/share/iurlshortener.js';
|
|
3
|
+
declare class ShareMobileComponent extends GirafeHTMLElement {
|
|
4
|
+
protected templateUrl: string | null;
|
|
5
|
+
protected styleUrls: string[] | null;
|
|
6
|
+
template: () => import("uhtml").Hole;
|
|
7
|
+
shareLink?: string;
|
|
8
|
+
qrCode?: string;
|
|
9
|
+
success: boolean;
|
|
10
|
+
twitterLogo: string;
|
|
11
|
+
facebookLogo: string;
|
|
12
|
+
linkedInLogo: string;
|
|
13
|
+
mailLogo: string;
|
|
14
|
+
shareNativeLogo: string;
|
|
15
|
+
awaitingShareLink: boolean;
|
|
16
|
+
get urlShortener(): IUrlShortener | undefined;
|
|
17
|
+
constructor();
|
|
18
|
+
render(): void;
|
|
19
|
+
protected connectedCallback(): void;
|
|
20
|
+
registerEvents(): void;
|
|
21
|
+
private generateShareLink;
|
|
22
|
+
shareFacebook(): void;
|
|
23
|
+
shareTwitter(): void;
|
|
24
|
+
shareLinkedIn(): void;
|
|
25
|
+
shareMail(): void;
|
|
26
|
+
shareNative(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export default ShareMobileComponent;
|
|
@@ -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
|
+
}
|
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.2750042516", "build":"2750042516", "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
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Style } from 'ol/style.js';
|
|
2
2
|
import { Circle as CircleGeom, Geometry } from 'ol/geom.js';
|
|
3
3
|
import Feature from 'ol/Feature.js';
|
|
4
|
+
import GeoJSON from 'ol/format/GeoJSON.js';
|
|
4
5
|
import IGirafeContext from '../context/icontext.js';
|
|
5
6
|
import DrawingState from './drawingState.js';
|
|
6
7
|
export declare enum DrawingShape {
|
|
@@ -37,6 +38,9 @@ export type SerializedFeature = {
|
|
|
37
38
|
t: DrawingShape;
|
|
38
39
|
};
|
|
39
40
|
export default class DrawingFeature {
|
|
41
|
+
static readonly geoJSONFormat: GeoJSON<Feature<Geometry, {
|
|
42
|
+
[x: string]: any;
|
|
43
|
+
}>>;
|
|
40
44
|
private readonly _tool;
|
|
41
45
|
private _name;
|
|
42
46
|
private _nameColor;
|
|
@@ -106,6 +110,11 @@ export default class DrawingFeature {
|
|
|
106
110
|
* with a limited number of edges to improve support across formats and services.
|
|
107
111
|
*/
|
|
108
112
|
static olFeatureFromGeoJson(geoJson: any, circleToPolygonNbEdges?: number): Feature<Geometry>;
|
|
113
|
+
/**
|
|
114
|
+
* Transform a geojson (valid object, or disk with its properties) to a new projection.
|
|
115
|
+
* @returns The transformed geojson object or null.
|
|
116
|
+
*/
|
|
117
|
+
static transformGeoJson(geoJson: object, oldProj: string, newProj: string): object | null;
|
|
109
118
|
/**
|
|
110
119
|
* Finds a name composed of the shape type and a number.
|
|
111
120
|
* The number starts from how many shapes of the same type are already in the state +1.
|
|
@@ -7,6 +7,7 @@ import Feature from 'ol/Feature.js';
|
|
|
7
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
|
+
import LineString from 'ol/geom/LineString.js';
|
|
10
11
|
export var DrawingShape;
|
|
11
12
|
(function (DrawingShape) {
|
|
12
13
|
DrawingShape[DrawingShape["Point"] = 0] = "Point";
|
|
@@ -19,6 +20,7 @@ export var DrawingShape;
|
|
|
19
20
|
DrawingShape[DrawingShape["FreehandPolygon"] = 7] = "FreehandPolygon";
|
|
20
21
|
})(DrawingShape || (DrawingShape = {}));
|
|
21
22
|
export default class DrawingFeature {
|
|
23
|
+
static geoJSONFormat = new GeoJSON();
|
|
22
24
|
_tool;
|
|
23
25
|
_name;
|
|
24
26
|
_nameColor;
|
|
@@ -263,17 +265,18 @@ export default class DrawingFeature {
|
|
|
263
265
|
const radius = disk.getRadius();
|
|
264
266
|
const pointer = disk.getProperties()['pointer'] ?? [center[0] + radius, center[1]];
|
|
265
267
|
return {
|
|
268
|
+
id: olFeature.getId(),
|
|
266
269
|
type: 'Feature',
|
|
267
270
|
geometry: {
|
|
268
271
|
type: 'Disk',
|
|
269
272
|
center: center,
|
|
270
273
|
radius: radius,
|
|
271
|
-
azimuth: disk.
|
|
274
|
+
azimuth: disk.get('azimuth') ?? 0,
|
|
272
275
|
pointer: pointer
|
|
273
276
|
}
|
|
274
277
|
};
|
|
275
278
|
}
|
|
276
|
-
return JSON.parse(
|
|
279
|
+
return JSON.parse(DrawingFeature.geoJSONFormat.writeFeature(olFeature));
|
|
277
280
|
}
|
|
278
281
|
/**
|
|
279
282
|
* Create an ol feature instance from a geojson object, optionally simplifying circle geometries to polygons
|
|
@@ -294,17 +297,54 @@ export default class DrawingFeature {
|
|
|
294
297
|
azimuth: geometry.azimuth,
|
|
295
298
|
pointer: geometry.pointer
|
|
296
299
|
});
|
|
297
|
-
|
|
300
|
+
const feature = new Feature(olGeometry);
|
|
301
|
+
if (geoJson.id) {
|
|
302
|
+
feature.setId(geoJson.id);
|
|
303
|
+
}
|
|
304
|
+
return feature;
|
|
298
305
|
}
|
|
299
306
|
else {
|
|
300
307
|
// Standard case: parse with GeoJSON format reader
|
|
301
|
-
const feature = new Feature(
|
|
308
|
+
const feature = new Feature(DrawingFeature.geoJSONFormat.readFeatures(geoJson)[0].getGeometry());
|
|
302
309
|
if (Array.isArray(feature)) {
|
|
303
310
|
throw new TypeError('GeoJSON feature is an array, expected single feature');
|
|
304
311
|
}
|
|
305
312
|
return feature;
|
|
306
313
|
}
|
|
307
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Transform a geojson (valid object, or disk with its properties) to a new projection.
|
|
317
|
+
* @returns The transformed geojson object or null.
|
|
318
|
+
*/
|
|
319
|
+
static transformGeoJson(geoJson, oldProj, newProj) {
|
|
320
|
+
if (oldProj === null || oldProj === newProj) {
|
|
321
|
+
return geoJson;
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
if (geoJson.geometry?.type === 'Disk') {
|
|
325
|
+
// Case of disks that are not a valid geojson object, handle them manually.
|
|
326
|
+
const olFeature = DrawingFeature.olFeatureFromGeoJson(geoJson);
|
|
327
|
+
const geom = olFeature.getGeometry();
|
|
328
|
+
geom?.transform(oldProj, newProj);
|
|
329
|
+
if (geom) {
|
|
330
|
+
const center = geom.getCenter();
|
|
331
|
+
const line = new LineString([center, [center[0], center[1] + geom.getRadius()]]);
|
|
332
|
+
line.rotate(toRadians((geom.get('azimuth') ?? 0) * -1), center);
|
|
333
|
+
geom.set('pointer', line.getLastCoordinate());
|
|
334
|
+
}
|
|
335
|
+
return DrawingFeature.geojsonFromOlFeature(olFeature, DrawingShape.Disk);
|
|
336
|
+
}
|
|
337
|
+
// Case of valid geojson objects.
|
|
338
|
+
return DrawingFeature.geoJSONFormat.writeFeatureObject(DrawingFeature.geoJSONFormat.readFeature(geoJson, {
|
|
339
|
+
dataProjection: oldProj,
|
|
340
|
+
featureProjection: newProj
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
catch (e) {
|
|
344
|
+
console.error('Error transforming this draw object to the new projection.', e);
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
308
348
|
/**
|
|
309
349
|
* Finds a name composed of the shape type and a number.
|
|
310
350
|
* The number starts from how many shapes of the same type are already in the state +1.
|
|
@@ -528,8 +528,9 @@ export default class OlDrawing {
|
|
|
528
528
|
createDiskFixedLength(coordinates, geom) {
|
|
529
529
|
const coord = coordinates;
|
|
530
530
|
this.fixLastLength(this.fixedLineLength, coord);
|
|
531
|
-
|
|
532
|
-
geom
|
|
531
|
+
const radius = new LineString(coord).getLength();
|
|
532
|
+
geom ??= new CircleGeom(coord[0], radius);
|
|
533
|
+
geom.setCenterAndRadius(coord[0], radius);
|
|
533
534
|
geom.setProperties({
|
|
534
535
|
azimuth: (90 - Math.atan2(coord[1][1] - coord[0][1], coord[1][0] - coord[0][0]) * (180 / Math.PI) + 360) % 360,
|
|
535
536
|
pointer: coord[1]
|
|
@@ -793,9 +794,9 @@ export default class OlDrawing {
|
|
|
793
794
|
}
|
|
794
795
|
else if (dFeature.type == DrawingShape.Disk) {
|
|
795
796
|
if (dFeature.measureInformation !== 'none') {
|
|
796
|
-
const radiusDataForCircle = getRadiusDataForCircle(geometry, defaultStyle, new Stroke({ color: measureColor, width: 2 }));
|
|
797
|
+
const radiusDataForCircle = getRadiusDataForCircle(geometry, this.state.projection, defaultStyle, new Stroke({ color: measureColor, width: 2 }));
|
|
797
798
|
styles.push(radiusDataForCircle.style);
|
|
798
|
-
const lengthText = dFeature.getLengthText(radiusDataForCircle.
|
|
799
|
+
const lengthText = dFeature.getLengthText(radiusDataForCircle.radiusMeter);
|
|
799
800
|
const azimuthText = dFeature.getAzimuthText(geometry);
|
|
800
801
|
const textTemplate = dFeature.measureInformation === 'simple' ? lengthText : lengthText + ', ' + azimuthText;
|
|
801
802
|
addLabel(getHalfPoint(radiusDataForCircle.radiusLine), textTemplate);
|
|
@@ -26,5 +26,5 @@ export default class GraphicalInterface {
|
|
|
26
26
|
darkFrontendMode: boolean;
|
|
27
27
|
swipeupPanelMode: SwipeupPanelMode;
|
|
28
28
|
contactPanelVisible: boolean;
|
|
29
|
-
swipeupPanelContent: 'selector' | 'features' | 'menu' | 'drawing' | 'offline' | null;
|
|
29
|
+
swipeupPanelContent: 'selector' | 'features' | 'menu' | 'drawing' | 'offline' | 'share' | null;
|
|
30
30
|
}
|
package/tools/utils/olutils.d.ts
CHANGED
|
@@ -43,9 +43,9 @@ export declare const reprojectGeometry: (geometry: Geometry, sourceProjection: P
|
|
|
43
43
|
export declare const ensurePolygonIsProperlyClosed: (polygon: Polygon) => Coordinate[];
|
|
44
44
|
export declare const getHalfPoint: (coordinates: Coordinate[]) => Point;
|
|
45
45
|
export declare const getLabelStyle: (position: Point, text: string, labelStyle: Style) => Style | undefined;
|
|
46
|
-
export declare const getRadiusDataForCircle: (circle: Circle, defaultStyle: Style, stroke: Stroke) => {
|
|
46
|
+
export declare const getRadiusDataForCircle: (circle: Circle, projection: ProjectionLike, defaultStyle: Style, stroke: Stroke) => {
|
|
47
|
+
radiusMeter: number;
|
|
47
48
|
style: Style;
|
|
48
|
-
radius: number;
|
|
49
49
|
radiusLine: Coordinate[];
|
|
50
50
|
};
|
|
51
51
|
export declare const getLengthAsMetricText: (length?: number) => string;
|
package/tools/utils/olutils.js
CHANGED
|
@@ -147,9 +147,10 @@ export const getLabelStyle = (position, text, labelStyle) => {
|
|
|
147
147
|
}
|
|
148
148
|
return undefined;
|
|
149
149
|
};
|
|
150
|
-
export const getRadiusDataForCircle = (circle, defaultStyle, stroke) => {
|
|
150
|
+
export const getRadiusDataForCircle = (circle, projection, defaultStyle, stroke) => {
|
|
151
151
|
const radius = circle.getRadius();
|
|
152
152
|
const center = circle.getCenter();
|
|
153
|
+
const radiusMeter = getDistance([center, [center[0], center[1] + radius]], projection);
|
|
153
154
|
const pointer = circle.getProperties()['pointer'] ?? [center[0] + radius, center[1]];
|
|
154
155
|
const radiusLine = [center, pointer];
|
|
155
156
|
const radiusLineStyle = defaultStyle.clone();
|
|
@@ -157,8 +158,8 @@ export const getRadiusDataForCircle = (circle, defaultStyle, stroke) => {
|
|
|
157
158
|
radiusLineStyle.getText().setText('');
|
|
158
159
|
radiusLineStyle.setGeometry(new LineString(radiusLine));
|
|
159
160
|
return {
|
|
161
|
+
radiusMeter,
|
|
160
162
|
style: radiusLineStyle,
|
|
161
|
-
radius: radius,
|
|
162
163
|
radiusLine: radiusLine
|
|
163
164
|
};
|
|
164
165
|
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|