@panoramax/web-viewer 5.0.0-develop-d7206514 → 5.0.0-develop-d26305dd

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.
Files changed (152) hide show
  1. package/build/bundle.cjs +3399 -0
  2. package/build/bundle.cjs.map +1 -0
  3. package/build/bundle_photoviewer.cjs +2510 -0
  4. package/build/bundle_photoviewer.cjs.map +1 -0
  5. package/build/cjs/index.js +1 -1
  6. package/build/cjs/index_photoviewer.js +1 -1
  7. package/build/components/core/Basic.css +56 -0
  8. package/build/components/core/Basic.js +378 -0
  9. package/build/components/core/CoverageMap.css +10 -0
  10. package/build/components/core/CoverageMap.js +169 -0
  11. package/build/components/core/Editor.css +33 -0
  12. package/build/components/core/Editor.js +398 -0
  13. package/build/components/core/PhotoViewer.css +70 -0
  14. package/build/components/core/PhotoViewer.js +650 -0
  15. package/build/components/core/Viewer.css +130 -0
  16. package/build/components/core/Viewer.js +711 -0
  17. package/build/components/core/index.js +10 -0
  18. package/build/components/index.js +11 -0
  19. package/build/components/index_photoviewer.js +6 -0
  20. package/build/components/layout/BottomDrawer.js +258 -0
  21. package/build/components/layout/CorneredGrid.js +143 -0
  22. package/build/components/layout/Mini.js +121 -0
  23. package/build/components/layout/Tabs.js +140 -0
  24. package/build/components/layout/index.js +9 -0
  25. package/build/components/menus/LocationPrecisionDoc.js +42 -0
  26. package/build/components/menus/MapBackground.js +110 -0
  27. package/build/components/menus/MapFilters.js +567 -0
  28. package/build/components/menus/MapLayers.js +238 -0
  29. package/build/components/menus/MapLegend.js +68 -0
  30. package/build/components/menus/MiniPictureLegend.js +73 -0
  31. package/build/components/menus/PictureLegend.js +379 -0
  32. package/build/components/menus/PictureMetadata.js +380 -0
  33. package/build/components/menus/PlayerOptions.js +93 -0
  34. package/build/components/menus/QualityScoreDoc.js +42 -0
  35. package/build/components/menus/ReportForm.js +132 -0
  36. package/build/components/menus/SemanticsDoc.js +38 -0
  37. package/build/components/menus/SemanticsDownload.js +33 -0
  38. package/build/components/menus/SemanticsFilters.js +153 -0
  39. package/build/components/menus/SemanticsList.js +413 -0
  40. package/build/components/menus/SemanticsMetadata.js +368 -0
  41. package/build/components/menus/Share.js +105 -0
  42. package/build/components/menus/index.js +22 -0
  43. package/build/components/menus/index_photoviewer.js +11 -0
  44. package/build/components/styles.js +557 -0
  45. package/build/components/ui/AnnotationsSwitch.js +159 -0
  46. package/build/components/ui/Button.js +77 -0
  47. package/build/components/ui/ButtonGroup.css +59 -0
  48. package/build/components/ui/ButtonGroup.js +69 -0
  49. package/build/components/ui/CopyButton.js +110 -0
  50. package/build/components/ui/Grade.js +54 -0
  51. package/build/components/ui/GradeFilter.js +122 -0
  52. package/build/components/ui/IconSwitch.js +193 -0
  53. package/build/components/ui/LinkButton.js +67 -0
  54. package/build/components/ui/ListGroup.js +66 -0
  55. package/build/components/ui/ListItem.js +90 -0
  56. package/build/components/ui/Loader.js +203 -0
  57. package/build/components/ui/Map.css +63 -0
  58. package/build/components/ui/Map.js +853 -0
  59. package/build/components/ui/MapMore.js +175 -0
  60. package/build/components/ui/Photo.css +50 -0
  61. package/build/components/ui/Photo.js +1502 -0
  62. package/build/components/ui/Popup.js +145 -0
  63. package/build/components/ui/ProgressBar.js +104 -0
  64. package/build/components/ui/QualityScore.js +147 -0
  65. package/build/components/ui/SearchBar.js +374 -0
  66. package/build/components/ui/SemanticsEditor.js +191 -0
  67. package/build/components/ui/SemanticsTable.js +88 -0
  68. package/build/components/ui/Switch.js +139 -0
  69. package/build/components/ui/TogglableGroup.js +157 -0
  70. package/build/components/ui/index.js +29 -0
  71. package/build/components/ui/index_photoviewer.js +21 -0
  72. package/build/components/ui/widgets/CopyCoordinates.js +75 -0
  73. package/build/components/ui/widgets/GeoSearch.css +21 -0
  74. package/build/components/ui/widgets/GeoSearch.js +150 -0
  75. package/build/components/ui/widgets/Legend.js +190 -0
  76. package/build/components/ui/widgets/LevelSelect.css +51 -0
  77. package/build/components/ui/widgets/LevelSelect.js +143 -0
  78. package/build/components/ui/widgets/MapFiltersButton.js +114 -0
  79. package/build/components/ui/widgets/MapLayersButton.js +79 -0
  80. package/build/components/ui/widgets/OSMEditors.js +155 -0
  81. package/build/components/ui/widgets/PictureLegendActions.js +99 -0
  82. package/build/components/ui/widgets/Player.css +7 -0
  83. package/build/components/ui/widgets/Player.js +154 -0
  84. package/build/components/ui/widgets/SemanticsFiltersButton.js +65 -0
  85. package/build/components/ui/widgets/Zoom.js +84 -0
  86. package/build/components/ui/widgets/index.js +16 -0
  87. package/build/components/ui/widgets/index_photoviewer.js +7 -0
  88. package/build/esm/components/core/Basic.js +1 -1
  89. package/build/img/arrow_360.svg +14 -0
  90. package/build/img/arrow_flat.svg +11 -0
  91. package/build/img/arrow_triangle.svg +9 -0
  92. package/build/img/arrow_turn.svg +8 -0
  93. package/build/img/bg_aerial.jpg +0 -0
  94. package/build/img/bg_streets.jpg +0 -0
  95. package/build/img/loader_base.jpg +0 -0
  96. package/build/img/logo_dead.svg +91 -0
  97. package/build/img/marker.svg +17 -0
  98. package/build/img/marker_blue.svg +20 -0
  99. package/build/img/osm.svg +49 -0
  100. package/build/img/panoramax.svg +13 -0
  101. package/build/img/switch_big.svg +54 -0
  102. package/build/img/switch_mini.svg +48 -0
  103. package/build/img/wd.svg +1 -0
  104. package/build/index_photoviewer.js +4 -0
  105. package/build/package.json +148 -0
  106. package/build/servers.js +14 -0
  107. package/build/translations/ar.json +1 -0
  108. package/build/translations/be.json +257 -0
  109. package/build/translations/br.json +81 -0
  110. package/build/translations/cy.json +117 -0
  111. package/build/translations/da.json +300 -0
  112. package/build/translations/de.json +309 -0
  113. package/build/translations/en.json +294 -0
  114. package/build/translations/eo.json +235 -0
  115. package/build/translations/es.json +292 -0
  116. package/build/translations/fi.json +1 -0
  117. package/build/translations/fr.json +294 -0
  118. package/build/translations/hr.json +294 -0
  119. package/build/translations/hu.json +294 -0
  120. package/build/translations/it.json +306 -0
  121. package/build/translations/ja.json +182 -0
  122. package/build/translations/ko.json +1 -0
  123. package/build/translations/nl.json +305 -0
  124. package/build/translations/nn.json +1 -0
  125. package/build/translations/pl.json +169 -0
  126. package/build/translations/pt.json +296 -0
  127. package/build/translations/pt_BR.json +304 -0
  128. package/build/translations/sv.json +182 -0
  129. package/build/translations/ti.json +9 -0
  130. package/build/translations/tr.json +297 -0
  131. package/build/translations/uk.json +268 -0
  132. package/build/translations/zh_Hant.json +309 -0
  133. package/build/utils/API.js +928 -0
  134. package/build/utils/InitParameters.js +521 -0
  135. package/build/utils/MapStyleComposer.js +889 -0
  136. package/build/utils/PanoraMapProtocol.js +49 -0
  137. package/build/utils/PhotoAdapter.js +49 -0
  138. package/build/utils/PresetsManager.js +148 -0
  139. package/build/utils/SemanticsMapProtocol.js +144 -0
  140. package/build/utils/URLHandler.js +426 -0
  141. package/build/utils/geocoder.js +203 -0
  142. package/build/utils/i18n.js +128 -0
  143. package/build/utils/index.js +17 -0
  144. package/build/utils/index_photoviewer.js +14 -0
  145. package/build/utils/indoor.js +200 -0
  146. package/build/utils/map.js +788 -0
  147. package/build/utils/picture.js +507 -0
  148. package/build/utils/semantics.js +321 -0
  149. package/build/utils/services.js +148 -0
  150. package/build/utils/utils.js +433 -0
  151. package/build/utils/widgets.js +110 -0
  152. package/package.json +1 -1
