map-gl-offline 0.8.6 → 0.8.8

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 CHANGED
@@ -137,10 +137,71 @@ await offlineManager.cleanupExpiredRegions();
137
137
  await offlineManager.setupAutoCleanup({ intervalHours: 24, maxAge: 30 });
138
138
  ```
139
139
 
140
+ ### Multi-region downloads (global overview + city detail)
141
+
142
+ For app shipping use cases — a low-zoom basemap of the world, plus high-zoom detail in the cities your users actually visit — set `multipleRegions: true` on each region so the manager reuses the shared style/sprites/glyphs across downloads. The exported `BoundingBox` type keeps city lists from being widened to `number[][]`, so you can write the bounds inline without `as` casts.
143
+
144
+ ```typescript
145
+ import mapboxgl from 'mapbox-gl';
146
+ import {
147
+ OfflineMapManager,
148
+ type BoundingBox,
149
+ type DownloadRegionProgress,
150
+ } from 'map-gl-offline';
151
+
152
+ const offlineManager = new OfflineMapManager();
153
+ const STYLE_URL = 'mapbox://styles/mapbox/standard';
154
+
155
+ const opts = {
156
+ accessToken: mapboxgl.accessToken, // `string | null` is accepted — no cast needed
157
+ onProgress: ({ phase, percentage, message }: DownloadRegionProgress) =>
158
+ console.log(`[${phase}] ${percentage.toFixed(1)}% ${message ?? ''}`),
159
+ };
160
+
161
+ // 1) Whole planet, low zoom only (~5,500 tiles/source) — countries, major cities
162
+ await offlineManager.downloadRegion(
163
+ {
164
+ id: 'global-overview',
165
+ name: 'Global overview',
166
+ bounds: [[-180, -85.0511], [180, 85.0511]], // ±85.0511° = Web Mercator cutoff
167
+ minZoom: 0,
168
+ maxZoom: 6,
169
+ styleUrl: STYLE_URL,
170
+ multipleRegions: true,
171
+ },
172
+ opts,
173
+ );
174
+
175
+ // 2) High-detail per city — tight bbox per place your users actually go
176
+ const cities: Array<{ id: string; name: string; bounds: BoundingBox }> = [
177
+ { id: 'nyc', name: 'New York', bounds: [[-74.05, 40.68], [-73.90, 40.82]] },
178
+ { id: 'london', name: 'London', bounds: [[-0.25, 51.43], [0.02, 51.57]] },
179
+ ];
180
+
181
+ for (const city of cities) {
182
+ await offlineManager.downloadRegion(
183
+ {
184
+ id: city.id,
185
+ name: city.name,
186
+ bounds: city.bounds,
187
+ minZoom: 6, // overlaps the overview's maxZoom — clean handoff, no seam
188
+ maxZoom: 14, // street-level detail
189
+ styleUrl: STYLE_URL,
190
+ multipleRegions: true,
191
+ },
192
+ opts,
193
+ );
194
+ }
195
+ ```
196
+
197
+ > **Don't download the whole globe at high zoom.** The tile count quadruples per zoom level — `minZoom: 0, maxZoom: 15` for the whole planet is ~1.4 billion tiles per source, which will blow past IndexedDB quota and may violate provider TOS. The two-tier setup above gives countries-everywhere plus streets-where-it-matters for a few thousand tiles total.
198
+
140
199
  ### Sparse-source detection
141
200
 
142
201
  For composite styles (e.g. Mapbox Standard) that reference sparse tilesets like `indoor-v3` or `landmark-pois-v1`, the tile downloader probes start/middle/end tiles per source and drops any that return majority-404. Disable with `tileOptions: { probeSourcesBeforeDownload: false }`.
143
202
 
203
+ Three Mapbox Standard sub-tilesets are sparse-by-design across the planet — `mapbox.indoor-v3`, `mapbox.landmark-pois-v1`, `mapbox.procedural-buildings-v1`. Since 0.8.8 these are hard-skipped **before** the probe step so no network request is issued (and no 404s land in devtools). Opt out with `tileOptions: { skipKnownSparseSources: false }` to run them through the probe path instead.
204
+
144
205
  ### Recovering from an incompatible DB
145
206
 
146
207
  If another app on the same origin has created `offline-map-db` at a newer version, `dbPromise` throws a typed error. Offer a reset UX:
package/dist/index.esm.js CHANGED
@@ -160,6 +160,23 @@ const MAPBOX_API = {
160
160
  MODELS_PATH: '/models/v1',
161
161
  PROTOCOL: 'mapbox://',
162
162
  };
163
+ /**
164
+ * Mapbox Standard sub-tilesets that are sparse-by-design across the planet.
165
+ * These only have tiles where the underlying feature (indoor venues, landmark
166
+ * POIs, 3D procedural buildings) actually exists, and return 404 for nearly
167
+ * every other coordinate. The tile downloader pre-skips any source whose tile
168
+ * URL templates reference one of these tileset IDs, so we never issue probe
169
+ * or download requests for them — eliminating the 404 noise in devtools.
170
+ *
171
+ * Matching is done against the tileset segment of the tile URL template
172
+ * (e.g. `https://api.mapbox.com/v4/mapbox.indoor-v3/{z}/{x}/{y}.vector.pbf`
173
+ * or `mapbox://mapbox.indoor-v3`).
174
+ */
175
+ const MAPBOX_STANDARD_SPARSE_TILESETS = [
176
+ 'mapbox.indoor-v3',
177
+ 'mapbox.landmark-pois-v1',
178
+ 'mapbox.procedural-buildings-v1',
179
+ ];
163
180
  // Map Providers
