maplibre-gl-basemap-control 0.11.0 → 0.12.0
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/README.md +17 -2
- package/dist/{BasemapControl-CDGZrQ2Y.cjs → BasemapControl-BbsxylnF.cjs} +147 -30
- package/dist/BasemapControl-BbsxylnF.cjs.map +1 -0
- package/dist/{BasemapControl-Cya-U1Q7.js → BasemapControl-DcXJBZtu.js} +147 -30
- package/dist/BasemapControl-DcXJBZtu.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/maplibre-gl-basemap-control.css +24 -0
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/types/lib/core/BasemapControl.d.ts +4 -0
- package/dist/types/lib/core/BasemapControl.d.ts.map +1 -1
- package/dist/types/lib/core/catalog.d.ts.map +1 -1
- package/dist/types/lib/core/types.d.ts +13 -2
- package/dist/types/lib/core/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/BasemapControl-CDGZrQ2Y.cjs.map +0 -1
- package/dist/BasemapControl-Cya-U1Q7.js.map +0 -1
package/README.md
CHANGED
|
@@ -213,6 +213,21 @@ key must have the Map Tiles API enabled.
|
|
|
213
213
|
> key" or add **Map Tiles API** to the allowed list, then save (changes can take a
|
|
214
214
|
> few minutes to propagate). Make sure billing is enabled on the project too.
|
|
215
215
|
|
|
216
|
+
The same `googleMapsApiKey` also upgrades the base Google basemaps
|
|
217
|
+
(`google-maps`, `google-satellite`, `google-terrain`, `google-hybrid`). When a
|
|
218
|
+
key is set they load from the authorized Map Tiles API (via a tile session, like
|
|
219
|
+
Google Traffic); without a key they fall back to keyless `mt1.google.com` xyz
|
|
220
|
+
tiles so they keep working out of the box.
|
|
221
|
+
|
|
222
|
+
> **Licensing caveat.** The keyless `mt1.google.com` tiles are Google Maps'
|
|
223
|
+
> internal endpoints. They are **not** covered by any public or open license, and
|
|
224
|
+
> accessing Google map content outside an official Google Maps Platform API
|
|
225
|
+
> violates the [Google Maps Platform Terms of Service](https://cloud.google.com/maps-platform/terms).
|
|
226
|
+
> They are convenient for local development and demos, but Google may rate-limit,
|
|
227
|
+
> change, or block them at any time. For production use, set a `googleMapsApiKey`
|
|
228
|
+
> so these basemaps use the authorized [Map Tiles API](https://developers.google.com/maps/documentation/tile)
|
|
229
|
+
> (which requires the Map Tiles API enabled and billing on your project).
|
|
230
|
+
|
|
216
231
|
## API
|
|
217
232
|
|
|
218
233
|
### BasemapControl Options
|
|
@@ -230,7 +245,7 @@ key must have the Map Tiles API enabled.
|
|
|
230
245
|
| `mapboxAccessToken` | `string` | `undefined` | Initial Mapbox access token for built-in Mapbox styles and the Mapbox Traffic overlay |
|
|
231
246
|
| `tomtomApiKey` | `string` | `undefined` | Initial TomTom API key for the TomTom Traffic overlays |
|
|
232
247
|
| `hereApiKey` | `string` | `undefined` | Initial HERE API key for the HERE Traffic overlay |
|
|
233
|
-
| `googleMapsApiKey` | `string` | `undefined` | Initial Google Maps API key (Map Tiles API) for the Google Traffic overlay |
|
|
248
|
+
| `googleMapsApiKey` | `string` | `undefined` | Initial Google Maps API key (Map Tiles API) for the Google Traffic overlay and the base Google Maps/Satellite/Terrain/Hybrid basemaps (which fall back to keyless tiles without a key) |
|
|
234
249
|
| `basemaps` | `BasemapDefinition[]` | `[]` | Custom basemaps to add or use |
|
|
235
250
|
| `providers` | `BasemapProvider[]` | `[]` | Custom provider labels |
|
|
236
251
|
| `includeDefaultBasemaps` | `boolean` | `true` | Include the built-in public catalog |
|
|
@@ -296,7 +311,7 @@ const control = new BasemapControl({
|
|
|
296
311
|
- `setMapboxAccessToken(accessToken)` - Set or update the Mapbox access token
|
|
297
312
|
- `setTomTomApiKey(apiKey)` - Set or update the TomTom API key used by TomTom Traffic overlays
|
|
298
313
|
- `setHereApiKey(apiKey)` - Set or update the HERE API key used by the HERE Traffic overlay
|
|
299
|
-
- `setGoogleMapsApiKey(apiKey)` - Set or update the Google Maps API key used by the Google Traffic overlay
|
|
314
|
+
- `setGoogleMapsApiKey(apiKey)` - Set or update the Google Maps API key used by the Google Traffic overlay and the base Google basemaps
|
|
300
315
|
- `getActiveBasemap()` - Return the most recently selected basemap definition
|
|
301
316
|
- `getActiveBasemaps()` - Return all currently active basemap definitions
|
|
302
317
|
- `getBasemaps()` - Return the catalog
|
|
@@ -151,6 +151,37 @@ const OPENFREEMAP_ATTRIBUTION = "OpenFreeMap © OpenMapTiles Data from OpenS
|
|
|
151
151
|
const TOMTOM_ATTRIBUTION = "© TomTom";
|
|
152
152
|
const HERE_ATTRIBUTION = "© HERE";
|
|
153
153
|
const GOOGLE_ATTRIBUTION = "© Google";
|
|
154
|
+
const GOOGLE_2DTILES_URL = "https://tile.googleapis.com/v1/2dtiles/{z}/{x}/{y}?session={session}&key={api-key}";
|
|
155
|
+
function googleRasterBasemap({
|
|
156
|
+
id,
|
|
157
|
+
name,
|
|
158
|
+
category,
|
|
159
|
+
description,
|
|
160
|
+
fallbackLyrs,
|
|
161
|
+
session,
|
|
162
|
+
tags
|
|
163
|
+
}) {
|
|
164
|
+
return {
|
|
165
|
+
id,
|
|
166
|
+
name,
|
|
167
|
+
provider: "google",
|
|
168
|
+
type: "raster",
|
|
169
|
+
category,
|
|
170
|
+
description,
|
|
171
|
+
attribution: GOOGLE_ATTRIBUTION,
|
|
172
|
+
source: {
|
|
173
|
+
type: "raster",
|
|
174
|
+
tiles: [GOOGLE_2DTILES_URL],
|
|
175
|
+
fallbackTiles: [
|
|
176
|
+
`https://mt1.google.com/vt/lyrs=${fallbackLyrs}&x={x}&y={y}&z={z}`
|
|
177
|
+
],
|
|
178
|
+
tileSize: 256,
|
|
179
|
+
maxzoom: 20,
|
|
180
|
+
googleSession: session
|
|
181
|
+
},
|
|
182
|
+
tags
|
|
183
|
+
};
|
|
184
|
+
}
|
|
154
185
|
const eoxS2CloudlessAttribution = (year) => `Sentinel-2 cloudless <a href="https://cloudless.eox.at" target="_blank" rel="noopener">EOxCloudless</a> by EOX IT Services GmbH (Contains modified Copernicus Sentinel data ${year})`;
|
|
155
186
|
const EOX_S2CLOUDLESS_YEARS = [
|
|
156
187
|
2018,
|
|
@@ -268,48 +299,44 @@ const DEFAULT_BASEMAPS = [
|
|
|
268
299
|
tiles: ["https://tile.osm.ch/switzerland/{z}/{x}/{y}.png"],
|
|
269
300
|
tags: ["osm", "switzerland", "regional"]
|
|
270
301
|
}),
|
|
271
|
-
|
|
302
|
+
googleRasterBasemap({
|
|
272
303
|
id: "google-maps",
|
|
273
304
|
name: "Google Maps",
|
|
274
|
-
provider: "google",
|
|
275
305
|
category: "Street",
|
|
276
|
-
description: "Google road map tiles
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
maxzoom: 20,
|
|
306
|
+
description: "Google road map tiles. Uses the Map Tiles API when a Google Maps API key is set, otherwise keyless xyz tiles.",
|
|
307
|
+
fallbackLyrs: "m",
|
|
308
|
+
session: { mapType: "roadmap" },
|
|
280
309
|
tags: ["google", "street"]
|
|
281
310
|
}),
|
|
282
|
-
|
|
311
|
+
googleRasterBasemap({
|
|
283
312
|
id: "google-satellite",
|
|
284
313
|
name: "Google Satellite",
|
|
285
|
-
provider: "google",
|
|
286
314
|
category: "Imagery",
|
|
287
|
-
description: "Google satellite tiles
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
maxzoom: 20,
|
|
315
|
+
description: "Google satellite tiles. Uses the Map Tiles API when a Google Maps API key is set, otherwise keyless xyz tiles.",
|
|
316
|
+
fallbackLyrs: "s",
|
|
317
|
+
session: { mapType: "satellite" },
|
|
291
318
|
tags: ["google", "satellite", "imagery"]
|
|
292
319
|
}),
|
|
293
|
-
|
|
320
|
+
googleRasterBasemap({
|
|
294
321
|
id: "google-terrain",
|
|
295
322
|
name: "Google Terrain",
|
|
296
|
-
provider: "google",
|
|
297
323
|
category: "Terrain",
|
|
298
|
-
description: "Google terrain tiles
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
324
|
+
description: "Google terrain tiles. Uses the Map Tiles API when a Google Maps API key is set, otherwise keyless xyz tiles.",
|
|
325
|
+
fallbackLyrs: "p",
|
|
326
|
+
// The Map Tiles API requires `layerRoadmap` alongside the terrain map type
|
|
327
|
+
// for the terrain tiles to render.
|
|
328
|
+
session: { mapType: "terrain", layerTypes: ["layerRoadmap"] },
|
|
302
329
|
tags: ["google", "terrain"]
|
|
303
330
|
}),
|
|
304
|
-
|
|
331
|
+
googleRasterBasemap({
|
|
305
332
|
id: "google-hybrid",
|
|
306
333
|
name: "Google Hybrid",
|
|
307
|
-
provider: "google",
|
|
308
334
|
category: "Imagery",
|
|
309
|
-
description: "Google hybrid imagery tiles
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
335
|
+
description: "Google hybrid imagery tiles. Uses the Map Tiles API when a Google Maps API key is set, otherwise keyless xyz tiles.",
|
|
336
|
+
fallbackLyrs: "y",
|
|
337
|
+
// Hybrid is satellite imagery with the roadmap layer (roads and labels)
|
|
338
|
+
// painted on top, matching the classic Google "hybrid" map type.
|
|
339
|
+
session: { mapType: "satellite", layerTypes: ["layerRoadmap"] },
|
|
313
340
|
tags: ["google", "hybrid", "imagery"]
|
|
314
341
|
}),
|
|
315
342
|
amazonStyleBasemap({
|
|
@@ -1482,6 +1509,11 @@ class BasemapControl {
|
|
|
1482
1509
|
// The basemap whose application last failed for a missing credential, so the
|
|
1483
1510
|
// inline credential field's Enter key can re-attempt it (#837).
|
|
1484
1511
|
__publicField(this, "_lastFailedBasemapId");
|
|
1512
|
+
// The id of a just-applied basemap that is showing its keyless public tiles
|
|
1513
|
+
// because no API key is set (currently the Google Maps/Satellite/Terrain/
|
|
1514
|
+
// Hybrid basemaps). The panel offers an optional inline key input that
|
|
1515
|
+
// upgrades it to the authorized tiles; leaving it blank keeps the fallback.
|
|
1516
|
+
__publicField(this, "_optionalKeyBasemapId");
|
|
1485
1517
|
// Detaches the listeners watching the in-flight style load (see
|
|
1486
1518
|
// _watchStyleLoad). Set while a style swap is settling, cleared once it
|
|
1487
1519
|
// succeeds, fails, or is superseded by a newer swap.
|
|
@@ -1790,6 +1822,7 @@ class BasemapControl {
|
|
|
1790
1822
|
loading: false,
|
|
1791
1823
|
error: void 0
|
|
1792
1824
|
};
|
|
1825
|
+
this._optionalKeyBasemapId = this._basemapUsesKeylessFallback(basemap) ? basemap.id : void 0;
|
|
1793
1826
|
this._renderContent(true);
|
|
1794
1827
|
this._emit({
|
|
1795
1828
|
type: "basemapchange",
|
|
@@ -2418,6 +2451,55 @@ class BasemapControl {
|
|
|
2418
2451
|
_retryLastFailedBasemap() {
|
|
2419
2452
|
if (this._lastFailedBasemapId) this._selectBasemap(this._lastFailedBasemapId);
|
|
2420
2453
|
}
|
|
2454
|
+
// Renders the optional API key input shown after a basemap falls back to its
|
|
2455
|
+
// keyless public tiles, so the user can enter a key to upgrade to the
|
|
2456
|
+
// authorized provider tiles (or leave it blank to keep the public ones).
|
|
2457
|
+
// Returns null unless such a basemap is the active selection and still keyless.
|
|
2458
|
+
_createOptionalKeyPrompt() {
|
|
2459
|
+
const id = this._optionalKeyBasemapId;
|
|
2460
|
+
if (!id) return null;
|
|
2461
|
+
const basemap = this._basemaps.find((candidate) => candidate.id === id);
|
|
2462
|
+
if (!basemap || !this._state.activeBasemapIds.includes(id) || !this._basemapUsesKeylessFallback(basemap)) {
|
|
2463
|
+
return null;
|
|
2464
|
+
}
|
|
2465
|
+
const def = this._providerFieldDefs().find(
|
|
2466
|
+
(candidate) => candidate.provider === basemap.provider && candidate.has
|
|
2467
|
+
);
|
|
2468
|
+
if (!def) return null;
|
|
2469
|
+
const wrapper = document.createElement("div");
|
|
2470
|
+
wrapper.className = "basemap-control-optional-key";
|
|
2471
|
+
const message = document.createElement("span");
|
|
2472
|
+
message.className = "basemap-control-optional-key-message";
|
|
2473
|
+
message.textContent = `Enter a ${def.fields[0].placeholder} to load the official ${def.label} tiles.`;
|
|
2474
|
+
const help = PROVIDER_CREDENTIAL_HELP[def.provider];
|
|
2475
|
+
if (help) {
|
|
2476
|
+
message.appendChild(document.createTextNode(" "));
|
|
2477
|
+
const link = document.createElement("a");
|
|
2478
|
+
link.className = "basemap-control-status-link";
|
|
2479
|
+
link.href = help.url;
|
|
2480
|
+
link.target = "_blank";
|
|
2481
|
+
link.rel = "noopener noreferrer";
|
|
2482
|
+
link.textContent = help.label;
|
|
2483
|
+
message.appendChild(link);
|
|
2484
|
+
}
|
|
2485
|
+
wrapper.appendChild(message);
|
|
2486
|
+
const fields = document.createElement("div");
|
|
2487
|
+
fields.className = "basemap-control-status-fields";
|
|
2488
|
+
for (const field of def.fields) {
|
|
2489
|
+
fields.appendChild(
|
|
2490
|
+
this._createProviderSettingsInput({
|
|
2491
|
+
...field,
|
|
2492
|
+
// Keep the field on screen while typing; Enter re-applies the basemap
|
|
2493
|
+
// so the freshly entered key takes effect immediately.
|
|
2494
|
+
rerenderOnInput: false,
|
|
2495
|
+
clearErrorOnInput: false,
|
|
2496
|
+
onEnter: () => this._reapplyBasemap(id)
|
|
2497
|
+
})
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
wrapper.appendChild(fields);
|
|
2501
|
+
return wrapper;
|
|
2502
|
+
}
|
|
2421
2503
|
_createProviderSettingsInput({
|
|
2422
2504
|
className,
|
|
2423
2505
|
type,
|
|
@@ -2539,7 +2621,15 @@ class BasemapControl {
|
|
|
2539
2621
|
const inlineFields = this._createInlineCredentialFields();
|
|
2540
2622
|
if (inlineFields) status.appendChild(inlineFields);
|
|
2541
2623
|
} else {
|
|
2542
|
-
|
|
2624
|
+
const count = document.createElement("span");
|
|
2625
|
+
count.className = "basemap-control-status-message";
|
|
2626
|
+
count.textContent = `${resultCount} basemap${resultCount === 1 ? "" : "s"}`;
|
|
2627
|
+
status.appendChild(count);
|
|
2628
|
+
const optionalKey = this._createOptionalKeyPrompt();
|
|
2629
|
+
if (optionalKey) {
|
|
2630
|
+
status.classList.add("has-optional-key");
|
|
2631
|
+
status.appendChild(optionalKey);
|
|
2632
|
+
}
|
|
2543
2633
|
}
|
|
2544
2634
|
return status;
|
|
2545
2635
|
}
|
|
@@ -2620,9 +2710,10 @@ class BasemapControl {
|
|
|
2620
2710
|
_hasHereBasemaps() {
|
|
2621
2711
|
return this._basemaps.some((basemap) => basemap.provider === "here");
|
|
2622
2712
|
}
|
|
2623
|
-
// True when a Google basemap
|
|
2624
|
-
//
|
|
2625
|
-
//
|
|
2713
|
+
// True when a Google basemap can use a Map Tiles API key. Session-based Google
|
|
2714
|
+
// layers (Google Traffic, plus the base Maps/Satellite/Terrain/Hybrid layers
|
|
2715
|
+
// that upgrade to the authorized Map Tiles API when a key is present) and any
|
|
2716
|
+
// api-key based Google tiles reveal the key input.
|
|
2626
2717
|
_hasGoogleApiKeyBasemaps() {
|
|
2627
2718
|
return this._basemaps.some(
|
|
2628
2719
|
(basemap) => basemap.provider === "google" && basemap.source.type === "raster" && (Boolean(basemap.source.googleSession) || basemap.source.tiles.some((tile) => tile.includes(API_KEY_PLACEHOLDER)))
|
|
@@ -2853,8 +2944,11 @@ class BasemapControl {
|
|
|
2853
2944
|
// surface a "Get a ..." link and reveal the credential inputs.
|
|
2854
2945
|
async _resolveRasterTiles(basemap) {
|
|
2855
2946
|
if (basemap.source.type !== "raster") return [];
|
|
2856
|
-
const { tiles, googleSession } = basemap.source;
|
|
2947
|
+
const { tiles, googleSession, fallbackTiles } = basemap.source;
|
|
2857
2948
|
if (googleSession) {
|
|
2949
|
+
if (!this._googleMapsApiKey.trim() && (fallbackTiles == null ? void 0 : fallbackTiles.length)) {
|
|
2950
|
+
return fallbackTiles;
|
|
2951
|
+
}
|
|
2858
2952
|
return this._resolveGoogleSessionTiles(tiles, googleSession);
|
|
2859
2953
|
}
|
|
2860
2954
|
if (!tiles.some((tile) => tile.includes(API_KEY_PLACEHOLDER))) {
|
|
@@ -2873,6 +2967,29 @@ class BasemapControl {
|
|
|
2873
2967
|
if (provider === "google") return this._googleMapsApiKey.trim();
|
|
2874
2968
|
return "";
|
|
2875
2969
|
}
|
|
2970
|
+
// True when a basemap is (or would be) served from its keyless public
|
|
2971
|
+
// fallback tiles because its provider API key is not set. Drives the optional
|
|
2972
|
+
// key prompt: these basemaps work without a key but can upgrade to the
|
|
2973
|
+
// authorized provider tiles once one is entered.
|
|
2974
|
+
_basemapUsesKeylessFallback(basemap) {
|
|
2975
|
+
var _a;
|
|
2976
|
+
return basemap.source.type === "raster" && Boolean(basemap.source.googleSession) && (((_a = basemap.source.fallbackTiles) == null ? void 0 : _a.length) ?? 0) > 0 && !this._rasterApiKeyFor(basemap.provider);
|
|
2977
|
+
}
|
|
2978
|
+
// Re-applies an already-active basemap so a newly entered key takes effect.
|
|
2979
|
+
// Unlike `_selectBasemap`, this never toggles the basemap off in multiple
|
|
2980
|
+
// mode: a stacked raster is re-added on top (replacing its prior instance),
|
|
2981
|
+
// and a single-mode basemap is simply re-set.
|
|
2982
|
+
_reapplyBasemap(id) {
|
|
2983
|
+
const basemap = this._basemaps.find((candidate) => candidate.id === id);
|
|
2984
|
+
const isOverlay = (basemap == null ? void 0 : basemap.source.type) === "raster" || (basemap == null ? void 0 : basemap.source.type) === "vector-overlay";
|
|
2985
|
+
if (this._state.allowMultiple && isOverlay) {
|
|
2986
|
+
this.addBasemap(id).catch(() => {
|
|
2987
|
+
});
|
|
2988
|
+
} else {
|
|
2989
|
+
this.setBasemap(id).catch(() => {
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2876
2993
|
_missingRasterKeyError(provider) {
|
|
2877
2994
|
const label = provider === "tomtom" ? "TomTom API key" : provider === "here" ? "HERE API key" : provider === "google" ? "Google Maps API key" : "API key";
|
|
2878
2995
|
const helpProvider = provider in PROVIDER_CREDENTIAL_HELP ? provider : "maptiler";
|
|
@@ -3108,4 +3225,4 @@ exports.createBasemapCatalog = createBasemapCatalog;
|
|
|
3108
3225
|
exports.filterBasemaps = filterBasemaps;
|
|
3109
3226
|
exports.getBasemapCategories = getBasemapCategories;
|
|
3110
3227
|
exports.resolveBasemapProviders = resolveBasemapProviders;
|
|
3111
|
-
//# sourceMappingURL=BasemapControl-
|
|
3228
|
+
//# sourceMappingURL=BasemapControl-BbsxylnF.cjs.map
|