@@ -0,0 +1,139 @@
1
+ import { LitElement, css, html } from "lit";
2
+
3
+ /**
4
+ * Switch button, based on a native checkbox.
5
+ * @class Panoramax.components.ui.Switch
6
+ * @element pnx-switch
7
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
8
+ * @fires Panoramax.components.ui.Switch#change
9
+ * @example
10
+ * ```html
11
+ * <pnx-switch
12
+ * checked="true"
13
+ * name="my-switch"
14
+ * @change=${doSomething}
15
+ * >
16
+ * Label text
17
+ * </pnx-switch>
18
+ * ```
19
+ */
20
+ export default class Switch extends LitElement {
21
+ static styles = css`
22
+ .toggle {
23
+ cursor: pointer;
24
+ display: inline-block;
25
+ }
26
+
27
+ .toggle-switch {
28
+ display: inline-block;
29
+ background: #ccc;
30
+ border-radius: 11px;
31
+ width: 42px;
32
+ height: 22px;
33
+ position: relative;
34
+ vertical-align: middle;
35
+ transition: background 0.25s;
36
+ }
37
+ .toggle-switch:before, .toggle-switch:after {
38
+ content: "";
39
+ }
40
+ .toggle-switch:before {
41
+ display: block;
42
+ background: linear-gradient(to bottom, #fff 0%, #eee 100%);
43
+ border-radius: 50%;
44
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
45
+ width: 16px;
46
+ height: 16px;
47
+ position: absolute;
48
+ top: 3px;
49
+ left: 3px;
50
+ transition: left 0.25s;
51
+ }
52
+ .toggle:hover .toggle-switch:before {
53
+ background: linear-gradient(to bottom, #fff 0%, #fff 100%);
54
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
55
+ }
56
+ .toggle-checkbox:checked + .toggle-switch {
57
+ background: var(--widget-bg-active);
58
+ }
59
+ .toggle-checkbox:disabled + .toggle-switch:before,
60
+ .toggle-checkbox:disabled:hover + .toggle-switch:before {
61
+ background: var(--grey-pale);
62
+ box-shadow: none;
63
+ }
64
+ .toggle-checkbox:disabled + .toggle-switch {
65
+ background: var(--grey);
66
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
67
+ }
68
+ .toggle-checkbox:checked + .toggle-switch:before {
69
+ left: 23px;
70
+ }
71
+
72
+ .toggle-checkbox {
73
+ position: absolute;
74
+ visibility: hidden;
75
+ }
76
+
77
+ .toggle-label {
78
+ margin-left: 5px;
79
+ position: relative;
80
+ top: 2px;
81
+ }
82
+ `;
83
+
84
+ /**
85
+ * Component properties.
86
+ * @memberof Panoramax.components.ui.Switch#
87
+ * @type {Object}
88
+ * @property {boolean} checked True if checked
89
+ * @property {boolean} disabled Inactive if true
90
+ * @property {boolean} value The current switch value
91
+ * @property {string} name The switch name (also serves as ID)
92
+ */
93
+ static properties = {
94
+ checked: {type: Boolean, reflect: true},
95
+ disabled: {type: Boolean, reflect: true},
96
+ name: {type: String},
97
+ };
98
+
99
+ constructor() {
100
+ super();
101
+ this.checked = false;
102
+ this.disabled = false;
103
+ this.name = "pnx-switch-1"; // Default name+ID to avoid issues
104
+ }
105
+
106
+ /** @private */
107
+ _onChange(e) {
108
+ this.checked = e.target.checked;
109
+ /**
110
+ * Event for switch status change
111
+ * @event Panoramax.components.ui.Switch#change
112
+ * @type {Event}
113
+ */
114
+ this.dispatchEvent(new Event("change"));
115
+ }
116
+
117
+ /** @private */
118
+ render() {
119
+ return html`
120
+ <label class="toggle" for=${this.name}>
121
+ <input
122
+ class="toggle-checkbox"
123
+ type="checkbox"
124
+ id=${this.name}
125
+ name=${this.name}
126
+ .checked=${this.checked}
127
+ .disabled=${this.disabled}
128
+ @change=${this._onChange}
129
+ />
130
+ <div class="toggle-switch"></div>
131
+ <span class="toggle-label">
132
+ <slot></slot>
133
+ </span>
134
+ </label>
135
+ `;
136
+ }
137
+ }
138
+
139
+ customElements.define("pnx-switch", Switch);
@@ -0,0 +1,157 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { panel } from "../styles.js";
3
+ import { listenForMenuClosure } from "../../utils/widgets.js";
4
+ import { classMap } from "lit/directives/class-map.js";
5
+
6
+ /**
7
+ * Togglable Group element allows to make a menu appear/disappear based on button click.
8
+ * @class Panoramax.components.ui.TogglableGroup
9
+ * @element pnx-togglable-group
10
+ * @slot `button` The button on which click leads to open/close menu
11
+ * @slot `default` The menu to make shown/hidden on button click
12
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
13
+ * @example
14
+ * ```html
15
+ * <pnx-togglable-group padded="false" ._parent=${viewer}>
16
+ * <pnx-button slot="button">
17
+ * Open menu
18
+ * </pnx-button>
19
+ * <div>Menu content</div>
20
+ * </pnx-togglable-group>
21
+ * ```
22
+ */
23
+ export default class TogglableGroup extends LitElement {
24
+ /** @private */
25
+ static styles = [ panel, css`
26
+ div.container {
27
+ position: relative;
28
+ height: 100%;
29
+ }
30
+
31
+ .pnx-panel { z-index: 130; }
32
+ .pnx-panel-fixed { position: fixed; }
33
+ ` ];
34
+
35
+ /**
36
+ * Component properties.
37
+ * @memberof Panoramax.components.ui.TogglableGroup#
38
+ * @type {Object}
39
+ * @property {string} [padded] If set, adds a 15px padding to the menu panel.
40
+ */
41
+ static properties = {
42
+ _opened: {state: true},
43
+ padded: {type: String},
44
+ };
45
+
46
+ constructor() {
47
+ super();
48
+ this._opened = false;
49
+ }
50
+
51
+ /** @private */
52
+ open() {
53
+ this._opened = true;
54
+ this._parent?.dispatchEvent(new CustomEvent("menu-opened", { detail: { menu: this }}));
55
+ this.adjustMenuPosition(this._btn);
56
+ if(this._chevron) { this._chevron.style.rotate = "180deg"; }
57
+ }
58
+
59
+ /** @private */
60
+ close() {
61
+ this._opened = false;
62
+ if(this._chevron) { this._chevron.style.rotate = ""; }
63
+ }
64
+
65
+ /** @private */
66
+ connectedCallback() {
67
+ super.connectedCallback();
68
+ listenForMenuClosure(this, this.close.bind(this));
69
+ }
70
+
71
+ /** @private */
72
+ handleButtonSlotChange(e) {
73
+ this._btn = e.target.assignedNodes().pop();
74
+ if(this._btn) {
75
+ // Chevron
76
+ this._chevron = this._btn.querySelector("svg.fa-chevron-down");
77
+ if(this._chevron) {
78
+ this._chevron.style.transition = "rotate 0.2s";
79
+ this._chevron.style.rotate = this._opened ? "180deg" : "";
80
+ }
81
+
82
+ this._btn.addEventListener("click", () => {
83
+ if(this._opened) { this.close(); }
84
+ else { this.open(); }
85
+ });
86
+ }
87
+ }
88
+
89
+ /** @private */
90
+ adjustMenuPosition(btn) {
91
+ const btnMenuMargin = 5;
92
+ const borderMargin = 10;
93
+
94
+ // Reset menu position
95
+ const menu = this.shadowRoot.querySelector("div[part='menu']");
96
+ menu.style.top = null;
97
+ menu.style.bottom = null;
98
+ menu.style.right = null;
99
+ menu.style.left = null;
100
+ menu.style.overflowY = null;
101
+ menu.style.marginTop = null;
102
+
103
+ // Get positions on screen
104
+ const btnRect = btn.getBoundingClientRect();
105
+ let menuRect = menu.getBoundingClientRect();
106
+ const fitsWidth = menuRect.right <= window.innerWidth - borderMargin;
107
+ const fitsHeight = menuRect.bottom <= window.innerHeight - borderMargin;
108
+
109
+ // No overflow = space a bit under button
110
+ if(fitsWidth && fitsHeight) {
111
+ menu.style.top = `${btnRect.bottom+btnMenuMargin}px`;
112
+ }
113
+ // Overflows width+height = put at button's top+left
114
+ else if(!fitsWidth && !fitsHeight) {
115
+ menu.style.right = `${window.innerWidth - btnRect.left + btnMenuMargin}px`;
116
+ menu.style.bottom = `${window.innerHeight - btnRect.bottom}px`;
117
+
118
+ // Check if it doesn't overflow on top
119
+ menuRect = menu.getBoundingClientRect();
120
+ if(menuRect.top - borderMargin < 0) {
121
+ menu.style.bottom = `${borderMargin}px`;
122
+ menu.style.top = `${btnRect.top}px`;
123
+ menu.style.overflowY = "auto";
124
+ }
125
+ }
126
+ // Overflows height = limit height
127
+ else if(!fitsHeight) {
128
+ menu.style.bottom = `${borderMargin}px`;
129
+ menu.style.top = `${btnRect.bottom+btnMenuMargin}px`;
130
+ menu.style.overflowY = "auto";
131
+ }
132
+ // Overflows width = move to left
133
+ else if(!fitsWidth) {
134
+ menu.style.marginTop = `${btnMenuMargin}px`;
135
+ menu.style.right = `${window.innerWidth - btnRect.right}px`;
136
+ }
137
+ }
138
+
139
+ /** @private */
140
+ render() {
141
+ const panelClasses = {
142
+ "pnx-panel": true,
143
+ "pnx-panel-fixed": true,
144
+ "pnx-hidden": !this._opened,
145
+ "pnx-padded": this.padded !== "false",
146
+ };
147
+
148
+ return html`<div class="container" @click=${e => e.stopPropagation()}>
149
+ <slot name="button" @slotchange=${this.handleButtonSlotChange}></slot>
150
+ <div class=${classMap(panelClasses)} part="menu">
151
+ <slot></slot>
152
+ </div>
153
+ </div>`;
154
+ }
155
+ }
156
+
157
+ customElements.define("pnx-togglable-group", TogglableGroup);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Smaller UI pieces components
3
+ * @module Panoramax:components:ui
4
+ */
5
+
6
+ export {default as AnnotationsSwitch} from "./AnnotationsSwitch.js";
7
+ export {default as ButtonGroup} from "./ButtonGroup.js";
8
+ export {default as Button} from "./Button.js";
9
+ export {default as CopyButton} from "./CopyButton.js";
10
+ export {default as Grade} from "./Grade.js";
11
+ export {default as GradeFilter} from "./GradeFilter.js";
12
+ export {default as IconSwitch} from "./IconSwitch.js";
13
+ export {default as LinkButton} from "./LinkButton.js";
14
+ export {default as ListGroup} from "./ListGroup.js";
15
+ export {default as ListItem} from "./ListItem.js";
16
+ export {default as Loader} from "./Loader.js";
17
+ export {default as Map} from "./Map.js";
18
+ export {default as MapMore} from "./MapMore.js";
19
+ export {default as Photo} from "./Photo.js";
20
+ export {default as Popup} from "./Popup.js";
21
+ export {default as ProgressBar} from "./ProgressBar.js";
22
+ export {default as QualityScore} from "./QualityScore.js";
23
+ export {default as SearchBar} from "./SearchBar.js";
24
+ export {default as SemanticsEditor} from "./SemanticsEditor.js";
25
+ export {default as SemanticsTable} from "./SemanticsTable.js";
26
+ export {default as Switch} from "./Switch.js";
27
+ export {default as TogglableGroup} from "./TogglableGroup.js";
28
+ import * as widgets from "./widgets/index.js";
29
+ export {widgets};
@@ -0,0 +1,21 @@
1
+ // Lightweight export for PhotoViewer
2
+ export {default as ButtonGroup} from "./ButtonGroup.js";
3
+ export {default as Button} from "./Button.js";
4
+ export {default as CopyButton} from "./CopyButton.js";
5
+ export {default as Grade} from "./Grade.js";
6
+ export {default as AnnotationsSwitch} from "./AnnotationsSwitch.js";
7
+ export {default as LinkButton} from "./LinkButton.js";
8
+ export {default as ListGroup} from "./ListGroup.js";
9
+ export {default as ListItem} from "./ListItem.js";
10
+ export {default as Loader} from "./Loader.js";
11
+ export {default as Photo} from "./Photo.js";
12
+ export {default as Popup} from "./Popup.js";
13
+ export {default as ProgressBar} from "./ProgressBar.js";
14
+ export {default as QualityScore} from "./QualityScore.js";
15
+ export {default as SearchBar} from "./SearchBar.js";
16
+ export {default as SemanticsEditor} from "./SemanticsEditor.js";
17
+ export {default as SemanticsTable} from "./SemanticsTable.js";
18
+ export {default as Switch} from "./Switch.js";
19
+ export {default as TogglableGroup} from "./TogglableGroup.js";
20
+ import * as widgets from "./widgets/index_photoviewer.js";
21
+ export {widgets};
@@ -0,0 +1,75 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { degToDms } from "../../../utils/utils.js";
3
+ import { fa } from "../../../utils/widgets.js";
4
+ import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
5
+ import { btngroup } from "../../styles.js";
6
+
7
+ /**
8
+ * Copy Coordinates button allows easy copy of several format of map coordinates.
9
+ * @class Panoramax.components.ui.widgets.CopyCoordinates
10
+ * @element pnx-copy-coordinates
11
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
12
+ * @example
13
+ * ```html
14
+ * <pnx-copy-coordinates gps=${[-1.7, 48.6]} _parent=${viewer} />
15
+ * ```
16
+ */
17
+ export default class CopyCoordinates extends LitElement {
18
+ /** @private */
19
+ static styles = [btngroup, css`
20
+ pnx-togglable-group::part(menu) {
21
+ border-radius: 5px;
22
+ min-width: unset;
23
+ }
24
+ `];
25
+
26
+ /**
27
+ * Component properties.
28
+ * @memberof Panoramax.components.ui.widgets.CopyCoordinates#
29
+ * @type {Object}
30
+ * @property {number[]} gps GPS/map coordinates, as [lon, lat]
31
+ */
32
+ static properties = {
33
+ gps: {type: Array},
34
+ };
35
+
36
+ /** @private */
37
+ render() {
38
+ const dmslonval = degToDms(this.gps[0]);
39
+ const dmslon = dmslonval.d < 0 ? `${Math.abs(dmslonval.d)}°${dmslonval.m}'${dmslonval.s}"W` : `${dmslonval.d}°${dmslonval.m}'${dmslonval.s}"E`;
40
+ const dmslatval = degToDms(this.gps[1]);
41
+ const dmslat = dmslatval.d < 0 ? `${Math.abs(dmslatval.d)}°${dmslatval.m}'${dmslatval.s}"S` : `${dmslatval.d}°${dmslatval.m}'${dmslatval.s}"N`;
42
+
43
+ const values = {
44
+ ddeclatlon: `${this.gps[1].toFixed(5)}, ${this.gps[0].toFixed(5)}`,
45
+ ddeclonlat: `${this.gps[0].toFixed(5)}, ${this.gps[1].toFixed(5)}`,
46
+ ddmslonlat: `${dmslon}, ${dmslat}`,
47
+ ddmslatlon: `${dmslat}, ${dmslon}`,
48
+ };
49
+
50
+ return html`<pnx-button-group style="display: inline-block; vertical-align: baseline" dir="row">
51
+ <pnx-copy-button
52
+ size="sm"
53
+ text=${values.ddeclonlat}
54
+ title=${this._parent?._t.pnx.metadata_location_copy.replace("{v}", values.ddeclonlat)}
55
+ ></pnx-copy-button>
56
+
57
+ <pnx-togglable-group padded="false" ._parent=${this._parent}>
58
+ <pnx-button
59
+ size="sm"
60
+ slot="button"
61
+ title=${this._parent?._t.pnx.metadata_location_copy_more}
62
+ >${fa(faChevronDown)}</pnx-button>
63
+ <pnx-list-group>
64
+ ${Object.values(values).map(text => html`
65
+ <pnx-copy-button unstyled text=${text} ._t=${this._parent._t}>
66
+ ${this._parent?._t.pnx.metadata_location_copy.replace("{v}", text)}
67
+ </pnx-copy-button>
68
+ `)}
69
+ </pnx-list-group>
70
+ </pnx-togglable-group>
71
+ </pnx-button-group>`;
72
+ }
73
+ }
74
+
75
+ customElements.define("pnx-copy-coordinates", CopyCoordinates);
@@ -0,0 +1,21 @@
1
+ pnx-widget-geosearch .maplibregl-ctrl {
2
+ box-shadow: none !important;
3
+ background: none !important;
4
+ }
5
+
6
+ pnx-widget-geosearch .maplibregl-ctrl-geolocate {
7
+ border-radius: 15px;
8
+ }
9
+
10
+ pnx-widget-geosearch pnx-search-bar[reduced] .maplibregl-ctrl-geolocate {
11
+ width: 40px;
12
+ }
13
+
14
+ pnx-widget-geosearch pnx-search-bar:not([reduced]) .maplibregl-ctrl-geolocate {
15
+ margin-left: -5px;
16
+ }
17
+
18
+ pnx-widget-geosearch pnx-search-bar:not([reduceable])::part(input) {
19
+ width: 300px;
20
+ max-width: 30vw;
21
+ }
@@ -0,0 +1,150 @@
1
+ import "maplibre-gl";
2
+ import { LitElement, html } from "lit";
3
+ import { forwardGeocodingBAN, forwardGeocodingNominatim, forwardGeocodingStandard } from "../../../utils/geocoder.js";
4
+ import { onceParentAvailable } from "../../../utils/widgets.js";
5
+ import GeoSearchStyles from "./GeoSearch.css" with { type: "css" };
6
+ document.adoptedStyleSheets.push(GeoSearchStyles);
7
+
8
+ const GEOCODER_ENGINES = {
9
+ "ban": forwardGeocodingBAN,
10
+ "standard": forwardGeocodingStandard,
11
+ "nominatim": forwardGeocodingNominatim
12
+ };
13
+
14
+ /**
15
+ * Ready-to-use geocoder search bar.
16
+ * @class Panoramax.components.ui.widgets.GeoSearch
17
+ * @element pnx-widget-geosearch
18
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
19
+ * @example
20
+ * ```html
21
+ * <!-- Default geocoder -->
22
+ * <pnx-widget-geosearch _parent=${viewer} />
23
+ *
24
+ * <!-- Custom-URL geocoder -->
25
+ * <pnx-widget-geosearch geocoder="https://photon.komoot.io/api" _parent=${viewer} />
26
+ * ```
27
+ */
28
+ export default class GeoSearch extends LitElement {
29
+ /**
30
+ * Component properties.
31
+ * @memberof Panoramax.components.ui.widgets.GeoSearch#
32
+ * @type {Object}
33
+ * @property {string} [geocoder=nominatim] The geocoder engine to use (nominatim, ban, or URL to a standard [GeocodeJSON-compliant](https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md) API)
34
+ */
35
+ static properties = {
36
+ geocoder: {type: String},
37
+ _geolocate: {state: true},
38
+ };
39
+
40
+ constructor() {
41
+ super();
42
+ this.geocoder = "nominatim";
43
+
44
+ // eslint-disable-next-line no-undef
45
+ this._geolocateCtrl = new maplibregl.GeolocateControl({
46
+ positionOptions: {
47
+ enableHighAccuracy: true,
48
+ timeout: 60000, // Max 1 minute for first position
49
+ maximumAge: 300000, // Accepts 5 minutes old position
50
+ },
51
+ showAccuracyCircle: true,
52
+ showUserLocation: true,
53
+ trackUserLocation: true,
54
+ });
55
+ }
56
+
57
+ /** @private */
58
+ createRenderRoot() {
59
+ return this;
60
+ }
61
+
62
+ /** @private */
63
+ connectedCallback() {
64
+ super.connectedCallback();
65
+
66
+ this._geocoderEngine = GEOCODER_ENGINES[this.geocoder] || (config => GEOCODER_ENGINES.standard(config, this.geocoder));
67
+ onceParentAvailable(this).then(() => this._parent?.onceMapReady?.().then(() => {
68
+ if(!this._geolocate) {
69
+ this._geolocate = this._geolocateCtrl.onAdd(this._parent.map);
70
+ this._geolocate.setAttribute("slot", "pre");
71
+ }
72
+ }));
73
+ }
74
+
75
+ /** @private */
76
+ _onInput(query) {
77
+ const rgxCoords = /([-+]?\d{1,2}\.\d+),\s*([-+]?\d{1,3}\.\d+)/;
78
+ const coordsMatch = query.match(rgxCoords);
79
+ const rgxUuid = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
80
+ const uuidMatch = query.match(rgxUuid);
81
+
82
+ if(coordsMatch) {
83
+ const lat = parseFloat(coordsMatch[1]);
84
+ const lon = parseFloat(coordsMatch[2]);
85
+ this._parent.map.jumpTo({
86
+ center: [lon, lat],
87
+ zoom: 16,
88
+ });
89
+ return Promise.resolve(true);
90
+ }
91
+ else if(uuidMatch) {
92
+ this._parent.select(null, query);
93
+ return Promise.resolve(true);
94
+ }
95
+ else {
96
+ return this._geocoderEngine({
97
+ query,
98
+ limit: 5,
99
+ //Bbox: this._parent.map.getBounds().toArray().map(d => d.join(",")).join(","),
100
+ proximity: this._parent.map.getCenter().lat+","+this._parent.map.getCenter().lng,
101
+ }).then(data => {
102
+ data = data.features.map(f => ({
103
+ title: f.place_name.split(",")[0],
104
+ subtitle: f.place_name.split(",").slice(1).join(", "),
105
+ data: f
106
+ }));
107
+ return data;
108
+ });
109
+ }
110
+ }
111
+
112
+ /** @private */
113
+ _onSelect(e) {
114
+ const entry = e.detail;
115
+ if(entry) {
116
+ if(entry.data.bounds) {
117
+ this._parent?.map.fitBounds(entry.data.bounds, {animate: false});
118
+ }
119
+ else {
120
+ this._parent?.map.jumpTo({
121
+ center: entry.data.center,
122
+ zoom: entry.data.zoom || 13,
123
+ });
124
+ }
125
+ }
126
+ }
127
+
128
+ /** @private */
129
+ render() {
130
+ const isSmall = this._parent?.isWidthSmall() || false;
131
+
132
+ return html`
133
+ <pnx-search-bar
134
+ id="pnx-widget-search-bar"
135
+ placeholder=${this._parent?._t.pnx.search_address}
136
+ ._parent=${this._parent}
137
+ .searcher=${this._onInput.bind(this)}
138
+ .reduceable=${isSmall}
139
+ .reduced=${isSmall}
140
+ size="xxl"
141
+ class="pnx-print-hidden"
142
+ @result=${this._onSelect.bind(this)}
143
+ >
144
+ ${this._geolocate}
145
+ </pnx-search-bar>
146
+ `;
147
+ }
148
+ }
149
+
150
+ customElements.define("pnx-widget-geosearch", GeoSearch);