164
181
  const MAP_PROVIDERS = {
165
182
  AUTO: 'auto',
@@ -6124,6 +6141,26 @@ const getExpiredResourceCount = () => cleanupService.getExpiredResourceCount();
6124
6141
  const cleanupExpiredTiles = (styleId) => cleanupService.cleanupExpiredTiles(styleId);
6125
6142
 
6126
6143
  const tileLogger = logger.scope('TileService');
6144
+ /**
6145
+ * Match a tile-URL template against the known-sparse Mapbox Standard
6146
+ * sub-tilesets. Catches:
6147
+ * - `mapbox://<tileset>` — original style source URL.
6148
+ * - `.../v4/<tileset>.json...` — resolved TileJSON URL produced when the
6149
+ * library resolves a `mapbox://` source URL into an HTTPS one.
6150
+ * - `.../v4/<tileset>/{z}/{x}/{y}...` — resolved tile-template URL.
6151
+ */
6152
+ function urlReferencesKnownSparseTileset(template) {
6153
+ return MAPBOX_STANDARD_SPARSE_TILESETS.some(tileset => {
6154
+ if (template.includes(`mapbox://${tileset}`))
6155
+ return true;
6156
+ // `/v4/<tileset>` followed by `/` (tile template) or `.` (e.g. `.json`)
6157
+ const idx = template.indexOf(`/${tileset}`);
6158
+ if (idx === -1)
6159
+ return false;
6160
+ const next = template[idx + tileset.length + 1];
6161
+ return next === '/' || next === '.';
6162
+ });
6163
+ }
6127
6164
  /**
6128
6165
  * Service for managing offline map tiles
6129
6166
  * Handles downloading, storing, and retrieving map tiles from IndexedDB
@@ -6141,7 +6178,7 @@ class TileService {
6141
6178
  */
6142
6179
  async downloadTiles(region, style, styleId, options = {}) {
6143
6180
  const db = await this.db;
6144
- const { onProgress, batchSize = 10, maxRetries = 3, skipExisting = true, timeout = 10000, retryDelay = 1000, priorityZoomLevels = [], storageQuotaCheck = true, validateTiles = false, compressTiles = false, bandwidthLimit, probeSourcesBeforeDownload = true, } = options;
6181
+ const { onProgress, batchSize = 10, maxRetries = 3, skipExisting = true, timeout = 10000, retryDelay = 1000, priorityZoomLevels = [], storageQuotaCheck = true, validateTiles = false, compressTiles = false, bandwidthLimit, probeSourcesBeforeDownload = true, skipKnownSparseSources = true, } = options;
6145
6182
  const startTime = Date.now();
6146
6183
  let totalSize = 0;
6147
6184
  let downloadedTiles = 0;
@@ -6245,6 +6282,16 @@ class TileService {
6245
6282
  tileLogger.debug(`Skipping source ${sourceId}: no tiles array`);
6246
6283
  continue;
6247
6284
  }
6285
+ // Pre-skip Mapbox Standard sparse sub-tilesets (indoor-v3 etc.) before
6286
+ // issuing any network request. They 404 for nearly every coord in a
6287
+ // typical region; the downstream probe step would catch them, but the
6288
+ // probe itself logs 404s in devtools. Matching here keeps the console
6289
+ // clean.
6290
+ if (skipKnownSparseSources &&
6291
+ tiles.some(template => urlReferencesKnownSparseTileset(template))) {
6292
+ tileLogger.info(`Skipping source "${sourceId}" — references a known sparse Mapbox Standard sub-tileset (no probe/download issued)`);
6293
+ continue;
6294
+ }
6248
6295
  const sourceMinZ = Math.ceil(sourceConfig.minzoom ?? region.minZoom);
6249
6296
  const sourceMaxZ = Math.floor(sourceConfig.maxzoom ?? region.maxZoom);
6250
6297
  let coordsForSource = tileCoords.filter(coord => coord.z >= sourceMinZ && coord.z <= sourceMaxZ);
@@ -7022,7 +7069,8 @@ var tileService$1 = /*#__PURE__*/Object.freeze({
7022
7069
  downloadTiles: downloadTiles,
7023
7070
  getTileAnalytics: getTileAnalytics,
7024
7071
  getTileStats: getTileStats,
7025
- tileService: tileService
7072
+ tileService: tileService,
7073
+ urlReferencesKnownSparseTileset: urlReferencesKnownSparseTileset
7026
7074
  });
7027
7075
 
7028
7076
  const glyphLogger = logger.scope('GlyphService');
@@ -11365,7 +11413,7 @@ class PanelRenderer extends BaseComponent {
11365
11413
  try {
11366
11414
  const { loadStyleById } = await Promise.resolve().then(function () { return styleService; });
11367
11415
  const styleEntry = await loadStyleById(region.styleId);
11368
- accessToken = styleEntry?.accessToken;
11416
+ accessToken = styleEntry?.accessToken ?? undefined;
11369
11417
  }
11370
11418
  catch {
11371
11419
  panelLogger.warn('Could not retrieve access token from stored style');
@@ -13977,5 +14025,5 @@ class OfflineManagerControl {
13977
14025
  }
13978
14026
  }
13979
14027
 
13980
- export { AnalyticsService, CONTENT_TYPES, CategorizedError, CleanupService, DB_NAME, DB_VERSION, DOWNLOAD_DEFAULTS, ERROR_MESSAGES, ErrorType, FontService, GLYPH_BLOCK_SIZE, GLYPH_CONFIG, GZIP_MAGIC_BYTES, GlyphService, ImportExportService, LogLevel, MAPBOX_API, MAPBOX_CACHE_TTL, MAPBOX_CLASSIC_STYLES, MAP_PROVIDERS, MAX_GLYPH_CODEPOINT, MaintenanceService, ModelService, OfflineManagerControl, OfflineMapDBVersionError, OfflineMapManager, RESOURCE_TYPES, RegionService, ResourceService, STORAGE_CONFIG, STORE_NAMES, STYLE_CONFIG, SUCCESS_MESSAGES, ScopedLogger, SpriteService, TILE_CONFIG, TileService, URL_SCHEMES, VALIDATION_PATTERNS, applyProxy, categorizeError, cleanupCompressedTiles, cleanupExpiredTiles, cleanupOldFonts, cleanupOldGlyphs, cleanupOldModels, cleanupOldSprites, cleanupOldStyles, cleanupOldTiles, cleanupService, clearAllCaches, configureLogger, configureProxy, configureSqlJs, convertStyleForServiceWorker, countCompressedTiles, createProgressTracker, createTileKey, dbPromise, OfflineMapManager as default, deleteStyleById, deleteStyles, deriveTileExtension, detectCssPrefix, detectStyleProvider, downloadFonts, downloadGlyphs, downloadModels, downloadSprites, downloadStyleWithProvider, downloadStyles, downloadTiles, escapeHtml$1 as escapeHtml, extractAccessToken, extractAllFontNames, extractFontNamesFromTextField, extractTileExtensionFromUrl, fetchResourceWithRetry, fetchWithRetry, fontService, formatBytes, formatDate, generateGlyphUrlsFromStyle, getExpiredResourceCount, getFontAnalytics, getFontStats, getGlyphAnalytics, getGlyphStats, getIcon, getModel, getModelStats, getRegionAnalytics, getSpriteAnalytics, getSpriteStats, getSqlJs, getStyleStats, getTileAnalytics, getTileStats, getUrlHostname, getUserErrorMessage, glyphService, hasImports, hostMatches, i18n, icons, idbFetchHandler, isMapboxHost, isMapboxProtocol, isStyleDownloaded, loadAllStoredRegions, loadGlyphs, loadStyleById, loadStyles, logger, modelKeyBelongsToStyle, modelService, normalizeSpriteProperty, normalizeStyleUrl, optimizeStorage, parseCacheExpiry, parseTileKey, patchStyleForOffline, performCleanup, processBatch, processStyleSources, registerOfflineServiceWorker, resetOfflineMapDB, resolveImports, resolveMapboxUrl, resourceKeyBelongsToStyle, rewriteMapboxCdnTileUrl, safeExecute, sanitizeIndoorExpressions, setupAutoCleanup, spriteService, stopAutoCleanup, t, tileService, unregisterOfflineServiceWorker, validateBounds, validateRegionOptions, validateResource, validateStyleForProvider, validateZoomLevels, verifyAndRepairFonts, verifyAndRepairGlyphs, verifyAndRepairModels, verifyAndRepairSprites };
14028
+ export { AnalyticsService, CONTENT_TYPES, CategorizedError, CleanupService, DB_NAME, DB_VERSION, DOWNLOAD_DEFAULTS, ERROR_MESSAGES, ErrorType, FontService, GLYPH_BLOCK_SIZE, GLYPH_CONFIG, GZIP_MAGIC_BYTES, GlyphService, ImportExportService, LogLevel, MAPBOX_API, MAPBOX_CACHE_TTL, MAPBOX_CLASSIC_STYLES, MAPBOX_STANDARD_SPARSE_TILESETS, MAP_PROVIDERS, MAX_GLYPH_CODEPOINT, MaintenanceService, ModelService, OfflineManagerControl, OfflineMapDBVersionError, OfflineMapManager, RESOURCE_TYPES, RegionService, ResourceService, STORAGE_CONFIG, STORE_NAMES, STYLE_CONFIG, SUCCESS_MESSAGES, ScopedLogger, SpriteService, TILE_CONFIG, TileService, URL_SCHEMES, VALIDATION_PATTERNS, applyProxy, categorizeError, cleanupCompressedTiles, cleanupExpiredTiles, cleanupOldFonts, cleanupOldGlyphs, cleanupOldModels, cleanupOldSprites, cleanupOldStyles, cleanupOldTiles, cleanupService, clearAllCaches, configureLogger, configureProxy, configureSqlJs, convertStyleForServiceWorker, countCompressedTiles, createProgressTracker, createTileKey, dbPromise, OfflineMapManager as default, deleteStyleById, deleteStyles, deriveTileExtension, detectCssPrefix, detectStyleProvider, downloadFonts, downloadGlyphs, downloadModels, downloadSprites, downloadStyleWithProvider, downloadStyles, downloadTiles, escapeHtml$1 as escapeHtml, extractAccessToken, extractAllFontNames, extractFontNamesFromTextField, extractTileExtensionFromUrl, fetchResourceWithRetry, fetchWithRetry, fontService, formatBytes, formatDate, generateGlyphUrlsFromStyle, getExpiredResourceCount, getFontAnalytics, getFontStats, getGlyphAnalytics, getGlyphStats, getIcon, getModel, getModelStats, getRegionAnalytics, getSpriteAnalytics, getSpriteStats, getSqlJs, getStyleStats, getTileAnalytics, getTileStats, getUrlHostname, getUserErrorMessage, glyphService, hasImports, hostMatches, i18n, icons, idbFetchHandler, isMapboxHost, isMapboxProtocol, isStyleDownloaded, loadAllStoredRegions, loadGlyphs, loadStyleById, loadStyles, logger, modelKeyBelongsToStyle, modelService, normalizeSpriteProperty, normalizeStyleUrl, optimizeStorage, parseCacheExpiry, parseTileKey, patchStyleForOffline, performCleanup, processBatch, processStyleSources, registerOfflineServiceWorker, resetOfflineMapDB, resolveImports, resolveMapboxUrl, resourceKeyBelongsToStyle, rewriteMapboxCdnTileUrl, safeExecute, sanitizeIndoorExpressions, setupAutoCleanup, spriteService, stopAutoCleanup, t, tileService, unregisterOfflineServiceWorker, urlReferencesKnownSparseTileset, validateBounds, validateRegionOptions, validateResource, validateStyleForProvider, validateZoomLevels, verifyAndRepairFonts, verifyAndRepairGlyphs, verifyAndRepairModels, verifyAndRepairSprites };
13981
14029
  //# sourceMappingURL=index.esm.js.map