@panoramax/web-viewer 4.0.1 → 4.0.2-develop-9b499e28
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 +137 -83
- 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/data/Map_geocoder_nominatim.json +25 -40
- package/tests/utils/InitParameters.test.js +15 -15
- package/tests/utils/__snapshots__/geocoder.test.js.snap +5 -16
- package/tests/utils/geocoder.test.js +2 -2
- 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,22 @@
|
|
|
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
|
+
|
|
6
|
+
const PLACETYPE_ZOOM = {
|
|
7
|
+
"house": 20,
|
|
8
|
+
"housenumber": 20,
|
|
9
|
+
"street": 18,
|
|
10
|
+
"locality": 15,
|
|
11
|
+
"district": 13,
|
|
12
|
+
"municipality": 12,
|
|
13
|
+
"city": 12,
|
|
14
|
+
"county": 8,
|
|
15
|
+
"region": 7,
|
|
16
|
+
"state": 7,
|
|
17
|
+
"country": 5
|
|
18
|
+
};
|
|
19
|
+
|
|
4
20
|
/**
|
|
5
21
|
* Transforms a set of parameters into an URL-ready string
|
|
6
22
|
* It also removes null/undefined values
|
|
@@ -18,69 +34,6 @@ function geocoderParamsToURLString(params) {
|
|
|
18
34
|
return new URLSearchParams(p).toString();
|
|
19
35
|
}
|
|
20
36
|
|
|
21
|
-
/**
|
|
22
|
-
* Transforms Nominatim search result into a nice-to-display address.
|
|
23
|
-
* @param {object} addr The Nominatim API "address" property
|
|
24
|
-
* @returns {string} The clean-up string for display
|
|
25
|
-
* @private
|
|
26
|
-
*/
|
|
27
|
-
function nominatimAddressToPlaceName(addr) {
|
|
28
|
-
// 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
|
-
|
|
50
|
-
// Street-like
|
|
51
|
-
let street;
|
|
52
|
-
if(addr.road && addr.road.length > 6) { street = addr.road; }
|
|
53
|
-
else {
|
|
54
|
-
const potentialNames = [
|
|
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
|
-
}
|
|
71
|
-
|
|
72
|
-
if(street && res.length > 0) { res += (addr.house_number ? " " : ", ")+street; }
|
|
73
|
-
else if(street) { res = street; }
|
|
74
|
-
|
|
75
|
-
// City
|
|
76
|
-
if(addr.village || addr.town || addr.city || addr.municipality) {
|
|
77
|
-
if(res.length > 0) { res += ", "; }
|
|
78
|
-
res += addr.village || addr.town || addr.city || addr.municipality;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return res;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
37
|
/**
|
|
85
38
|
* Nominatim (OSM) geocoder, ready to use for our Map
|
|
86
39
|
* @private
|
|
@@ -94,18 +47,19 @@ export function forwardGeocodingNominatim(config) {
|
|
|
94
47
|
viewbox: config.bbox,
|
|
95
48
|
};
|
|
96
49
|
|
|
97
|
-
return fetch(
|
|
50
|
+
return fetch(`${NominatimBaseUrl()}/search?${geocoderParamsToURLString(params)}&format=geocodejson&addressdetails=1`)
|
|
98
51
|
.then(res => res.json())
|
|
99
52
|
.then(res => {
|
|
100
53
|
const finalRes = { features: [] };
|
|
101
54
|
const listedNames = [];
|
|
102
|
-
res.features.forEach(f => {
|
|
103
|
-
const plname =
|
|
55
|
+
(res.features || []).forEach(f => {
|
|
56
|
+
const plname = geocodeJsonToPlaceName(f.properties?.geocoding) || f.properties?.geocoding?.label;
|
|
104
57
|
if(!listedNames.includes(plname)) {
|
|
105
58
|
finalRes.features.push({
|
|
106
59
|
place_type: ["place"],
|
|
107
60
|
place_name: plname,
|
|
108
|
-
|
|
61
|
+
center: new maplibregl.LngLat(...f.geometry.coordinates),
|
|
62
|
+
zoom: PLACETYPE_ZOOM[f.properties?.geocoding?.type],
|
|
109
63
|
});
|
|
110
64
|
listedNames.push(plname);
|
|
111
65
|
}
|
|
@@ -115,18 +69,113 @@ export function forwardGeocodingNominatim(config) {
|
|
|
115
69
|
}
|
|
116
70
|
|
|
117
71
|
export function reverseGeocodingNominatim(lat, lon) {
|
|
118
|
-
return fetch(
|
|
72
|
+
return fetch(`${NominatimBaseUrl()}/reverse?lat=${lat}&lon=${lon}&zoom=18&format=geocodejson`)
|
|
119
73
|
.then(res => res.json())
|
|
120
|
-
.then(res =>
|
|
74
|
+
.then(res => geocodeJsonToPlaceName(res?.features?.shift()?.properties?.geocoding));
|
|
121
75
|
}
|
|
122
76
|
|
|
123
77
|
/**
|
|
124
78
|
* 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
|
|
79
|
+
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html )
|
|
80
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html )
|
|
127
81
|
* @private
|
|
128
82
|
*/
|
|
129
83
|
export function forwardGeocodingBAN(config) {
|
|
84
|
+
return forwardGeocodingStandard(config, AdresseDataGouvBaseURL());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Transforms GeocodeJSON search result into a nice-to-display address.
|
|
89
|
+
* @param {object} props The GecodeJSON API feature properties
|
|
90
|
+
* @returns {string} The clean-up string for display
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
function geocodeJsonToPlaceName(props) {
|
|
94
|
+
// API format @ https://github.com/geocoders/geocodejson-spec/blob/master/draft/README.md
|
|
95
|
+
if(!props || typeof props != "object") { return ""; }
|
|
96
|
+
|
|
97
|
+
// P1 = main name, P2=locality-like, P3=country+high-level admin
|
|
98
|
+
let p1 = props.name;
|
|
99
|
+
let p2 = [], p3 = [];
|
|
100
|
+
|
|
101
|
+
switch(props.type) {
|
|
102
|
+
case "hamlet":
|
|
103
|
+
case "croft":
|
|
104
|
+
case "isolated_dwelling":
|
|
105
|
+
case "neighbourhood":
|
|
106
|
+
case "allotments":
|
|
107
|
+
case "quarter":
|
|
108
|
+
case "farm":
|
|
109
|
+
case "farmyard":
|
|
110
|
+
case "industrial":
|
|
111
|
+
case "commercial":
|
|
112
|
+
case "retail":
|
|
113
|
+
case "city_block":
|
|
114
|
+
case "residential":
|
|
115
|
+
case "locality":
|
|
116
|
+
case "district":
|
|
117
|
+
p3.push(props.city);
|
|
118
|
+
p3.push(props.county);
|
|
119
|
+
p3.push(props.state);
|
|
120
|
+
p3.push(props.country);
|
|
121
|
+
break;
|
|
122
|
+
case "city":
|
|
123
|
+
p3.push(props.county);
|
|
124
|
+
p3.push(props.state);
|
|
125
|
+
p3.push(props.country);
|
|
126
|
+
break;
|
|
127
|
+
case "region":
|
|
128
|
+
p3.push(props.county);
|
|
129
|
+
p3.push(props.state);
|
|
130
|
+
p3.push(props.country);
|
|
131
|
+
break;
|
|
132
|
+
case "country":
|
|
133
|
+
break;
|
|
134
|
+
case "house":
|
|
135
|
+
case "housenumber":
|
|
136
|
+
p2.push(props.housenumber);
|
|
137
|
+
p2.push(props.street);
|
|
138
|
+
p2.push(props.locality);
|
|
139
|
+
p2.push(props.district);
|
|
140
|
+
p3.push(props.city);
|
|
141
|
+
p3.push(props.county);
|
|
142
|
+
p3.push(props.state);
|
|
143
|
+
p3.push(props.country);
|
|
144
|
+
break;
|
|
145
|
+
case "street":
|
|
146
|
+
case "road":
|
|
147
|
+
default:
|
|
148
|
+
p2.push(props.street);
|
|
149
|
+
p2.push(props.locality);
|
|
150
|
+
p2.push(props.district);
|
|
151
|
+
p3.push(props.city);
|
|
152
|
+
p3.push(props.county);
|
|
153
|
+
p3.push(props.state);
|
|
154
|
+
p3.push(props.country);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
p2 = p2.filter(v => v);
|
|
159
|
+
p2 = p2.filter((v,i) => v != p1 && (i === 0 || p2[i-1] !== v));
|
|
160
|
+
p2 = p2.length > 0 ? (props.housenumber ? p2.slice(0,2).join(" ") : p2.shift()) : null;
|
|
161
|
+
if(p2 === p1) { p2 = null; }
|
|
162
|
+
|
|
163
|
+
p3 = p3.filter(v => v);
|
|
164
|
+
p3 = p3.filter((v,i) => v != p1 && (!p2 || !p2.includes(v)) && (i === 0 || p3[i-1] !== v));
|
|
165
|
+
|
|
166
|
+
let res = [p1, p2, p3.shift()].filter(v => v);
|
|
167
|
+
|
|
168
|
+
return res.join(", ");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Standard forward geocoder
|
|
173
|
+
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://maplibre.org/maplibre-gl-geocoder/types/MaplibreGeocoderApiConfig.html )
|
|
174
|
+
* @param {string} endpoint The URL endpoint (everything before the /?q=...)
|
|
175
|
+
* @returns {object} GeoJSON Feature collection in Carmen GeoJSON format ( https://maplibre.org/maplibre-gl-geocoder/types/CarmenGeojsonFeature.html )
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
export function forwardGeocodingStandard(config, endpoint) {
|
|
130
179
|
// Transform parameters into BAN format
|
|
131
180
|
const params = { q: config.query, limit: config.limit };
|
|
132
181
|
if(typeof config.proximity === "string") {
|
|
@@ -135,18 +184,23 @@ export function forwardGeocodingBAN(config) {
|
|
|
135
184
|
params.lon = lon;
|
|
136
185
|
}
|
|
137
186
|
|
|
138
|
-
|
|
139
|
-
const placeTypeToZoom = { "housenumber": 20, "street": 18, "locality": 15, "municipality": 12 };
|
|
140
|
-
|
|
141
|
-
return fetch(`https://api-adresse.data.gouv.fr/search/?${geocoderParamsToURLString(params)}`)
|
|
187
|
+
return fetch(`${endpoint}/?${geocoderParamsToURLString(params)}`)
|
|
142
188
|
.then(res => res.json())
|
|
143
189
|
.then(res => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
const finalRes = { features: [] };
|
|
191
|
+
const listedNames = [];
|
|
192
|
+
(res.features || []).forEach(f => {
|
|
193
|
+
const plname = geocodeJsonToPlaceName(f.properties);
|
|
194
|
+
if(!listedNames.includes(plname) && f.properties.type != "other") {
|
|
195
|
+
finalRes.features.push({
|
|
196
|
+
place_type: ["place"],
|
|
197
|
+
place_name: plname,
|
|
198
|
+
center: new maplibregl.LngLat(...f.geometry.coordinates),
|
|
199
|
+
zoom: PLACETYPE_ZOOM[f.properties.type],
|
|
200
|
+
});
|
|
201
|
+
listedNames.push(plname);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return finalRes;
|
|
151
205
|
});
|
|
152
|
-
}
|
|
206
|
+
}
|
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
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Settings that may be useful to change for special cases (e.g. offline implementation).
|
|
3
|
+
* Most users will be fine with the defaults.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OpenStreetMap iD editor URL
|
|
9
|
+
* @returns {string} The editor URL
|
|
10
|
+
*/
|
|
11
|
+
export function IdEditorURL() {
|
|
12
|
+
return "https://www.openstreetmap.org/edit?editor=id";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/* -----------------------------------------------------
|
|
17
|
+
* Internet speed tests
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the threshold fast internet speed value.
|
|
22
|
+
*
|
|
23
|
+
* @returns {number} the minimum speed in MBps for internet to be considered "fast"
|
|
24
|
+
*/
|
|
25
|
+
export function InternetFastThreshold() {
|
|
26
|
+
return 10;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the fast internet speed test file.
|
|
31
|
+
*
|
|
32
|
+
* @returns {string} URL for the file to use for internet speed testing.
|
|
33
|
+
*/
|
|
34
|
+
export function InternetFastTestFile() {
|
|
35
|
+
return "https://panoramax.openstreetmap.fr/images/05/ca/2c/98/0111-4baf-b6f3-587bb8847d2e.jpg";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/* -----------------------------------------------------
|
|
40
|
+
* Geocoding-related settings
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the Base Adresse Nationale URL for geocoding API.
|
|
45
|
+
* @returns {string} The Base Adresse Nationale URL (must support /search calls).
|
|
46
|
+
*/
|
|
47
|
+
export function AdresseDataGouvBaseURL() {
|
|
48
|
+
return "https://data.geopf.fr/geocodage/search";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the Nominatim base URL for geocoding API.
|
|
53
|
+
* @returns {string} The Nominatim URL (must support /search & /reverse calls).
|
|
54
|
+
*/
|
|
55
|
+
export function NominatimBaseUrl() {
|
|
56
|
+
return "https://nominatim.openstreetmap.org";
|
|
57
|
+
}
|