@panoramax/web-viewer 4.0.2-develop-9d664bb8 → 4.0.2-develop-e389d775
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/CHANGELOG.md +4 -0
- package/build/index.css +1 -1
- package/build/index.css.map +1 -1
- package/build/index.js +274 -59
- package/build/index.js.map +1 -1
- package/config/jest/mocks.js +5 -0
- package/docs/reference/components/core/PhotoViewer.md +1 -0
- package/docs/reference/components/core/Viewer.md +1 -0
- package/docs/reference/components/menus/AnnotationsList.md +16 -0
- package/docs/reference/components/ui/HashTags.md +15 -0
- package/docs/reference/components/ui/ListItem.md +38 -0
- package/docs/reference/components/ui/Photo.md +53 -1
- package/docs/reference/components/ui/SemanticsTable.md +32 -0
- package/docs/reference/utils/PresetsManager.md +35 -0
- package/docs/reference.md +4 -0
- package/mkdocs.yml +4 -0
- package/package.json +2 -1
- package/src/components/core/Basic.css +2 -0
- package/src/components/core/PhotoViewer.js +11 -0
- package/src/components/core/Viewer.js +7 -0
- package/src/components/layout/Tabs.js +1 -1
- package/src/components/menus/AnnotationsList.js +146 -0
- package/src/components/menus/PictureLegend.js +2 -4
- package/src/components/menus/PictureMetadata.js +66 -2
- package/src/components/menus/index.js +1 -0
- package/src/components/styles.js +34 -0
- package/src/components/ui/HashTags.js +98 -0
- package/src/components/ui/ListItem.js +83 -0
- package/src/components/ui/Photo.js +137 -0
- package/src/components/ui/SemanticsTable.js +87 -0
- package/src/components/ui/index.js +3 -0
- package/src/img/osm.svg +49 -0
- package/src/img/wd.svg +1 -0
- package/src/translations/en.json +22 -0
- package/src/translations/fr.json +20 -0
- package/src/utils/PresetsManager.js +137 -0
- package/src/utils/geocoder.js +23 -57
- package/src/utils/index.js +3 -1
- package/src/utils/picture.js +28 -0
- package/src/utils/semantics.js +162 -0
- package/src/utils/services.js +38 -0
- package/src/utils/widgets.js +18 -1
- package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +10 -0
- package/tests/components/core/__snapshots__/Viewer.test.js.snap +10 -0
- package/tests/data/Map_geocoder_nominatim.json +25 -40
- package/tests/utils/PresetsManager.test.js +123 -0
- package/tests/utils/__snapshots__/geocoder.test.js.snap +5 -10
- package/tests/utils/geocoder.test.js +1 -1
- package/tests/utils/semantics.test.js +125 -0
|
@@ -9,7 +9,10 @@ import { faImages } from "@fortawesome/free-solid-svg-icons/faImages";
|
|
|
9
9
|
import { faScroll } from "@fortawesome/free-solid-svg-icons/faScroll";
|
|
10
10
|
import { faQuestion } from "@fortawesome/free-solid-svg-icons/faQuestion";
|
|
11
11
|
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons/faInfoCircle";
|
|
12
|
-
import {
|
|
12
|
+
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
|
13
|
+
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
|
|
14
|
+
import { faTags } from "@fortawesome/free-solid-svg-icons/faTags";
|
|
15
|
+
import { faSvg, titles, textarea, hidden } from "../styles";
|
|
13
16
|
import { createWebComp } from "../../utils/widgets";
|
|
14
17
|
import { getGPSPrecision } from "../../utils/picture";
|
|
15
18
|
import {
|
|
@@ -31,7 +34,7 @@ const missing = () => fa(faQuestion, {styles: {height: "16px"}});
|
|
|
31
34
|
*/
|
|
32
35
|
export default class PictureMetadata extends LitElement {
|
|
33
36
|
/** @private */
|
|
34
|
-
static styles = [ titles, textarea, css`
|
|
37
|
+
static styles = [ faSvg, titles, textarea, hidden, css`
|
|
35
38
|
div[slot="content"] {
|
|
36
39
|
padding: 5px 10px;
|
|
37
40
|
background-color: #ededed;
|
|
@@ -56,13 +59,24 @@ export default class PictureMetadata extends LitElement {
|
|
|
56
59
|
.data-block div {
|
|
57
60
|
font-size: 0.8em;
|
|
58
61
|
}
|
|
62
|
+
|
|
63
|
+
pnx-semantics-table {
|
|
64
|
+
overflow-x: auto;
|
|
65
|
+
display: block;
|
|
66
|
+
}
|
|
59
67
|
` ];
|
|
60
68
|
|
|
61
69
|
/** @private */
|
|
62
70
|
static properties = {
|
|
63
71
|
_meta: {state: true},
|
|
72
|
+
_semanticsPicShowAll: {state: true},
|
|
64
73
|
};
|
|
65
74
|
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
this._semanticsPicShowAll = false;
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
/** @private */
|
|
67
81
|
connectedCallback() {
|
|
68
82
|
super.connectedCallback();
|
|
@@ -205,6 +219,51 @@ export default class PictureMetadata extends LitElement {
|
|
|
205
219
|
];
|
|
206
220
|
}
|
|
207
221
|
|
|
222
|
+
// Semantics data
|
|
223
|
+
const hasSemantics = (
|
|
224
|
+
(this._meta.properties.semantics || []).length > 0
|
|
225
|
+
|| (this._meta.properties.annotations || []).length > 0
|
|
226
|
+
);
|
|
227
|
+
let semanticsData = [];
|
|
228
|
+
if(hasSemantics) {
|
|
229
|
+
// Full list of picture tags
|
|
230
|
+
semanticsData.push({
|
|
231
|
+
title: this._parent?._t.pnx.semantics_tags_picture,
|
|
232
|
+
style: "width: 100%",
|
|
233
|
+
content: html`${this._meta.properties.semantics?.length > 0
|
|
234
|
+
? html`
|
|
235
|
+
<pnx-button
|
|
236
|
+
kind="outline"
|
|
237
|
+
size="sm"
|
|
238
|
+
style="width: 100%"
|
|
239
|
+
@click=${() => this._semanticsPicShowAll = !this._semanticsPicShowAll}
|
|
240
|
+
>
|
|
241
|
+
${this._semanticsPicShowAll ? fa(faChevronUp) : fa(faChevronDown)}
|
|
242
|
+
${this._semanticsPicShowAll ? this._parent?._t.pnx.semantics_hide_all_tags : this._parent?._t.pnx.semantics_show_all_tags}
|
|
243
|
+
</pnx-button>
|
|
244
|
+
<pnx-semantics-table
|
|
245
|
+
._t=${this._parent?._t}
|
|
246
|
+
.source=${this._meta.properties}
|
|
247
|
+
style="margin-top: 5px"
|
|
248
|
+
class=${this._semanticsPicShowAll ? "":"pnx-hidden"}
|
|
249
|
+
/>
|
|
250
|
+
`
|
|
251
|
+
: this._parent?._t.pnx.semantics_tags_picture_none
|
|
252
|
+
}`
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Annotations (features in picture)
|
|
256
|
+
semanticsData.push({
|
|
257
|
+
title: this._parent?._t.pnx.semantics_features,
|
|
258
|
+
style: "width: 100%",
|
|
259
|
+
content: html`
|
|
260
|
+
${this._meta.properties.annotations?.length > 0
|
|
261
|
+
? html`<pnx-annotations-list ._parent=${this._parent} />`
|
|
262
|
+
: this._parent?._t.pnx.semantics_features_none}
|
|
263
|
+
`
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
208
267
|
// EXIF data
|
|
209
268
|
const exifData = Object.entries(this._meta.properties.exif)
|
|
210
269
|
.sort()
|
|
@@ -327,6 +386,11 @@ export default class PictureMetadata extends LitElement {
|
|
|
327
386
|
qualityData
|
|
328
387
|
) : nothing}
|
|
329
388
|
|
|
389
|
+
${hasSemantics ? this._toTab( // Semantics
|
|
390
|
+
html`${fa(faTags)} ${this._parent?._t.pnx.semantics_title}`,
|
|
391
|
+
semanticsData
|
|
392
|
+
) : nothing}
|
|
393
|
+
|
|
330
394
|
${this._meta.properties?.exif ? this._toTab( // EXIF
|
|
331
395
|
html`${fa(faScroll)} ${this._parent?._t.pnx.metadata_exif}`,
|
|
332
396
|
exifData
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @module Panoramax:components:menus
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export {default as AnnotationsList} from "./AnnotationsList";
|
|
6
7
|
export {default as MapFilters} from "./MapFilters";
|
|
7
8
|
export {default as MapLayers} from "./MapLayers";
|
|
8
9
|
export {default as MapBackground} from "./MapBackground";
|
package/src/components/styles.js
CHANGED
|
@@ -27,6 +27,11 @@ export const panel = css`
|
|
|
27
27
|
}
|
|
28
28
|
`;
|
|
29
29
|
|
|
30
|
+
// Hidden
|
|
31
|
+
export const hidden = css`
|
|
32
|
+
.pnx-hidden { display: none !important; }
|
|
33
|
+
`;
|
|
34
|
+
|
|
30
35
|
// Font Awesome SVG
|
|
31
36
|
export const faSvg = css`
|
|
32
37
|
.svg-inline--fa {
|
|
@@ -442,3 +447,32 @@ export const noprint = css`
|
|
|
442
447
|
.pnx-print-hidden { display: none !important; }
|
|
443
448
|
}
|
|
444
449
|
`;
|
|
450
|
+
|
|
451
|
+
// Data table
|
|
452
|
+
export const table = css`
|
|
453
|
+
table {
|
|
454
|
+
border-collapse: collapse;
|
|
455
|
+
font-size: 0.9rem;
|
|
456
|
+
width: 100%;
|
|
457
|
+
max-width: 100%;
|
|
458
|
+
font-family: var(--font-family);
|
|
459
|
+
background: var(--white);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
th, td {
|
|
463
|
+
padding: 10px;
|
|
464
|
+
text-align: left;
|
|
465
|
+
border-bottom: 1px solid #ddd;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
th { font-weight: 600; }
|
|
469
|
+
`;
|
|
470
|
+
|
|
471
|
+
// Iconify icons
|
|
472
|
+
export const iconify = css`
|
|
473
|
+
iconify-icon {
|
|
474
|
+
display: inline-block;
|
|
475
|
+
width: 1em;
|
|
476
|
+
height: 1em;
|
|
477
|
+
}
|
|
478
|
+
`;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { LitElement, html, css, nothing } from "lit";
|
|
2
|
+
import { getHashTags, hasAnnotations } from "../../utils/picture";
|
|
3
|
+
import { fa } from "../../utils/widgets";
|
|
4
|
+
import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons/faDrawPolygon";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HashTags component shows the list of hashtags associated to a picture.
|
|
8
|
+
* @class Panoramax.components.ui.HashTags
|
|
9
|
+
* @element pnx-hashtags
|
|
10
|
+
* @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
|
|
11
|
+
* @example
|
|
12
|
+
* ```html
|
|
13
|
+
* <pnx-hashtags ._parent=${viewer} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export default class HashTags extends LitElement {
|
|
17
|
+
/** @private */
|
|
18
|
+
static styles = css`
|
|
19
|
+
div {
|
|
20
|
+
background: linear-gradient(to bottom left, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.2));
|
|
21
|
+
margin-top: -10px;
|
|
22
|
+
margin-right: -10px;
|
|
23
|
+
padding: 5px 10px;
|
|
24
|
+
border-bottom-left-radius: 10px;
|
|
25
|
+
font-family: var(--font-family);
|
|
26
|
+
color: white;
|
|
27
|
+
font-size: 0.8em;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
/** @private */
|
|
32
|
+
static properties = {
|
|
33
|
+
_tags: {state: true},
|
|
34
|
+
_visible: {state: true},
|
|
35
|
+
_annotationsToggled: {state: true},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
super();
|
|
40
|
+
this._tags = [];
|
|
41
|
+
this._visible = false;
|
|
42
|
+
this._annotationsToggled = false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @private */
|
|
46
|
+
connectedCallback() {
|
|
47
|
+
super.connectedCallback();
|
|
48
|
+
|
|
49
|
+
this._parent.onceReady().then(() => {
|
|
50
|
+
this._tags = getHashTags(this._parent?.psv?.getPictureMetadata?.());
|
|
51
|
+
|
|
52
|
+
// Component visibility : only if seen at least one pic with semantics
|
|
53
|
+
if(
|
|
54
|
+
!this._visible && (
|
|
55
|
+
this._tags.length > 0
|
|
56
|
+
|| hasAnnotations(this._parent?.psv?.getPictureMetadata?.())
|
|
57
|
+
)
|
|
58
|
+
) {
|
|
59
|
+
this._visible = true;
|
|
60
|
+
this._parent.psv.toggleAllAnnotations(true);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._parent.psv.addEventListener("picture-loaded", () => {
|
|
64
|
+
this._tags = getHashTags(this._parent.psv.getPictureMetadata());
|
|
65
|
+
if(
|
|
66
|
+
!this._visible && (
|
|
67
|
+
this._tags.length > 0 ||
|
|
68
|
+
hasAnnotations(this._parent.psv.getPictureMetadata())
|
|
69
|
+
)
|
|
70
|
+
) {
|
|
71
|
+
this._visible = true;
|
|
72
|
+
this._parent.psv.toggleAllAnnotations(true);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this._annotationsToggled = this._parent.psv.areAnnotationsVisible() || false;
|
|
77
|
+
this._parent.psv.addEventListener("annotations-toggled", e => {
|
|
78
|
+
this._annotationsToggled = e.detail.visible;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** @private */
|
|
84
|
+
render() {
|
|
85
|
+
return this._visible ? html`<div>
|
|
86
|
+
${this._tags.join(" ")}
|
|
87
|
+
<pnx-button
|
|
88
|
+
kind="outline"
|
|
89
|
+
style="vertical-align: middle"
|
|
90
|
+
title=${this._annotationsToggled ? this._parent._t?.pnx.semantics_hide_annotations : this._parent._t?.pnx.semantics_show_annotations}
|
|
91
|
+
active=${this._annotationsToggled ? "" : nothing}
|
|
92
|
+
@click=${() => this._parent.psv.toggleAllAnnotations(!this._annotationsToggled)}
|
|
93
|
+
>${fa(faDrawPolygon)}</pnx-button>
|
|
94
|
+
</div>` : nothing;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
customElements.define("pnx-hashtags", HashTags);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ListItem is a list entry, in a Material Design fashion.
|
|
5
|
+
* @class Panoramax.components.ui.ListItem
|
|
6
|
+
* @element pnx-list-item
|
|
7
|
+
* @slot `icon` The left icon (symbol for this item)
|
|
8
|
+
* @slot `action` The right icon (symbol for an interactive action)
|
|
9
|
+
* @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
|
|
10
|
+
* @example
|
|
11
|
+
* ```html
|
|
12
|
+
* <pnx-list-item title="My feature" subtitle="It is very cool">
|
|
13
|
+
* <img src="..." slot="icon" />
|
|
14
|
+
* <img src="..." slot="action" />
|
|
15
|
+
* </pnx-list-item>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export default class ListItem extends LitElement {
|
|
19
|
+
/** @private */
|
|
20
|
+
static styles = css`
|
|
21
|
+
.list-item {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
padding: 8px 16px;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
border-bottom: 1px solid #ddd;
|
|
27
|
+
font-family: var(--font-family);
|
|
28
|
+
background: var(--white);
|
|
29
|
+
min-height: 50px;
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
}
|
|
32
|
+
.list-item:hover { background: var(--widget-bg-hover); }
|
|
33
|
+
.icon {
|
|
34
|
+
margin-right: 16px;
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 10px;
|
|
38
|
+
}
|
|
39
|
+
.action { margin-left: 16px; }
|
|
40
|
+
.content {
|
|
41
|
+
flex: 1;
|
|
42
|
+
}
|
|
43
|
+
.title {
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
}
|
|
46
|
+
.subtitle {
|
|
47
|
+
font-size: 0.9em;
|
|
48
|
+
color: var(--grey-dark);
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Component properties.
|
|
54
|
+
* @memberof Panoramax.components.ui.ListItem#
|
|
55
|
+
* @type {Object}
|
|
56
|
+
* @property {string} title The item title
|
|
57
|
+
* @property {string} [subtitle] The item subtitle
|
|
58
|
+
*/
|
|
59
|
+
static properties = {
|
|
60
|
+
title: { type: String },
|
|
61
|
+
subtitle: { type: String },
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** @private */
|
|
65
|
+
render() {
|
|
66
|
+
return html`
|
|
67
|
+
<div class="list-item">
|
|
68
|
+
<div class="icon">
|
|
69
|
+
<slot name="icon"></slot>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="content">
|
|
72
|
+
<div class="title">${this.title}</div>
|
|
73
|
+
<div class="subtitle">${this.subtitle}</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="action">
|
|
76
|
+
<slot name="action"></slot>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
customElements.define("pnx-list-item", ListItem);
|
|
@@ -14,6 +14,7 @@ import "@photo-sphere-viewer/gallery-plugin/index.css";
|
|
|
14
14
|
import "@photo-sphere-viewer/markers-plugin/index.css";
|
|
15
15
|
import { Viewer as PSViewer } from "@photo-sphere-viewer/core";
|
|
16
16
|
import { VirtualTourPlugin } from "@photo-sphere-viewer/virtual-tour-plugin";
|
|
17
|
+
import { MarkersPlugin } from "@photo-sphere-viewer/markers-plugin";
|
|
17
18
|
import PhotoAdapter from "../../utils/PhotoAdapter";
|
|
18
19
|
|
|
19
20
|
|
|
@@ -77,6 +78,7 @@ PSViewer.useNewAnglesOrder = true;
|
|
|
77
78
|
* @fires Panoramax.components.ui.Photo#sequence-stopped
|
|
78
79
|
* @fires Panoramax.components.ui.Photo#pictures-navigation-changed
|
|
79
80
|
* @fires Panoramax.components.ui.Photo#ready
|
|
81
|
+
* @fires Panoramax.components.ui.Photo#annotations-toggled
|
|
80
82
|
* @example
|
|
81
83
|
* const psv = new Panoramax.components.ui.Photo(viewer, psvNode, {transitionDuration: 500})
|
|
82
84
|
*/
|
|
@@ -112,6 +114,7 @@ export default class Photo extends PSViewer {
|
|
|
112
114
|
linkOverlapAngle: Math.PI / 6,
|
|
113
115
|
}
|
|
114
116
|
}],
|
|
117
|
+
[MarkersPlugin, {}],
|
|
115
118
|
],
|
|
116
119
|
...options
|
|
117
120
|
});
|
|
@@ -126,6 +129,8 @@ export default class Photo extends PSViewer {
|
|
|
126
129
|
this._myVTour.config.transitionOptions = this._psvNodeTransition.bind(this);
|
|
127
130
|
this._clearArrows = this._myVTour.arrowsRenderer.clear.bind(this._myVTour.arrowsRenderer);
|
|
128
131
|
this._myVTour.arrowsRenderer.clear = () => {};
|
|
132
|
+
this._myMarkers = this.getPlugin(MarkersPlugin);
|
|
133
|
+
this._annotationsVisible = false;
|
|
129
134
|
this._sequencePlaying = false;
|
|
130
135
|
this._picturesNavigation = this._options.picturesNavigation || "any";
|
|
131
136
|
|
|
@@ -463,6 +468,11 @@ export default class Photo extends PSViewer {
|
|
|
463
468
|
else {
|
|
464
469
|
this.setOption("downloadUrl", null);
|
|
465
470
|
}
|
|
471
|
+
|
|
472
|
+
// Show annotations
|
|
473
|
+
if(this._annotationsVisible) {
|
|
474
|
+
this.toggleAllAnnotations(true);
|
|
475
|
+
}
|
|
466
476
|
}
|
|
467
477
|
|
|
468
478
|
this._onTilesStartLoading();
|
|
@@ -888,6 +898,133 @@ export default class Photo extends PSViewer {
|
|
|
888
898
|
}
|
|
889
899
|
}
|
|
890
900
|
|
|
901
|
+
/**
|
|
902
|
+
* Are there any picture annotations shown ?
|
|
903
|
+
* @returns {boolean} True if annotations are visible
|
|
904
|
+
* @memberof Panoramax.components.ui.Photo#
|
|
905
|
+
*/
|
|
906
|
+
areAnnotationsVisible() {
|
|
907
|
+
return this._annotationsVisible;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Toggle visibility of picture annotations
|
|
912
|
+
* @param {boolean} visible True to make visible, false to hide
|
|
913
|
+
* @memberof Panoramax.components.ui.Photo#
|
|
914
|
+
*/
|
|
915
|
+
toggleAllAnnotations(visible) {
|
|
916
|
+
const meta = this.getPictureMetadata();
|
|
917
|
+
if(!meta) {
|
|
918
|
+
this._myMarkers.clearMarkers();
|
|
919
|
+
throw new Error("No picture currently selected");
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if(!visible) { this._myMarkers.clearMarkers(); }
|
|
923
|
+
else {
|
|
924
|
+
let annotations = meta.properties.annotations;
|
|
925
|
+
if(annotations.length === 0) { console.warn("No annotation available on picture", meta.id); }
|
|
926
|
+
|
|
927
|
+
const picBData = this.state.textureData.panoData?.baseData;
|
|
928
|
+
|
|
929
|
+
annotations = annotations.map(a => {
|
|
930
|
+
// Get original HD picture dimensions
|
|
931
|
+
const origPicDim = this.getPictureMetadata().properties["pers:interior_orientation"].sensor_array_dimensions;
|
|
932
|
+
|
|
933
|
+
if(!origPicDim) {
|
|
934
|
+
console.warn("Picture lacks pers:interior_orientation.sensor_array_dimensions property, can't compute marker");
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const shape = a.shape.coordinates.map(c1 => c1.map(c2 => {
|
|
939
|
+
// Px coordinates in shown image
|
|
940
|
+
const pxShown = [
|
|
941
|
+
c2[0] * picBData.croppedWidth / origPicDim[0],
|
|
942
|
+
c2[1] * picBData.croppedHeight / origPicDim[1]
|
|
943
|
+
];
|
|
944
|
+
|
|
945
|
+
// Coords in %
|
|
946
|
+
const pct = [
|
|
947
|
+
(picBData.croppedX + pxShown[0]) / picBData.fullWidth,
|
|
948
|
+
(picBData.croppedY + pxShown[1]) / picBData.fullHeight
|
|
949
|
+
];
|
|
950
|
+
|
|
951
|
+
// Coords in radians as center offset
|
|
952
|
+
return [
|
|
953
|
+
(pct[0] - 0.5) * 2 * Math.PI,
|
|
954
|
+
(0.5 - pct[1]) * Math.PI,
|
|
955
|
+
];
|
|
956
|
+
}));
|
|
957
|
+
|
|
958
|
+
return {
|
|
959
|
+
id: `annotation-${a.id}`,
|
|
960
|
+
polygon: shape,
|
|
961
|
+
data: { id: a.id },
|
|
962
|
+
svgStyle: {
|
|
963
|
+
stroke: "var(--orange)",
|
|
964
|
+
strokeWidth: "3px",
|
|
965
|
+
fill: "var(--orange-transparent)",
|
|
966
|
+
cursor: "pointer",
|
|
967
|
+
},
|
|
968
|
+
};
|
|
969
|
+
});
|
|
970
|
+
this._myMarkers.setMarkers(annotations);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const sendEvent = this._annotationsVisible != visible;
|
|
974
|
+
this._annotationsVisible = visible;
|
|
975
|
+
|
|
976
|
+
if(sendEvent) {
|
|
977
|
+
/**
|
|
978
|
+
* Event for pictures annotation visibility change
|
|
979
|
+
* @event Panoramax.components.ui.Photo#annotations-toggled
|
|
980
|
+
* @type {CustomEvent}
|
|
981
|
+
* @property {boolean} detail.visible True if they are visible
|
|
982
|
+
*/
|
|
983
|
+
this.dispatchEvent(new CustomEvent("annotations-toggled", { detail: { visible } }));
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Make view centered and zoomed on given annotation.
|
|
989
|
+
* @param {string} id The annotation UUID
|
|
990
|
+
* @memberof Panoramax.components.ui.Photo#
|
|
991
|
+
*/
|
|
992
|
+
focusOnAnnotation(id) {
|
|
993
|
+
if(!this.areAnnotationsVisible()) { this.toggleAllAnnotations(true); }
|
|
994
|
+
this.unfocusAnnotation();
|
|
995
|
+
|
|
996
|
+
const annotationId = `annotation-${id}`;
|
|
997
|
+
this._myMarkers.updateMarker({
|
|
998
|
+
id: annotationId,
|
|
999
|
+
svgStyle: {
|
|
1000
|
+
stroke: "var(--red)",
|
|
1001
|
+
strokeWidth: "3px",
|
|
1002
|
+
fill: "var(--red-transparent)",
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
this._myMarkers.gotoMarker(annotationId, 0);
|
|
1006
|
+
this.zoom(65);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Remove focus styling on annotations.
|
|
1011
|
+
* @memberof Panoramax.components.ui.Photo#
|
|
1012
|
+
*/
|
|
1013
|
+
unfocusAnnotation() {
|
|
1014
|
+
Object.keys(this._myMarkers.markers)
|
|
1015
|
+
.filter(id => id.startsWith("annotation-"))
|
|
1016
|
+
.forEach(id => {
|
|
1017
|
+
this._myMarkers.updateMarker({
|
|
1018
|
+
id,
|
|
1019
|
+
svgStyle: {
|
|
1020
|
+
stroke: "var(--orange)",
|
|
1021
|
+
strokeWidth: "3px",
|
|
1022
|
+
fill: "var(--orange-transparent)",
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
|
|
891
1028
|
/**
|
|
892
1029
|
* Force reload of texture and tiles.
|
|
893
1030
|
* @memberof Panoramax.components.ui.Photo#
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { LitElement, html, css, nothing } from "lit";
|
|
2
|
+
import { table } from "../styles";
|
|
3
|
+
import { groupByPrefix } from "../../utils/semantics";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Semantics Tables display all semantics tags from either a picture or an annotation.
|
|
7
|
+
*
|
|
8
|
+
* @class Panoramax.components.ui.SemanticsTable
|
|
9
|
+
* @element pnx-semantics-table
|
|
10
|
+
* @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
|
|
11
|
+
* @example
|
|
12
|
+
* ```html
|
|
13
|
+
* <pnx-semantics-table source=${picture.properties} ._t=${viewer._t} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export default class SemanticsTable extends LitElement {
|
|
17
|
+
/** @private */
|
|
18
|
+
static styles = [ table, css`
|
|
19
|
+
th:first-child, td:first-child { width: 30%; }
|
|
20
|
+
td { vertical-align: top; }
|
|
21
|
+
|
|
22
|
+
td small { color: var(--grey-semi-dark); }
|
|
23
|
+
.qualifiers { margin-top: 5px; }
|
|
24
|
+
.qualifiers > b { margin: 8px 0; }
|
|
25
|
+
.qualifiers ul {
|
|
26
|
+
list-style: none;
|
|
27
|
+
margin: 0px 0px 0px 15px;
|
|
28
|
+
padding: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
img.prefix-logo {
|
|
32
|
+
max-width: 15px;
|
|
33
|
+
max-height: 15px;
|
|
34
|
+
aspect-ratio: 1;
|
|
35
|
+
vertical-align: middle;
|
|
36
|
+
}
|
|
37
|
+
` ];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Component properties.
|
|
41
|
+
* @memberof Panoramax.components.ui.SemanticsTable#
|
|
42
|
+
* @type {Object}
|
|
43
|
+
* @property {object} [source] The picture or annotation feature (having a `semantics` property)
|
|
44
|
+
*/
|
|
45
|
+
static properties = {
|
|
46
|
+
source: {type: Object},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
render() {
|
|
50
|
+
/* eslint-disable indent */
|
|
51
|
+
if(!this.source) { return nothing; }
|
|
52
|
+
|
|
53
|
+
const groups = groupByPrefix(this.source.semantics);
|
|
54
|
+
|
|
55
|
+
return html`<table>
|
|
56
|
+
<thead>
|
|
57
|
+
<tr><th>${this._t.pnx.semantics_key}</th><th>${this._t.pnx.semantics_value}</th></tr>
|
|
58
|
+
</thead>
|
|
59
|
+
<tbody>
|
|
60
|
+
${groups.map(g => g.tags.map(tag => html`
|
|
61
|
+
<tr>
|
|
62
|
+
<td>
|
|
63
|
+
${g.prefix != ""
|
|
64
|
+
? (g.logo
|
|
65
|
+
? html`<img src=${g.logo} class="prefix-logo" alt=${g.prefix} title=${g.title} />`
|
|
66
|
+
: html`<small>(${g.prefix})</small>`)
|
|
67
|
+
: nothing
|
|
68
|
+
}
|
|
69
|
+
${g.key_transform ? g.key_transform(tag, this._t) : tag.key}
|
|
70
|
+
</td>
|
|
71
|
+
<td>
|
|
72
|
+
${g.value_transform ? g.value_transform(tag, this._t) : tag.value}
|
|
73
|
+
${tag.qualifiers ? html`<div class="qualifiers">
|
|
74
|
+
<b>${this._t.pnx.semantics_qualifiers}</b>
|
|
75
|
+
<ul>
|
|
76
|
+
${tag.qualifiers.map(q => html`<li>${q.subkey} = ${q.value}</li>`)}
|
|
77
|
+
</ul>
|
|
78
|
+
</div>` : nothing}
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
`))}
|
|
82
|
+
</tbody>
|
|
83
|
+
</table>`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
customElements.define("pnx-semantics-table", SemanticsTable);
|
|
@@ -7,8 +7,10 @@ export {default as ButtonGroup} from "./ButtonGroup";
|
|
|
7
7
|
export {default as Button} from "./Button";
|
|
8
8
|
export {default as CopyButton} from "./CopyButton";
|
|
9
9
|
export {default as Grade} from "./Grade";
|
|
10
|
+
export {default as HashTags} from "./HashTags";
|
|
10
11
|
export {default as LinkButton} from "./LinkButton";
|
|
11
12
|
export {default as ListGroup} from "./ListGroup";
|
|
13
|
+
export {default as ListItem} from "./ListItem";
|
|
12
14
|
export {default as Loader} from "./Loader";
|
|
13
15
|
export {default as Map} from "./Map";
|
|
14
16
|
export {default as MapMore} from "./MapMore";
|
|
@@ -17,6 +19,7 @@ export {default as Popup} from "./Popup";
|
|
|
17
19
|
export {default as ProgressBar} from "./ProgressBar";
|
|
18
20
|
export {default as QualityScore} from "./QualityScore";
|
|
19
21
|
export {default as SearchBar} from "./SearchBar";
|
|
22
|
+
export {default as SemanticsTable} from "./SemanticsTable";
|
|
20
23
|
export {default as TogglableGroup} from "./TogglableGroup";
|
|
21
24
|
import * as widgets from "./widgets";
|
|
22
25
|
export {widgets};
|
package/src/img/osm.svg
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="240mm"
|
|
6
|
+
height="239.99869mm"
|
|
7
|
+
viewBox="0 0 240 239.99869"
|
|
8
|
+
id="svg6743"
|
|
9
|
+
version="1.1"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1" />
|
|
14
|
+
<g
|
|
15
|
+
id="layer1"
|
|
16
|
+
transform="translate(-30,-27.000656)">
|
|
17
|
+
<g
|
|
18
|
+
id="g7426"
|
|
19
|
+
transform="translate(322.142,-53.972906)">
|
|
20
|
+
<path
|
|
21
|
+
style="display:inline;fill:#76c551;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.61532"
|
|
22
|
+
id="path33449"
|
|
23
|
+
d="m -81.063095,208.94609 c -11.906339,9.92421 -27.194885,15.8987 -43.860635,15.8987 -4.95854,0 -9.78728,-0.55158 -14.45059,-1.55504 l -4.79842,5.8446 c -10.66449,-2.85736 -20.38429,-7.94043 -28.6326,-14.74578 l -12.89559,12.92212 1.58177,9.54347 -47.96347,47.98965 4.63827,1.7685 45.04032,-17.15658 45.0402,17.15658 45.040312,-17.15658 17.158544,-45.03976 z" />
|
|
24
|
+
<path
|
|
25
|
+
d="m -272.48495,89.294626 17.1585,45.040274 -17.1585,45.03993 17.1585,45.03972 -17.1585,45.03977 10.53634,4.02267 57.2385,-57.21102 10.26764,1.58169 12.46673,-12.46669 c -7.35519,-8.52221 -12.86173,-18.6902 -15.87027,-29.92032 l 5.8446,-4.79842 c -1.00531,-4.66323 -1.55503,-9.49189 -1.55503,-14.45044 0,-24.76984 13.21043,-46.53472 32.94893,-58.606534 l -21.79693,-8.31063 -45.04019,17.158544 z"
|
|
26
|
+
id="path33447"
|
|
27
|
+
style="display:inline;fill:#76c551;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.61532" />
|
|
28
|
+
<path
|
|
29
|
+
style="display:inline;fill:#59a336;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.61532"
|
|
30
|
+
id="path33451"
|
|
31
|
+
d="m -81.063095,208.94609 c -11.906339,9.92421 -27.194885,15.8987 -43.860635,15.8987 -4.95854,0 -9.78728,-0.55158 -14.45059,-1.55504 l -4.79842,5.8446 -28.67744,-15.11228 -12.85075,13.28862 1.58177,9.54347 46.85284,7.05799 -0.0982,42.70016 45.040313,-17.15658 17.158544,-45.03976 z" />
|
|
32
|
+
<ellipse
|
|
33
|
+
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.7048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
|
|
34
|
+
id="ellipse33453"
|
|
35
|
+
cx="-122.83165"
|
|
36
|
+
cy="155.70955"
|
|
37
|
+
rx="59.801929"
|
|
38
|
+
ry="60.929199" />
|
|
39
|
+
<path
|
|
40
|
+
style="opacity:1;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:12.7048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
|
|
41
|
+
d="m -70.867543,179.77128 c -4.021615,-66.77094 -70.182367,-83.311142 -110.499567,-45.77004 21.03677,-28.56709 48.05279,-46.045197 78.56638,-33.40602 30.51362,12.63913 41.797793,45.98173 31.933187,79.17606 z"
|
|
42
|
+
id="path33455" />
|
|
43
|
+
<path
|
|
44
|
+
id="path33457"
|
|
45
|
+
d="m -73.395575,102.22726 c -28.338185,-28.338264 -74.283665,-28.338264 -102.621895,0 -17.93779,17.93776 -24.48523,42.91327 -19.71413,66.02883 l -11.27485,8.16937 c 5.13477,13.61756 12.1317,21.8574 16.91241,26.2963 l -10.22839,10.22834 h -10.80252 l -81.01705,81.01654 27.00583,27.00561 81.01701,-81.01683 v -10.80225 l 10.19464,-10.19425 c 4.56162,4.75244 12.69997,11.38138 25.75686,16.30469 l 7.86543,-10.90386 c 23.35858,5.12197 48.742245,-1.34701 66.906498,-19.5115 28.338386,-28.33732 28.33819,-74.28276 1.57e-4,-102.62099 z m -12.962835,12.96296 c 21.179095,21.17921 21.179095,55.51685 0,76.69579 -21.17902,21.17897 -55.51721,21.17897 -76.6963,0 -21.17898,-21.17894 -21.17898,-55.51658 0,-76.69579 21.17929,-21.179216 55.51728,-21.179216 76.6963,0 z"
|
|
46
|
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#2f2f2f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.76059;marker:none;enable-background:accumulate" />
|
|
47
|
+
</g>
|
|
48
|
+
</g>
|
|
49
|
+
</svg>
|
package/src/img/wd.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 18"><path d="M2.5 1h1v16h-1zm2 0h3v16h-3zm4 0h3v16h-3z" fill="#900"/><path d="M12.5 1h1v16h-1zm2 0h1v16h-1zm12 0h1v16h-1zm2 0h1v16h-1z" fill="#396"/><path d="M16.5 1h3v16h-3zm4 0h1v16h-1zm2 0h3v16h-3z" fill="#069"/></svg>
|