@panoramax/web-viewer 4.0.1 → 4.0.2-develop-9d664bb8
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 +31 -3
- package/build/index.css +1 -1
- package/build/index.css.map +1 -1
- package/build/index.js +150 -51
- package/build/index.js.map +1 -1
- package/config/jest/mocks.js +2 -1
- package/docs/03_URL_settings.md +1 -1
- package/docs/09_Develop.md +5 -1
- package/docs/reference/components/core/Viewer.md +1 -1
- package/docs/reference/components/ui/CopyButton.md +1 -0
- package/docs/reference/components/ui/Map.md +13 -0
- package/docs/reference/components/ui/MapMore.md +13 -0
- package/docs/reference/components/ui/Photo.md +1 -1
- package/docs/reference/components/ui/widgets/CopyCoordinates.md +32 -0
- package/docs/reference/components/ui/widgets/GeoSearch.md +5 -1
- package/docs/reference/utils/API.md +1 -1
- package/docs/reference.md +1 -0
- package/docs/tutorials/migrate_v4.md +1 -1
- package/docs/tutorials/synced_coverage.md +1 -1
- package/mkdocs.yml +1 -0
- package/package.json +1 -1
- package/src/components/core/CoverageMap.js +2 -2
- package/src/components/core/PhotoViewer.js +5 -1
- package/src/components/core/Viewer.js +10 -5
- package/src/components/menus/PictureLegend.js +7 -4
- package/src/components/menus/PictureMetadata.js +23 -2
- package/src/components/styles.js +61 -0
- package/src/components/ui/ButtonGroup.css +2 -0
- package/src/components/ui/CopyButton.js +3 -1
- package/src/components/ui/Map.js +35 -4
- package/src/components/ui/Photo.js +4 -2
- package/src/components/ui/TogglableGroup.js +1 -1
- package/src/components/ui/widgets/CopyCoordinates.js +75 -0
- package/src/components/ui/widgets/GeoSearch.js +13 -5
- package/src/components/ui/widgets/Legend.js +1 -1
- package/src/components/ui/widgets/OSMEditors.js +2 -2
- package/src/components/ui/widgets/PictureLegendActions.js +1 -1
- package/src/components/ui/widgets/Player.js +1 -0
- package/src/components/ui/widgets/index.js +1 -0
- package/src/translations/en.json +6 -2
- package/src/translations/fr.json +6 -2
- package/src/translations/it.json +3 -1
- package/src/translations/ti.json +9 -0
- package/src/utils/API.js +1 -1
- package/src/utils/InitParameters.js +2 -2
- package/src/utils/geocoder.js +155 -67
- package/src/utils/index.js +2 -1
- package/src/utils/picture.js +6 -1
- package/src/utils/services.js +57 -0
- package/src/utils/utils.js +18 -5
- package/tests/components/ui/Map.test.js +7 -3
- package/tests/utils/InitParameters.test.js +15 -15
- package/tests/utils/__snapshots__/geocoder.test.js.snap +0 -6
- package/tests/utils/geocoder.test.js +1 -1
- package/tests/utils/utils.test.js +136 -109
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { degToDms } from "../../../utils/utils";
|
|
3
|
+
import { fa } from "../../../utils/widgets";
|
|
4
|
+
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
|
5
|
+
import { btngroup } from "../../styles";
|
|
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(7)}, ${this.gps[0].toFixed(7)}`,
|
|
45
|
+
ddeclonlat: `${this.gps[0].toFixed(7)}, ${this.gps[1].toFixed(7)}`,
|
|
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);
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
import maplibregl from "!maplibre-gl";
|
|
3
3
|
|
|
4
4
|
import { LitElement, html } from "lit";
|
|
5
|
-
import { forwardGeocodingBAN, forwardGeocodingNominatim } from "../../../utils/geocoder";
|
|
5
|
+
import { forwardGeocodingBAN, forwardGeocodingStandard, forwardGeocodingNominatim } from "../../../utils/geocoder";
|
|
6
6
|
import "./GeoSearch.css";
|
|
7
7
|
|
|
8
|
-
const GEOCODER_ENGINES = {
|
|
8
|
+
const GEOCODER_ENGINES = {
|
|
9
|
+
"ban": forwardGeocodingBAN,
|
|
10
|
+
"standard": forwardGeocodingStandard,
|
|
11
|
+
"nominatim": forwardGeocodingNominatim
|
|
12
|
+
};
|
|
9
13
|
|
|
10
14
|
/**
|
|
11
15
|
* Ready-to-use geocoder search bar.
|
|
@@ -14,7 +18,11 @@ const GEOCODER_ENGINES = { "ban": forwardGeocodingBAN, "nominatim": forwardGeoco
|
|
|
14
18
|
* @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
|
|
15
19
|
* @example
|
|
16
20
|
* ```html
|
|
21
|
+
* <!-- Default geocoder -->
|
|
17
22
|
* <pnx-widget-geosearch _parent=${viewer} />
|
|
23
|
+
*
|
|
24
|
+
* <!-- Custom-URL geocoder -->
|
|
25
|
+
* <pnx-widget-geosearch geocoder="https://photon.komoot.io/api" _parent=${viewer} />
|
|
18
26
|
* ```
|
|
19
27
|
*/
|
|
20
28
|
export default class GeoSearch extends LitElement {
|
|
@@ -22,7 +30,7 @@ export default class GeoSearch extends LitElement {
|
|
|
22
30
|
* Component properties.
|
|
23
31
|
* @memberof Panoramax.components.ui.widgets.GeoSearch#
|
|
24
32
|
* @type {Object}
|
|
25
|
-
* @property {string} [geocoder=nominatim] The geocoder engine to use (nominatim, ban)
|
|
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)
|
|
26
34
|
*/
|
|
27
35
|
static properties = {
|
|
28
36
|
geocoder: {type: String},
|
|
@@ -54,7 +62,7 @@ export default class GeoSearch extends LitElement {
|
|
|
54
62
|
connectedCallback() {
|
|
55
63
|
super.connectedCallback();
|
|
56
64
|
|
|
57
|
-
this._geocoderEngine = GEOCODER_ENGINES[this.geocoder];
|
|
65
|
+
this._geocoderEngine = GEOCODER_ENGINES[this.geocoder] || (config => GEOCODER_ENGINES.standard(config, this.geocoder));
|
|
58
66
|
this._parent?.onceMapReady?.().then(() => {
|
|
59
67
|
this._geolocate = this._geolocateCtrl.onAdd(this._parent.map);
|
|
60
68
|
this._geolocate.setAttribute("slot", "pre");
|
|
@@ -84,7 +92,7 @@ export default class GeoSearch extends LitElement {
|
|
|
84
92
|
else {
|
|
85
93
|
return this._geocoderEngine({
|
|
86
94
|
query,
|
|
87
|
-
limit:
|
|
95
|
+
limit: 5,
|
|
88
96
|
//bbox: this._parent.map.getBounds().toArray().map(d => d.join(",")).join(","),
|
|
89
97
|
proximity: this._parent.map.getCenter().lat+","+this._parent.map.getCenter().lng,
|
|
90
98
|
}).then(data => {
|
|
@@ -81,7 +81,7 @@ export default class Legend extends LitElement {
|
|
|
81
81
|
>
|
|
82
82
|
<img class="logo" src=${PanoramaxImg} alt="" />
|
|
83
83
|
<div>
|
|
84
|
-
|
|
84
|
+
${this._parent?._t.pnx.whats_panoramax}
|
|
85
85
|
<pnx-link-button
|
|
86
86
|
title=${this._parent?._t.map.more_panoramax}
|
|
87
87
|
kind="superinline"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { LitElement, html, nothing, css } from "lit";
|
|
2
2
|
import { fa } from "../../../utils/widgets";
|
|
3
3
|
import { josmBboxParameters } from "../../../utils/utils";
|
|
4
|
+
import { IdEditorURL } from "../../../utils/services";
|
|
4
5
|
import { faLocationDot } from "@fortawesome/free-solid-svg-icons/faLocationDot";
|
|
5
6
|
import { faSatelliteDish } from "@fortawesome/free-solid-svg-icons/faSatelliteDish";
|
|
6
7
|
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
|
|
7
8
|
|
|
8
9
|
const JOSM_REMOTE_URL = "http://127.0.0.1:8111";
|
|
9
|
-
const ID_URL = "https://www.openstreetmap.org/edit?editor=id";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* OSM Editors component offers direct links to OpenStreetMap's iD and JOSM editors.
|
|
@@ -126,7 +126,7 @@ export default class OSMEditors extends LitElement {
|
|
|
126
126
|
"photo_overlay": "panoramax",
|
|
127
127
|
"photo": `panoramax/${this._pic.id}`,
|
|
128
128
|
};
|
|
129
|
-
const idUrl = idOpts && `${
|
|
129
|
+
const idUrl = idOpts && `${IdEditorURL()}#${new URLSearchParams(idOpts).toString()}`;
|
|
130
130
|
|
|
131
131
|
return html`
|
|
132
132
|
<pnx-link-button
|
|
@@ -58,7 +58,7 @@ export default class PictureLegendActions extends LitElement {
|
|
|
58
58
|
|
|
59
59
|
/** @private */
|
|
60
60
|
_closeGroup() {
|
|
61
|
-
this.renderRoot.querySelector("
|
|
61
|
+
this.renderRoot.querySelector("#pic-legend-headline-menu").close();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** @private */
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @module Panoramax:components:ui:widgets
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export {default as CopyCoordinates} from "./CopyCoordinates";
|
|
6
7
|
export {default as GeoSearch} from "./GeoSearch";
|
|
7
8
|
export {default as Legend} from "./Legend";
|
|
8
9
|
export {default as MapFiltersButton} from "./MapFiltersButton";
|
package/src/translations/en.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"share_embed": "Embed on your website",
|
|
40
40
|
"share_embed_docs": "Read more about embed configuration",
|
|
41
41
|
"share_print": "Print",
|
|
42
|
+
"whats_panoramax": "Panoramax is the geo-commons for territories' pictures.",
|
|
42
43
|
"copy": "Copy",
|
|
43
44
|
"copied": "Copied",
|
|
44
45
|
"error": "We have a problem…",
|
|
@@ -156,10 +157,11 @@
|
|
|
156
157
|
"metadata_camera_resolution": "Resolution",
|
|
157
158
|
"metadata_camera_focal_length": "Focal length",
|
|
158
159
|
"metadata_location": "Position",
|
|
159
|
-
"
|
|
160
|
-
"metadata_location_latitude": "Latitude",
|
|
160
|
+
"metadata_location_coordinates": "Coordinates (longitude, latitude)",
|
|
161
161
|
"metadata_location_orientation": "Direction",
|
|
162
162
|
"metadata_location_precision": "Positioning precision",
|
|
163
|
+
"metadata_location_copy": "Copy {v}",
|
|
164
|
+
"metadata_location_copy_more": "More coordinates copy options",
|
|
163
165
|
"metadata_quality": "Quality",
|
|
164
166
|
"metadata_quality_help": "Know more about Quality Score",
|
|
165
167
|
"metadata_quality_score": "Global score",
|
|
@@ -169,6 +171,8 @@
|
|
|
169
171
|
"metadata_exif": "EXIF",
|
|
170
172
|
"metadata_exif_name": "Tag",
|
|
171
173
|
"metadata_exif_value": "Value",
|
|
174
|
+
"metadata_exif_doc": "Docs for EXIF tags",
|
|
175
|
+
"metadata_exif_doc_title": "Go to Exiv2 documentation to have full details on EXIF and XMP tags definitions",
|
|
172
176
|
"report": "Report",
|
|
173
177
|
"report_auth": "This report will be sent using your account \"{a}\"",
|
|
174
178
|
"report_nature_label": "Nature of the issue",
|
package/src/translations/fr.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"share_embed": "Intégration sur votre site",
|
|
40
40
|
"share_embed_docs": "Découvrir plus de possibilités pour l'intégration sur votre site",
|
|
41
41
|
"share_print": "Imprimer",
|
|
42
|
+
"whats_panoramax": "Panoramax est le géocommun des photos des territoires.",
|
|
42
43
|
"copy": "Copier",
|
|
43
44
|
"copied": "Copié",
|
|
44
45
|
"error": "On a un problème…",
|
|
@@ -156,10 +157,11 @@
|
|
|
156
157
|
"metadata_camera_resolution": "Résolution",
|
|
157
158
|
"metadata_camera_focal_length": "Longueur focale",
|
|
158
159
|
"metadata_location": "Position",
|
|
159
|
-
"
|
|
160
|
-
"metadata_location_latitude": "Latitude",
|
|
160
|
+
"metadata_location_coordinates": "Coordonnées (longitude, latitude)",
|
|
161
161
|
"metadata_location_orientation": "Direction",
|
|
162
162
|
"metadata_location_precision": "Précision du positionnement",
|
|
163
|
+
"metadata_location_copy": "Copier {v}",
|
|
164
|
+
"metadata_location_copy_more": "Copier d'autres formats de coordonnées",
|
|
163
165
|
"metadata_quality": "Qualité",
|
|
164
166
|
"metadata_quality_help": "En savoir plus sur le score de qualité",
|
|
165
167
|
"metadata_quality_score": "Note globale",
|
|
@@ -169,6 +171,8 @@
|
|
|
169
171
|
"metadata_exif": "EXIF",
|
|
170
172
|
"metadata_exif_name": "Balise",
|
|
171
173
|
"metadata_exif_value": "Valeur",
|
|
174
|
+
"metadata_exif_doc": "Doc des attributs EXIF",
|
|
175
|
+
"metadata_exif_doc_title": "Acééder à la doc Exiv2 pour en savoir plus sur les attributs EXIF et XMP",
|
|
172
176
|
"report": "Signaler",
|
|
173
177
|
"report_auth": "Ce signalement sera envoyé en utilisant votre compte \"{a}\"",
|
|
174
178
|
"report_nature_label": "Nature du problème",
|
package/src/translations/it.json
CHANGED
|
@@ -183,7 +183,9 @@
|
|
|
183
183
|
"contribute_id": "Contribuisci a OpenStreetMap con l’editor iD",
|
|
184
184
|
"geo_uri": "App esterna",
|
|
185
185
|
"filter_date_6months": "6 mesi",
|
|
186
|
-
"picture_all": "Tutte"
|
|
186
|
+
"picture_all": "Tutte",
|
|
187
|
+
"metadata_exif_doc": "Doc per gli attributi EXIF",
|
|
188
|
+
"metadata_exif_doc_title": "Vai alla documentazione di Exiv2 per i dettagli completi sulle definizioni degli attributi EXIF e XMP"
|
|
187
189
|
},
|
|
188
190
|
"psv": {
|
|
189
191
|
"loadError": "Impossibile caricare l’immagine panoramica",
|
package/src/utils/API.js
CHANGED
|
@@ -8,7 +8,7 @@ import { isNullId } from "./utils";
|
|
|
8
8
|
* @typicalname api
|
|
9
9
|
* @fires Panoramax.utils.API#ready
|
|
10
10
|
* @fires Panoramax.utils.API#broken
|
|
11
|
-
* @param {string} endpoint The endpoint. It corresponds to the <a href="https://github.com/radiantearth/stac-api-spec/blob/main/overview.md#example-landing-page">STAC landing page</a>, with all links describing the API
|
|
11
|
+
* @param {string} endpoint The endpoint. It corresponds to the <a href="https://github.com/radiantearth/stac-api-spec/blob/main/overview.md#example-landing-page">STAC landing page</a>, with all links describing the API capabilities.
|
|
12
12
|
* @param {object} [options] Options
|
|
13
13
|
* @param {string|object} [options.style] General map style
|
|
14
14
|
* @param {string} [options.tiles] API route serving pictures & sequences vector tiles
|
|
@@ -323,7 +323,7 @@ export function alterPSVState(psv, params) {
|
|
|
323
323
|
* @param {Map} map The MapLibre component to change.
|
|
324
324
|
* @param {object} params The parameters to apply.
|
|
325
325
|
*/
|
|
326
|
-
export function alterMapState(map, params) {
|
|
326
|
+
export async function alterMapState(map, params) {
|
|
327
327
|
// Map position
|
|
328
328
|
const mapOpts = getMapPositionFromString(params.map, map);
|
|
329
329
|
if(mapOpts) {
|
|
@@ -333,7 +333,7 @@ export function alterMapState(map, params) {
|
|
|
333
333
|
// Visible users
|
|
334
334
|
let vu = Array.isArray(params.users) ? params.users : (params.users || "").split(",");
|
|
335
335
|
if(vu.length === 0 || (vu.length === 1 && vu[0].trim() === "")) { vu = ["geovisio"]; }
|
|
336
|
-
map.setVisibleUsers(vu);
|
|
336
|
+
await map.setVisibleUsers(vu);
|
|
337
337
|
|
|
338
338
|
// Change map filters
|
|
339
339
|
map.setFilters?.(paramsToMapFilters(params));
|
package/src/utils/geocoder.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
|
|
2
2
|
import maplibregl from "!maplibre-gl";
|
|
3
3
|
|
|
4
|
+
import { NominatimBaseUrl, AdresseDataGouvBaseURL } from "./services";
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Transforms a set of parameters into an URL-ready string
|
|
6
8
|
* It also removes null/undefined values
|
|
@@ -20,65 +22,38 @@ function geocoderParamsToURLString(params) {
|
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Transforms Nominatim search result into a nice-to-display address.
|
|
23
|
-
* @param {object}
|
|
25
|
+
* @param {object} props The Nominatim API feature properties
|
|
24
26
|
* @returns {string} The clean-up string for display
|
|
25
27
|
* @private
|
|
26
28
|
*/
|
|
27
|
-
function nominatimAddressToPlaceName(
|
|
29
|
+
function nominatimAddressToPlaceName(props) {
|
|
28
30
|
// API format @ https://nominatim.org/release-docs/develop/api/Output/#addressdetails
|
|
29
|
-
if(!addr || typeof addr != "object") { return ""; }
|
|
30
|
-
|
|
31
|
-
let res = "";
|
|
32
|
-
|
|
33
|
-
// House n°-like
|
|
34
|
-
if(addr.house_number) { res = addr.house_number; }
|
|
35
|
-
else if(addr.house_name) { res = addr.house_name; }
|
|
36
|
-
else {
|
|
37
|
-
const potentialNames = [
|
|
38
|
-
"emergency", "historic", "military", "natural", "landuse", "place", "railway", "man_made",
|
|
39
|
-
"aerialway", "boundary", "amenity", "aeroway", "club", "craft", "leisure", "office",
|
|
40
|
-
"mountain_pass", "shop", "tourism", "bridge", "tunnel", "waterway", "park"
|
|
41
|
-
];
|
|
42
|
-
for(let pn of potentialNames) {
|
|
43
|
-
if(addr[pn]) {
|
|
44
|
-
res = addr[pn];
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
31
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// Hamlet-like
|
|
56
|
-
"hamlet", "croft", "isolated_dwelling",
|
|
57
|
-
// Zone Indus-like
|
|
58
|
-
"farm", "farmyard", "industrial", "commercial", "retail", "city_block", "residential",
|
|
59
|
-
// Quarter-like
|
|
60
|
-
"neighbourhood", "allotments", "quarter",
|
|
61
|
-
// Fallback to road if nothing else found
|
|
62
|
-
"road"
|
|
63
|
-
];
|
|
64
|
-
for(let pn of potentialNames) {
|
|
65
|
-
if(addr[pn]) {
|
|
66
|
-
street = addr[pn];
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
32
|
+
const nameKind = [
|
|
33
|
+
"house_name", "emergency", "historic", "military", "natural", "landuse", "place", "railway", "man_made",
|
|
34
|
+
"aerialway", "boundary", "amenity", "aeroway", "club", "craft", "leisure", "office",
|
|
35
|
+
"mountain_pass", "shop", "tourism", "bridge", "tunnel", "waterway", "park"
|
|
36
|
+
].find(pn => props?.address?.[pn]);
|
|
71
37
|
|
|
72
|
-
|
|
73
|
-
|
|
38
|
+
const localityKind = [
|
|
39
|
+
"hamlet", "croft", "isolated_dwelling",
|
|
40
|
+
"farm", "farmyard", "industrial", "commercial", "retail", "city_block", "residential",
|
|
41
|
+
"neighbourhood", "allotments", "quarter",
|
|
42
|
+
].find(pn => props?.address?.[pn]);
|
|
74
43
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
44
|
+
const stprops = {
|
|
45
|
+
type: props?.addresstype || props?.type,
|
|
46
|
+
name: (props?.name?.length > 0 ? props.name : null) || (nameKind ? props.address[nameKind] : undefined),
|
|
47
|
+
housenumber: props?.address?.house_number,
|
|
48
|
+
street: props?.address?.road,
|
|
49
|
+
locality: localityKind ? props.address[localityKind] : undefined,
|
|
50
|
+
city: props?.address?.village || props?.address?.town || props?.address?.city || props?.address?.municipality,
|
|
51
|
+
county: props?.address?.county,
|
|
52
|
+
state: props?.address?.state,
|
|
53
|
+
country: props?.address?.country,
|
|
54
|
+
};
|
|
80
55
|
|
|
81
|
-
return
|
|
56
|
+
return geocodeJsonToPlaceName(stprops);
|
|
82
57
|
}
|
|
83
58
|
|
|
84
59
|
/**
|
|
@@ -94,13 +69,13 @@ export function forwardGeocodingNominatim(config) {
|
|
|
94
69
|
viewbox: config.bbox,
|
|
95
70
|
};
|
|
96
71
|
|
|
97
|
-
return fetch(
|
|
72
|
+
return fetch(`${NominatimBaseUrl()}/search?${geocoderParamsToURLString(params)}&format=geojson&polygon_geojson=1&addressdetails=1`)
|
|
98
73
|
.then(res => res.json())
|
|
99
74
|
.then(res => {
|
|
100
75
|
const finalRes = { features: [] };
|
|
101
76
|
const listedNames = [];
|
|
102
77
|
res.features.forEach(f => {
|
|
103
|
-
const plname = nominatimAddressToPlaceName(f.properties
|
|
78
|
+
const plname = nominatimAddressToPlaceName(f.properties) || f.properties.display_name;
|
|
104
79
|
if(!listedNames.includes(plname)) {
|
|
105
80
|
finalRes.features.push({
|
|
106
81
|
place_type: ["place"],
|
|
@@ -115,18 +90,112 @@ export function forwardGeocodingNominatim(config) {
|
|
|
115
90
|
}
|
|
116
91
|
|
|
117
92
|
export function reverseGeocodingNominatim(lat, lon) {
|
|
118
|
-
return fetch(
|
|
93
|
+
return fetch(`${NominatimBaseUrl()}/reverse?lat=${lat}&lon=${lon}&zoom=18&format=jsonv2`)
|
|
119
94
|
.then(res => res.json())
|
|
120
95
|
.then(res => nominatimAddressToPlaceName(res?.address));
|
|
121
96
|
}
|
|
122
97
|
|
|
123
98
|
/**
|
|
124
99
|
* Base adresse nationale (FR) geocoder, ready to use for our Map
|
|
125
|
-
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://
|
|
126
|
-
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format
|
|
100
|
+
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html )
|
|
101
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html )
|
|
127
102
|
* @private
|
|
128
103
|
*/
|
|
129
104
|
export function forwardGeocodingBAN(config) {
|
|
105
|
+
return forwardGeocodingStandard(config, AdresseDataGouvBaseURL());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Transforms GeocodeJSON search result into a nice-to-display address.
|
|
110
|
+
* @param {object} props The GecodeJSON API feature properties
|
|
111
|
+
* @returns {string} The clean-up string for display
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
function geocodeJsonToPlaceName(props) {
|
|
115
|
+
// API format @ https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md
|
|
116
|
+
if(!props || typeof props != "object") { return ""; }
|
|
117
|
+
|
|
118
|
+
// P1 = main name, P2=locality-like, P3=country+high-level admin
|
|
119
|
+
let p1 = props.name;
|
|
120
|
+
let p2 = [], p3 = [];
|
|
121
|
+
|
|
122
|
+
switch(props.type) {
|
|
123
|
+
case "hamlet":
|
|
124
|
+
case "croft":
|
|
125
|
+
case "isolated_dwelling":
|
|
126
|
+
case "neighbourhood":
|
|
127
|
+
case "allotments":
|
|
128
|
+
case "quarter":
|
|
129
|
+
case "farm":
|
|
130
|
+
case "farmyard":
|
|
131
|
+
case "industrial":
|
|
132
|
+
case "commercial":
|
|
133
|
+
case "retail":
|
|
134
|
+
case "city_block":
|
|
135
|
+
case "residential":
|
|
136
|
+
case "locality":
|
|
137
|
+
case "district":
|
|
138
|
+
p3.push(props.city);
|
|
139
|
+
p3.push(props.county);
|
|
140
|
+
p3.push(props.state);
|
|
141
|
+
p3.push(props.country);
|
|
142
|
+
break;
|
|
143
|
+
case "city":
|
|
144
|
+
p3.push(props.county);
|
|
145
|
+
p3.push(props.state);
|
|
146
|
+
p3.push(props.country);
|
|
147
|
+
break;
|
|
148
|
+
case "region":
|
|
149
|
+
p3.push(props.county);
|
|
150
|
+
p3.push(props.state);
|
|
151
|
+
p3.push(props.country);
|
|
152
|
+
break;
|
|
153
|
+
case "country":
|
|
154
|
+
break;
|
|
155
|
+
case "house":
|
|
156
|
+
case "housenumber":
|
|
157
|
+
p2.push(props.housenumber);
|
|
158
|
+
p2.push(props.street);
|
|
159
|
+
p2.push(props.locality);
|
|
160
|
+
p2.push(props.district);
|
|
161
|
+
p3.push(props.city);
|
|
162
|
+
p3.push(props.county);
|
|
163
|
+
p3.push(props.state);
|
|
164
|
+
p3.push(props.country);
|
|
165
|
+
break;
|
|
166
|
+
case "street":
|
|
167
|
+
default:
|
|
168
|
+
p2.push(props.street);
|
|
169
|
+
p2.push(props.locality);
|
|
170
|
+
p2.push(props.district);
|
|
171
|
+
p3.push(props.city);
|
|
172
|
+
p3.push(props.county);
|
|
173
|
+
p3.push(props.state);
|
|
174
|
+
p3.push(props.country);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
p2 = p2.filter(v => v);
|
|
179
|
+
p2 = p2.filter((v,i) => v != p1 && (i === 0 || p2[i-1] !== v));
|
|
180
|
+
p2 = p2.length > 0 ? (props.housenumber ? p2.slice(0,2).join(" ") : p2.shift()) : null;
|
|
181
|
+
if(p2 === p1) { p2 = null; }
|
|
182
|
+
|
|
183
|
+
p3 = p3.filter(v => v);
|
|
184
|
+
p3 = p3.filter((v,i) => v != p1 && (!p2 || !p2.includes(v)) && (i === 0 || p3[i-1] !== v));
|
|
185
|
+
|
|
186
|
+
let res = [p1, p2, p3.shift()].filter(v => v);
|
|
187
|
+
|
|
188
|
+
return res.join(", ");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Standard forward geocoder
|
|
193
|
+
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html )
|
|
194
|
+
* @param {string} endpoint The URL endpoint (everything before the /?q=...)
|
|
195
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html )
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
export function forwardGeocodingStandard(config, endpoint) {
|
|
130
199
|
// Transform parameters into BAN format
|
|
131
200
|
const params = { q: config.query, limit: config.limit };
|
|
132
201
|
if(typeof config.proximity === "string") {
|
|
@@ -135,18 +204,37 @@ export function forwardGeocodingBAN(config) {
|
|
|
135
204
|
params.lon = lon;
|
|
136
205
|
}
|
|
137
206
|
|
|
138
|
-
const
|
|
139
|
-
|
|
207
|
+
const placeTypeToZoom = {
|
|
208
|
+
"house": 20,
|
|
209
|
+
"housenumber": 20,
|
|
210
|
+
"street": 18,
|
|
211
|
+
"locality": 15,
|
|
212
|
+
"district": 13,
|
|
213
|
+
"municipality": 12,
|
|
214
|
+
"city": 12,
|
|
215
|
+
"county": 8,
|
|
216
|
+
"region": 7,
|
|
217
|
+
"state": 7,
|
|
218
|
+
"country": 5
|
|
219
|
+
};
|
|
140
220
|
|
|
141
|
-
return fetch(
|
|
221
|
+
return fetch(`${endpoint}/?${geocoderParamsToURLString(params)}`)
|
|
142
222
|
.then(res => res.json())
|
|
143
223
|
.then(res => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
224
|
+
const finalRes = { features: [] };
|
|
225
|
+
const listedNames = [];
|
|
226
|
+
(res.features || []).forEach(f => {
|
|
227
|
+
const plname = geocodeJsonToPlaceName(f.properties);
|
|
228
|
+
if(!listedNames.includes(plname) && f.properties.type != "other") {
|
|
229
|
+
finalRes.features.push({
|
|
230
|
+
place_type: ["place"],
|
|
231
|
+
place_name: plname,
|
|
232
|
+
center: new maplibregl.LngLat(...f.geometry.coordinates),
|
|
233
|
+
zoom: placeTypeToZoom[f.properties.type],
|
|
234
|
+
});
|
|
235
|
+
listedNames.push(plname);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return finalRes;
|
|
151
239
|
});
|
|
152
|
-
}
|
|
240
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -2,10 +2,11 @@ import * as geocoder from "./geocoder";
|
|
|
2
2
|
import * as i18n from "./i18n";
|
|
3
3
|
import * as map from "./map";
|
|
4
4
|
import * as picture from "./picture";
|
|
5
|
+
import * as services from "./services";
|
|
5
6
|
import * as utils from "./utils";
|
|
6
7
|
import * as widgets from "./widgets";
|
|
7
8
|
|
|
8
|
-
export { geocoder, i18n, map, picture, utils, widgets };
|
|
9
|
+
export { geocoder, i18n, map, picture, services, utils, widgets };
|
|
9
10
|
export {default as API} from "./API";
|
|
10
11
|
export {default as PhotoAdapter} from "./PhotoAdapter";
|
|
11
12
|
export {default as URLHandler} from "./URLHandler";
|
package/src/utils/picture.js
CHANGED
|
@@ -191,7 +191,12 @@ export function getCroppedPanoData(picture) {
|
|
|
191
191
|
// Check if crop is really necessary
|
|
192
192
|
if(res) {
|
|
193
193
|
res = Object.fromEntries(Object.entries(res || {}).filter(e => !isNaN(e[1])));
|
|
194
|
-
if(
|
|
194
|
+
if(
|
|
195
|
+
(!res.fullWidth && !res.croppedWidth && res.fullHeight && !res.croppedHeight)
|
|
196
|
+
|| (res.fullWidth && !res.croppedWidth && !res.fullHeight && !res.croppedHeight)
|
|
197
|
+
|| (res.fullWidth && !res.croppedWidth && res.fullHeight && !res.croppedHeight)
|
|
198
|
+
|| (res.fullWidth == res.croppedWidth && res.fullHeight == res.croppedHeight)
|
|
199
|
+
) {
|
|
195
200
|
res = {};
|
|
196
201
|
}
|
|
197
202
|
}
|