@panoramax/web-viewer 4.0.1-develop-16812d39 → 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 +5 -0
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/docs/reference/components/core/Viewer.md +1 -1
- package/docs/reference/components/ui/widgets/GeoSearch.md +5 -1
- package/package.json +1 -1
- package/src/components/core/Viewer.js +1 -1
- package/src/components/ui/widgets/GeoSearch.js +13 -5
- package/src/utils/geocoder.js +151 -65
- package/src/utils/services.js +1 -1
- package/tests/utils/__snapshots__/geocoder.test.js.snap +0 -6
|
@@ -114,7 +114,7 @@ Component properties. All of [Basic properties](Basic.md/#Panoramax.components.c
|
|
|
114
114
|
| [psv] | <code>object</code> | | [Any option to pass to Photo component](../ui/Photo.md/#Panoramax.components.ui.Photo) as an object.<br />Example: `psv="{'transitionDuration': 500, 'picturesNavigation': 'pic'}"` |
|
|
115
115
|
| [url-parameters] | <code>string</code> | <code>true</code> | Should the component add and update URL query parameters to save viewer state ? |
|
|
116
116
|
| [focus] | <code>string</code> | <code>"pic"</code> | The component showing up as main component (pic, map) |
|
|
117
|
-
| [geocoder] | <code>string</code> | <code>"nominatim"</code> | The geocoder engine to use (nominatim, ban) |
|
|
117
|
+
| [geocoder] | <code>string</code> | <code>"nominatim"</code> | 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) |
|
|
118
118
|
| [widgets] | <code>string</code> | <code>true</code> | Use default set of widgets ? Set to false to avoid any widget to show up, and use slots to populate as you like. |
|
|
119
119
|
| [picture] | <code>string</code> | | The picture ID to display |
|
|
120
120
|
| [sequence] | <code>string</code> | | The sequence ID of the picture displayed |
|
|
@@ -16,7 +16,11 @@ Ready-to-use geocoder search bar.
|
|
|
16
16
|
|
|
17
17
|
**Example**
|
|
18
18
|
```html
|
|
19
|
+
<!-- Default geocoder -->
|
|
19
20
|
<pnx-widget-geosearch _parent=${viewer} />
|
|
21
|
+
|
|
22
|
+
<!-- Custom-URL geocoder -->
|
|
23
|
+
<pnx-widget-geosearch geocoder="https://photon.komoot.io/api" _parent=${viewer} />
|
|
20
24
|
```
|
|
21
25
|
<a name="Panoramax.components.ui.widgets.GeoSearch+properties"></a>
|
|
22
26
|
|
|
@@ -28,5 +32,5 @@ Component properties.
|
|
|
28
32
|
|
|
29
33
|
| Name | Type | Default | Description |
|
|
30
34
|
| --- | --- | --- | --- |
|
|
31
|
-
| [geocoder] | <code>string</code> | <code>"nominatim"</code> | The geocoder engine to use (nominatim, ban) |
|
|
35
|
+
| [geocoder] | <code>string</code> | <code>"nominatim"</code> | 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) |
|
|
32
36
|
|
package/package.json
CHANGED
|
@@ -95,7 +95,7 @@ export default class Viewer extends PhotoViewer {
|
|
|
95
95
|
* @property {object} [psv] [Any option to pass to Photo component](#Panoramax.components.ui.Photo) as an object.<br />Example: `psv="{'transitionDuration': 500, 'picturesNavigation': 'pic'}"`
|
|
96
96
|
* @property {string} [url-parameters=true] Should the component add and update URL query parameters to save viewer state ?
|
|
97
97
|
* @property {string} [focus=pic] The component showing up as main component (pic, map)
|
|
98
|
-
* @property {string} [geocoder=nominatim] The geocoder engine to use (nominatim, ban)
|
|
98
|
+
* @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)
|
|
99
99
|
* @property {string} [widgets=true] Use default set of widgets ? Set to false to avoid any widget to show up, and use slots to populate as you like.
|
|
100
100
|
* @property {string} [picture] The picture ID to display
|
|
101
101
|
* @property {string} [sequence] The sequence ID of the picture displayed
|
|
@@ -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 => {
|
package/src/utils/geocoder.js
CHANGED
|
@@ -22,65 +22,38 @@ function geocoderParamsToURLString(params) {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Transforms Nominatim search result into a nice-to-display address.
|
|
25
|
-
* @param {object}
|
|
25
|
+
* @param {object} props The Nominatim API feature properties
|
|
26
26
|
* @returns {string} The clean-up string for display
|
|
27
27
|
* @private
|
|
28
28
|
*/
|
|
29
|
-
function nominatimAddressToPlaceName(
|
|
29
|
+
function nominatimAddressToPlaceName(props) {
|
|
30
30
|
// API format @ https://nominatim.org/release-docs/develop/api/Output/#addressdetails
|
|
31
|
-
if(!addr || typeof addr != "object") { return ""; }
|
|
32
|
-
|
|
33
|
-
let res = "";
|
|
34
|
-
|
|
35
|
-
// House n°-like
|
|
36
|
-
if(addr.house_number) { res = addr.house_number; }
|
|
37
|
-
else if(addr.house_name) { res = addr.house_name; }
|
|
38
|
-
else {
|
|
39
|
-
const potentialNames = [
|
|
40
|
-
"emergency", "historic", "military", "natural", "landuse", "place", "railway", "man_made",
|
|
41
|
-
"aerialway", "boundary", "amenity", "aeroway", "club", "craft", "leisure", "office",
|
|
42
|
-
"mountain_pass", "shop", "tourism", "bridge", "tunnel", "waterway", "park"
|
|
43
|
-
];
|
|
44
|
-
for(let pn of potentialNames) {
|
|
45
|
-
if(addr[pn]) {
|
|
46
|
-
res = addr[pn];
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Hamlet-like
|
|
58
|
-
"hamlet", "croft", "isolated_dwelling",
|
|
59
|
-
// Zone Indus-like
|
|
60
|
-
"farm", "farmyard", "industrial", "commercial", "retail", "city_block", "residential",
|
|
61
|
-
// Quarter-like
|
|
62
|
-
"neighbourhood", "allotments", "quarter",
|
|
63
|
-
// Fallback to road if nothing else found
|
|
64
|
-
"road"
|
|
65
|
-
];
|
|
66
|
-
for(let pn of potentialNames) {
|
|
67
|
-
if(addr[pn]) {
|
|
68
|
-
street = addr[pn];
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
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]);
|
|
73
37
|
|
|
74
|
-
|
|
75
|
-
|
|
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]);
|
|
76
43
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
};
|
|
82
55
|
|
|
83
|
-
return
|
|
56
|
+
return geocodeJsonToPlaceName(stprops);
|
|
84
57
|
}
|
|
85
58
|
|
|
86
59
|
/**
|
|
@@ -102,7 +75,7 @@ export function forwardGeocodingNominatim(config) {
|
|
|
102
75
|
const finalRes = { features: [] };
|
|
103
76
|
const listedNames = [];
|
|
104
77
|
res.features.forEach(f => {
|
|
105
|
-
const plname = nominatimAddressToPlaceName(f.properties
|
|
78
|
+
const plname = nominatimAddressToPlaceName(f.properties) || f.properties.display_name;
|
|
106
79
|
if(!listedNames.includes(plname)) {
|
|
107
80
|
finalRes.features.push({
|
|
108
81
|
place_type: ["place"],
|
|
@@ -124,11 +97,105 @@ export function reverseGeocodingNominatim(lat, lon) {
|
|
|
124
97
|
|
|
125
98
|
/**
|
|
126
99
|
* Base adresse nationale (FR) geocoder, ready to use for our Map
|
|
127
|
-
* @param {object} config Configuration sent by MapLibre GL Geocoder, following the geocoderApi format ( https://
|
|
128
|
-
* @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 )
|
|
129
102
|
* @private
|
|
130
103
|
*/
|
|
131
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) {
|
|
132
199
|
// Transform parameters into BAN format
|
|
133
200
|
const params = { q: config.query, limit: config.limit };
|
|
134
201
|
if(typeof config.proximity === "string") {
|
|
@@ -137,18 +204,37 @@ export function forwardGeocodingBAN(config) {
|
|
|
137
204
|
params.lon = lon;
|
|
138
205
|
}
|
|
139
206
|
|
|
140
|
-
const
|
|
141
|
-
|
|
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
|
+
};
|
|
142
220
|
|
|
143
|
-
return fetch(`${
|
|
221
|
+
return fetch(`${endpoint}/?${geocoderParamsToURLString(params)}`)
|
|
144
222
|
.then(res => res.json())
|
|
145
223
|
.then(res => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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;
|
|
153
239
|
});
|
|
154
|
-
}
|
|
240
|
+
}
|
package/src/utils/services.js
CHANGED
|
@@ -45,7 +45,7 @@ export function InternetFastTestFile() {
|
|
|
45
45
|
* @returns {string} The Base Adresse Nationale URL (must support /search calls).
|
|
46
46
|
*/
|
|
47
47
|
export function AdresseDataGouvBaseURL() {
|
|
48
|
-
return "https://data.geopf.fr/geocodage";
|
|
48
|
+
return "https://data.geopf.fr/geocodage/search";
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`forwardGeocodingBAN works 1`] = `
|
|
4
4
|
Object {
|
|
5
|
-
"attribution": "BAN",
|
|
6
5
|
"features": Array [
|
|
7
6
|
Object {
|
|
8
7
|
"center": Object {
|
|
@@ -16,11 +15,6 @@ Object {
|
|
|
16
15
|
"zoom": 20,
|
|
17
16
|
},
|
|
18
17
|
],
|
|
19
|
-
"licence": "ODbL 1.0",
|
|
20
|
-
"limit": 1,
|
|
21
|
-
"query": "8 bd du port",
|
|
22
|
-
"type": "FeatureCollection",
|
|
23
|
-
"version": "draft",
|
|
24
18
|
}
|
|
25
19
|
`;
|
|
26
20
|
|