map-gl-offline 0.5.6 โ 0.7.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 +90 -443
- package/dist/index.d.ts +18 -8
- package/dist/index.esm.js +2955 -2657
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2967 -2656
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +2967 -2656
- package/dist/index.umd.js.map +1 -1
- package/dist/managers/offlineMapManager/index.d.ts +3 -57
- package/dist/managers/offlineMapManager/regionManagement.d.ts +3 -2
- package/dist/managers/offlineMapManager/resourceManagement.d.ts +8 -4
- package/dist/services/modelService.d.ts +57 -0
- package/dist/services/regionService.d.ts +30 -2
- package/dist/services/resourceService.d.ts +15 -5
- package/dist/storage/indexedDbManager.d.ts +18 -15
- package/dist/style.css +1 -1
- package/dist/types/database.d.ts +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/model.d.ts +62 -0
- package/dist/types/region.d.ts +59 -0
- package/dist/types/style.d.ts +11 -2
- package/dist/types/tile.d.ts +15 -0
- package/dist/ui/managers/downloadManager.d.ts +1 -6
- package/dist/ui/offlineManagerControl.d.ts +0 -11
- package/dist/utils/constants.d.ts +3 -2
- package/dist/utils/styleUtils.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,93 +4,35 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
**[Documentation](https://map-gl-offline.netlify.app)**
|
|
7
|
+
**[๐ Documentation](https://map-gl-offline.netlify.app)** ยท **[๐ฎ Live Demo](https://map-gl-offline-demo.netlify.app)** ยท **[๐ Issues](https://github.com/muimsd/map-gl-offline/issues)**
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## ๐ฌ Demo
|
|
9
|
+
TypeScript offline-map library for **MapLibre GL JS** and **Mapbox GL JS**. Download styles, tiles, sprites, glyphs, and fonts to IndexedDB; load them back with zero network. Ships with a glassmorphic UI control and a complete programmatic API.
|
|
12
10
|
|
|
13
11
|

|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## โจ Features
|
|
18
|
-
|
|
19
|
-
### ๐ฏ Core Offline Capabilities
|
|
20
|
-
|
|
21
|
-
- ๐บ๏ธ **Complete Offline Maps**: Download and store entire map regions with polygon-based selection
|
|
22
|
-
- ๐ฏ **Smart Tile Management**: Efficient vector/raster tile downloading, caching, and retrieval with zoom-level optimization
|
|
23
|
-
- ๐งฉ **Extra Tile Sources**: Save additional vector (MVT/PBF) and raster tile layers alongside the style's own sources
|
|
24
|
-
- ๐ค **Font & Glyph Support**: Comprehensive font and glyph management with Unicode range support
|
|
25
|
-
- ๐จ **Sprite Management**: Handle map sprites and icons offline with multi-resolution support (@1x, @2x)
|
|
26
|
-
- ๐ **Real-time Analytics**: Detailed storage analytics, performance metrics, and optimization recommendations
|
|
27
|
-
|
|
28
|
-
### ๐ Mapbox GL JS Support
|
|
29
|
-
|
|
30
|
-
- ๐ **mapbox:// Protocol Resolution**: Automatic resolution of `mapbox://` style, source, sprite, and glyph URLs
|
|
31
|
-
- ๐๏ธ **Mapbox Standard Style**: Full support including 3D models, raster-dem terrain, and import-based style resolution
|
|
32
|
-
- ๐
**Day/Night Light Presets**: Toggle between day and night lighting in Mapbox Standard style
|
|
33
|
-
- ๐ง๏ธ **Weather Controls**: Rain and snow effects for Mapbox Standard style
|
|
34
|
-
- ๐ **Auto-detection**: Automatically detects whether a style is Mapbox or MapLibre and applies the correct handling
|
|
35
|
-
|
|
36
|
-
### ๐จ Modern UI Control
|
|
37
|
-
|
|
38
|
-
- ๐ผ๏ธ **Glassmorphic Design**: Beautiful modern interface with glassmorphism effects and smooth animations
|
|
39
|
-
- ๐ **Dark/Light Theme**: Automatic theme switching with system preference detection
|
|
40
|
-
- ๐ **Polygon Drawing**: Interactive polygon tool for precise region selection
|
|
41
|
-
- ๐ **Live Progress**: Real-time download progress with detailed statistics
|
|
42
|
-
- ๐ฏ **Region Management**: Easy-to-use interface for managing multiple offline regions
|
|
43
|
-
- โก **Responsive**: Mobile-friendly design that adapts to all screen sizes
|
|
44
|
-
- ๐ **Internationalization**: English and Arabic language support with full RTL layout
|
|
13
|
+
## Features
|
|
45
14
|
|
|
46
|
-
|
|
15
|
+
- ๐บ๏ธ **Offline regions** โ polygon selection, smart tile management, extra vector/raster overlays
|
|
16
|
+
- ๐จ **Full resource capture** โ styles, sprites, fonts, glyphs with Unicode ranges
|
|
17
|
+
- ๐ **Mapbox GL + MapLibre GL** โ auto-detection, `mapbox://` URL resolution, Standard style with 3D/terrain
|
|
18
|
+
- ๐ **Analytics & cleanup** โ storage reports, auto-cleanup, quota-aware downloads
|
|
19
|
+
- ๐จ **UI control** โ glassmorphic panel, dark/light themes, English/Arabic (RTL), polygon drawing
|
|
20
|
+
- ๐ ๏ธ **Programmatic API** โ `downloadRegion` with per-phase progress, full TypeScript types
|
|
47
21
|
|
|
48
|
-
|
|
49
|
-
- ๐ง **Full TypeScript**: Complete type definitions, interfaces, and compile-time safety
|
|
50
|
-
- โก **Performance Optimized**: Concurrent downloads, async/await patterns, and memory-efficient operations
|
|
51
|
-
- ๐งน **Intelligent Cleanup**: Smart cleanup of expired data with customizable policies
|
|
52
|
-
- ๐ **Robust Error Handling**: Comprehensive error recovery, retry mechanisms, and graceful degradation
|
|
53
|
-
- ๐ **Enhanced Logging**: Detailed debugging with zoom-level specific logging (Z12 tracking)
|
|
54
|
-
|
|
55
|
-
## ๐ฆ Installation
|
|
22
|
+
## Install
|
|
56
23
|
|
|
57
24
|
```bash
|
|
58
25
|
npm install map-gl-offline
|
|
59
|
-
# or
|
|
60
|
-
yarn add map-gl-offline
|
|
61
|
-
# or
|
|
62
|
-
pnpm add map-gl-offline
|
|
63
26
|
```
|
|
64
27
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
For use via `<script>` tag, the library is available as the `mapgloffline` global (similar to `mapboxgl` and `maplibregl`):
|
|
28
|
+
Or via CDN as the `mapgloffline` global:
|
|
68
29
|
|
|
69
30
|
```html
|
|
70
31
|
<script src="https://unpkg.com/map-gl-offline/dist/index.umd.js"></script>
|
|
71
32
|
<link rel="stylesheet" href="https://unpkg.com/map-gl-offline/style.css" />
|
|
72
|
-
<script>
|
|
73
|
-
const manager = new mapgloffline.OfflineMapManager();
|
|
74
|
-
const control = new mapgloffline.OfflineManagerControl(manager, {
|
|
75
|
-
styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
|
|
76
|
-
});
|
|
77
|
-
map.addControl(control, 'top-right');
|
|
78
|
-
</script>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## ๐ Environment Setup
|
|
82
|
-
|
|
83
|
-
For development or when using Maptiler styles, create a `.env` file:
|
|
84
|
-
|
|
85
|
-
```env
|
|
86
|
-
VITE_MAPTILER_API_KEY=your_api_key_here
|
|
87
33
|
```
|
|
88
34
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
For Mapbox styles, you will also need a Mapbox access token from [Mapbox](https://www.mapbox.com/).
|
|
92
|
-
|
|
93
|
-
## ๐ Quick Start
|
|
35
|
+
## Quick Start
|
|
94
36
|
|
|
95
37
|
### MapLibre GL JS
|
|
96
38
|
|
|
@@ -100,11 +42,9 @@ import { OfflineMapManager, OfflineManagerControl } from 'map-gl-offline';
|
|
|
100
42
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
101
43
|
import 'map-gl-offline/style.css';
|
|
102
44
|
|
|
103
|
-
const styleUrl = 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_API_KEY';
|
|
104
|
-
|
|
105
45
|
const map = new maplibregl.Map({
|
|
106
46
|
container: 'map',
|
|
107
|
-
style:
|
|
47
|
+
style: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
|
|
108
48
|
center: [-74.006, 40.7128],
|
|
109
49
|
zoom: 12,
|
|
110
50
|
});
|
|
@@ -112,48 +52,33 @@ const map = new maplibregl.Map({
|
|
|
112
52
|
const offlineManager = new OfflineMapManager();
|
|
113
53
|
|
|
114
54
|
map.on('load', () => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
55
|
+
map.addControl(
|
|
56
|
+
new OfflineManagerControl(offlineManager, {
|
|
57
|
+
styleUrl: map.getStyle().sprite,
|
|
58
|
+
mapLib: maplibregl, // enables idb:// protocol
|
|
59
|
+
}),
|
|
60
|
+
'top-right',
|
|
61
|
+
);
|
|
122
62
|
});
|
|
123
63
|
```
|
|
124
64
|
|
|
125
65
|
### Mapbox GL JS
|
|
126
66
|
|
|
127
|
-
Mapbox GL JS v3
|
|
128
|
-
|
|
129
|
-
**Option 1: CLI (recommended)**
|
|
67
|
+
Mapbox GL JS v3 lacks `addProtocol`, so the library uses a Service Worker. Run **one** of:
|
|
130
68
|
|
|
131
69
|
```bash
|
|
132
|
-
npx map-gl-offline init
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
```js
|
|
138
|
-
// vite.config.js
|
|
139
|
-
import { offlineSwPlugin } from 'map-gl-offline/vite-plugin';
|
|
140
|
-
|
|
141
|
-
export default defineConfig({
|
|
142
|
-
plugins: [offlineSwPlugin()],
|
|
143
|
-
});
|
|
70
|
+
npx map-gl-offline init # CLI (recommended)
|
|
71
|
+
# or add to vite.config.js:
|
|
72
|
+
# import { offlineSwPlugin } from 'map-gl-offline/vite-plugin';
|
|
73
|
+
# plugins: [offlineSwPlugin()]
|
|
74
|
+
# or manually: cp node_modules/map-gl-offline/dist/idb-offline-sw.js public/
|
|
144
75
|
```
|
|
145
76
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
cp node_modules/map-gl-offline/dist/idb-offline-sw.js public/idb-offline-sw.js
|
|
150
|
-
```
|
|
77
|
+
Then:
|
|
151
78
|
|
|
152
79
|
```typescript
|
|
153
80
|
import mapboxgl from 'mapbox-gl';
|
|
154
81
|
import { OfflineMapManager, OfflineManagerControl } from 'map-gl-offline';
|
|
155
|
-
import 'mapbox-gl/dist/mapbox-gl.css';
|
|
156
|
-
import 'map-gl-offline/style.css';
|
|
157
82
|
|
|
158
83
|
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
|
|
159
84
|
|
|
@@ -165,381 +90,105 @@ const map = new mapboxgl.Map({
|
|
|
165
90
|
});
|
|
166
91
|
|
|
167
92
|
const offlineManager = new OfflineMapManager();
|
|
168
|
-
|
|
169
|
-
map.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
178
|
-
map.addControl(control, 'top-right');
|
|
179
|
-
});
|
|
93
|
+
map.on('load', () =>
|
|
94
|
+
map.addControl(
|
|
95
|
+
new OfflineManagerControl(offlineManager, {
|
|
96
|
+
styleUrl: 'mapbox://styles/mapbox/standard',
|
|
97
|
+
accessToken: mapboxgl.accessToken,
|
|
98
|
+
}),
|
|
99
|
+
'top-right',
|
|
100
|
+
),
|
|
101
|
+
);
|
|
180
102
|
```
|
|
181
103
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
The UI control provides:
|
|
185
|
-
|
|
186
|
-
- ๐ **Polygon drawing** for region selection
|
|
187
|
-
- ๐ **Download progress** tracking
|
|
188
|
-
- ๐๏ธ **Region management** (view, delete)
|
|
189
|
-
- ๐ **Theme toggle** (dark/light mode)
|
|
190
|
-
- ๐ **Storage analytics**
|
|
191
|
-
- ๐ **Language switcher** (English / Arabic with RTL)
|
|
104
|
+
## Programmatic Usage
|
|
192
105
|
|
|
193
|
-
|
|
106
|
+
`downloadRegion` runs the full pipeline (**style โ sprites โ glyphs โ tiles โ metadata**) with per-phase progress. `addRegion` alone only stores metadata โ use `downloadRegion` to actually fetch assets.
|
|
194
107
|
|
|
195
108
|
```typescript
|
|
196
109
|
import { OfflineMapManager } from 'map-gl-offline';
|
|
197
110
|
|
|
198
|
-
// Initialize the offline manager
|
|
199
111
|
const offlineManager = new OfflineMapManager();
|
|
200
112
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
minZoom: 10,
|
|
210
|
-
maxZoom: 16,
|
|
211
|
-
styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
|
|
212
|
-
onProgress: progress => {
|
|
213
|
-
console.log(`Progress: ${progress.percentage}%`);
|
|
214
|
-
console.log(`Current: ${progress.message}`);
|
|
113
|
+
await offlineManager.downloadRegion(
|
|
114
|
+
{
|
|
115
|
+
id: 'downtown',
|
|
116
|
+
name: 'Downtown',
|
|
117
|
+
bounds: [[-74.0559, 40.7128], [-74.0059, 40.7628]],
|
|
118
|
+
minZoom: 10,
|
|
119
|
+
maxZoom: 16,
|
|
120
|
+
styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
|
|
215
121
|
},
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// List all regions
|
|
225
|
-
const regions = await offlineManager.listStoredRegions();
|
|
226
|
-
console.log(`Stored regions:`, regions);
|
|
122
|
+
{
|
|
123
|
+
onProgress: ({ phase, percentage, message }) => {
|
|
124
|
+
console.log(`[${phase}] ${percentage.toFixed(1)}% ${message ?? ''}`);
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
);
|
|
227
128
|
|
|
228
|
-
//
|
|
129
|
+
// Manage
|
|
130
|
+
await offlineManager.listStoredRegions();
|
|
131
|
+
await offlineManager.getStoredRegion('downtown');
|
|
229
132
|
await offlineManager.deleteRegion('downtown');
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Analytics & Monitoring
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
// Get comprehensive storage analytics
|
|
236
|
-
const analytics = await offlineManager.getComprehensiveStorageAnalytics();
|
|
237
|
-
console.log(`Total storage: ${analytics.totalStorageSize} bytes`);
|
|
238
|
-
console.log(`Tiles: ${analytics.tiles.count} (${analytics.tiles.totalSize} bytes)`);
|
|
239
|
-
console.log(`Fonts: ${analytics.fonts.count} (${analytics.fonts.totalSize} bytes)`);
|
|
240
|
-
console.log(`Sprites: ${analytics.sprites.count} (${analytics.sprites.totalSize} bytes)`);
|
|
241
|
-
console.log(`Recommendations:`, analytics.recommendations);
|
|
242
|
-
```
|
|
243
133
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const deletedCount = await offlineManager.cleanupExpiredRegions();
|
|
249
|
-
console.log(`Cleaned ${deletedCount} expired regions`);
|
|
250
|
-
|
|
251
|
-
// Verify and repair fonts
|
|
252
|
-
const verification = await offlineManager.verifyAndRepairFonts('style_123', { removeCorrupted: true });
|
|
253
|
-
console.log(`Verified: ${verification.verified}, Repaired: ${verification.repaired}, Removed: ${verification.removed}`);
|
|
254
|
-
|
|
255
|
-
// Set up automatic cleanup (runs every 24 hours)
|
|
256
|
-
const cleanupId = await offlineManager.setupAutoCleanup({
|
|
257
|
-
intervalHours: 24,
|
|
258
|
-
maxAge: 30, // days
|
|
259
|
-
});
|
|
134
|
+
// Analytics & cleanup
|
|
135
|
+
await offlineManager.getComprehensiveStorageAnalytics();
|
|
136
|
+
await offlineManager.cleanupExpiredRegions();
|
|
137
|
+
await offlineManager.setupAutoCleanup({ intervalHours: 24, maxAge: 30 });
|
|
260
138
|
```
|
|
261
139
|
|
|
262
|
-
|
|
140
|
+
### Sparse-source detection
|
|
263
141
|
|
|
264
|
-
|
|
142
|
+
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 }`.
|
|
265
143
|
|
|
266
|
-
|
|
144
|
+
### Recovering from an incompatible DB
|
|
267
145
|
|
|
268
|
-
|
|
146
|
+
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:
|
|
269
147
|
|
|
270
148
|
```typescript
|
|
271
|
-
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
The constructor accepts optional service overrides for dependency injection (advanced usage). For most cases, use the default: `new OfflineMapManager()`.
|
|
275
|
-
|
|
276
|
-
**Core Methods:**
|
|
277
|
-
|
|
278
|
-
- `addRegion(options: OfflineRegionOptions)` - Download and store a map region
|
|
279
|
-
- `getStoredRegion(id: string)` - Retrieve a stored region by ID
|
|
280
|
-
- `deleteRegion(id: string)` - Delete a specific region and its resources
|
|
281
|
-
- `listStoredRegions()` - List all stored regions with metadata
|
|
282
|
-
- `listRegions()` - List all region options
|
|
283
|
-
|
|
284
|
-
**Analytics Methods:**
|
|
285
|
-
|
|
286
|
-
- `getComprehensiveStorageAnalytics()` - Get detailed storage statistics
|
|
287
|
-
- `getRegionAnalytics()` - Get aggregate analytics across all regions
|
|
288
|
-
- `getTileStatistics(styleId: string)` - Get tile-specific statistics
|
|
289
|
-
- `getFontStatistics(styleId: string)` - Get font statistics
|
|
290
|
-
- `getSpriteStatistics(styleId: string)` - Get sprite statistics
|
|
291
|
-
|
|
292
|
-
**Cleanup & Maintenance Methods:**
|
|
293
|
-
|
|
294
|
-
- `cleanupExpiredRegions()` - Remove regions past expiration date
|
|
295
|
-
- `performSmartCleanup(options)` - Intelligent cleanup with configurable criteria
|
|
296
|
-
- `cleanupOldFonts(styleId?, options?)` - Remove old font data
|
|
297
|
-
- `cleanupOldSprites(styleId?, options?)` - Remove old sprite data
|
|
298
|
-
- `cleanupOldGlyphs(styleId?, options?)` - Remove old glyph data
|
|
299
|
-
- `verifyAndRepairFonts(styleId, options?)` - Verify font integrity
|
|
300
|
-
- `verifyAndRepairSprites(styleId, options?)` - Verify sprite integrity
|
|
301
|
-
- `verifyAndRepairGlyphs(styleId, options?)` - Verify glyph integrity
|
|
302
|
-
- `setupAutoCleanup(options)` - Enable automatic periodic cleanup
|
|
303
|
-
- `stopAutoCleanup(cleanupId?)` - Disable a specific auto-cleanup
|
|
304
|
-
- `stopAllAutoCleanup()` - Disable all auto-cleanups
|
|
305
|
-
- `performCompleteMaintenance(options?)` - Run comprehensive maintenance
|
|
306
|
-
|
|
307
|
-
### OfflineManagerControl
|
|
149
|
+
import { dbPromise, OfflineMapDBVersionError, resetOfflineMapDB } from 'map-gl-offline';
|
|
308
150
|
|
|
309
|
-
UI control for MapLibre GL JS and Mapbox GL JS with glassmorphic design.
|
|
310
|
-
|
|
311
|
-
**Constructor:**
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
const offlineManager = new OfflineMapManager();
|
|
315
|
-
|
|
316
|
-
const control = new OfflineManagerControl(offlineManager, {
|
|
317
|
-
styleUrl: 'https://example.com/style.json', // Map style URL (required)
|
|
318
|
-
theme?: 'light' | 'dark', // UI theme (default: 'dark')
|
|
319
|
-
showBbox?: boolean, // Show region bounding boxes (default: false)
|
|
320
|
-
accessToken?: string, // Mapbox access token (for mapbox:// URLs)
|
|
321
|
-
mapLib?: MapLibProtocol, // Map library module (e.g. maplibregl) for idb:// protocol
|
|
322
|
-
});
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
**Features:**
|
|
326
|
-
|
|
327
|
-
- Interactive polygon drawing for region selection
|
|
328
|
-
- Real-time download progress tracking
|
|
329
|
-
- Region management (view, delete)
|
|
330
|
-
- Theme toggle (dark/light mode)
|
|
331
|
-
- Storage analytics display
|
|
332
|
-
- Language switcher (English / Arabic with RTL support)
|
|
333
|
-
- Responsive mobile-friendly design
|
|
334
|
-
|
|
335
|
-
## ๐ง Configuration Options
|
|
336
|
-
|
|
337
|
-
### OfflineRegionOptions
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
interface OfflineRegionOptions {
|
|
341
|
-
id: string; // Unique region identifier
|
|
342
|
-
name: string; // Human-readable name (required)
|
|
343
|
-
bounds: [[number, number], [number, number]]; // [[lng, lat], [lng, lat]]
|
|
344
|
-
minZoom: number; // Minimum zoom level (e.g., 10)
|
|
345
|
-
maxZoom: number; // Maximum zoom level (e.g., 16)
|
|
346
|
-
styleUrl?: string; // Map style URL
|
|
347
|
-
expiry?: number; // Expiration timestamp (ms since epoch)
|
|
348
|
-
deleteOnExpiry?: boolean; // Auto-delete on expiration
|
|
349
|
-
multipleRegions?: boolean; // Part of a multi-region download
|
|
350
|
-
tileExtension?: string; // Tile extension (pbf, mvt, png, etc.)
|
|
351
|
-
extraSources?: ExtraSource[]; // Additional tile sources to download alongside the style
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
interface ExtraSource {
|
|
355
|
-
id: string; // Unique source identifier
|
|
356
|
-
type?: 'vector' | 'raster' | 'raster-dem'; // Defaults to 'vector'
|
|
357
|
-
tiles: string[]; // Tile URL templates with {z}/{x}/{y}
|
|
358
|
-
minzoom?: number;
|
|
359
|
-
maxzoom?: number;
|
|
360
|
-
attribution?: string;
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Extra Tile Sources
|
|
365
|
-
|
|
366
|
-
Save additional vector or raster layers (e.g. custom overlays) alongside the style's own sources:
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
await manager.addRegion({
|
|
370
|
-
id: 'downtown',
|
|
371
|
-
name: 'Downtown',
|
|
372
|
-
bounds: [[-74.05, 40.71], [-74.00, 40.76]],
|
|
373
|
-
minZoom: 10,
|
|
374
|
-
maxZoom: 16,
|
|
375
|
-
styleUrl: 'https://example.com/style.json',
|
|
376
|
-
extraSources: [
|
|
377
|
-
{
|
|
378
|
-
id: 'buildings',
|
|
379
|
-
type: 'vector',
|
|
380
|
-
tiles: ['https://tiles.example.com/buildings/{z}/{x}/{y}.pbf'],
|
|
381
|
-
minzoom: 13,
|
|
382
|
-
maxzoom: 16,
|
|
383
|
-
},
|
|
384
|
-
],
|
|
385
|
-
});
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
When using the `OfflineManagerControl`, the region download form auto-discovers tile sources on the live map and shows them as checkboxes โ users pick which extra layers to include.
|
|
389
|
-
|
|
390
|
-
## ๐ฏ Use Cases
|
|
391
|
-
|
|
392
|
-
- ๐๏ธ **Outdoor & Recreation Apps**: Hiking, camping, and adventure apps with offline trail maps
|
|
393
|
-
- ๐ฑ **Field Data Collection**: Survey and data collection in remote areas
|
|
394
|
-
- ๐จ **Emergency Response**: Critical map access during network outages
|
|
395
|
-
- โ๏ธ **Travel Apps**: Tourist apps with offline city maps
|
|
396
|
-
- ๐ **Fleet Management**: Vehicle tracking with offline map fallback
|
|
397
|
-
- ๐ **Asset Management**: Field service apps with offline capability
|
|
398
|
-
- ๐ **Educational Apps**: Geography and learning apps with downloadable maps
|
|
399
|
-
- ๐๏ธ **Construction & Engineering**: Site management with offline blueprints
|
|
400
|
-
- ๐พ **Bandwidth Optimization**: Reduce data costs by pre-downloading maps
|
|
401
|
-
|
|
402
|
-
## ๐ก Best Practices
|
|
403
|
-
|
|
404
|
-
### Performance Optimization
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
// Balance quality vs storage with appropriate zoom levels
|
|
408
|
-
const region = {
|
|
409
|
-
minZoom: 10, // Don't go too low (tile count grows exponentially)
|
|
410
|
-
maxZoom: 16, // Don't go too high (diminishing returns)
|
|
411
|
-
bounds: [
|
|
412
|
-
/* ... */
|
|
413
|
-
],
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
// Monitor storage usage
|
|
417
|
-
const analytics = await manager.getComprehensiveStorageAnalytics();
|
|
418
|
-
if (analytics.totalStorageSize > 500 * 1024 * 1024) {
|
|
419
|
-
console.warn('High storage usage detected');
|
|
420
|
-
await manager.performSmartCleanup({ maxStorageSize: 500 * 1024 * 1024 });
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Use progressive loading for better UX
|
|
424
|
-
const progressiveDownload = {
|
|
425
|
-
priorityZoomLevels: [12, 13, 11, 14, 10, 15, 16],
|
|
426
|
-
onProgress: p => updateUI(p),
|
|
427
|
-
};
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### Error Handling
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
151
|
try {
|
|
434
|
-
await
|
|
435
|
-
} catch (
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
} else {
|
|
442
|
-
console.error('Unexpected error:', error);
|
|
152
|
+
await dbPromise;
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (err instanceof OfflineMapDBVersionError) {
|
|
155
|
+
if (confirm('Offline storage is incompatible. Clear it?')) {
|
|
156
|
+
await resetOfflineMapDB(); // destructive
|
|
157
|
+
location.reload();
|
|
158
|
+
}
|
|
443
159
|
}
|
|
444
160
|
}
|
|
445
161
|
```
|
|
446
162
|
|
|
447
|
-
|
|
163
|
+
> **Upgrading from 0.5.x?** Read the [0.6.0 migration guide](https://map-gl-offline.netlify.app/docs/migration-0.6) โ covers the rename of `ResourceService.getXxxStatistics` โ `getXxxStats`, the `addRegion` vs `downloadRegion` split, and the `expiry` timestamp fix.
|
|
448
164
|
|
|
449
|
-
|
|
450
|
-
| ------- | ------- | ------- |
|
|
451
|
-
| Chrome | 51+ | โ
|
|
|
452
|
-
| Firefox | 45+ | โ
|
|
|
453
|
-
| Safari | 10+ | โ
|
|
|
454
|
-
| Edge | 79+ | โ
|
|
|
455
|
-
| Mobile | Modern | โ
|
|
|
165
|
+
## API at a glance
|
|
456
166
|
|
|
457
|
-
**
|
|
167
|
+
- **Regions** โ `downloadRegion`, `loadRegion`, `addRegion`, `getStoredRegion`, `listStoredRegions`, `listRegions`, `deleteRegion`
|
|
168
|
+
- **Analytics** โ `getComprehensiveStorageAnalytics`, `getRegionAnalytics`, `getTileStats`, `getFontStats`, `getSpriteStats`, `getGlyphStats`
|
|
169
|
+
- **Cleanup** โ `cleanupExpiredRegions`, `performSmartCleanup`, `cleanupOld{Fonts,Sprites,Glyphs}`, `verifyAndRepair{Fonts,Sprites,Glyphs}`, `setupAutoCleanup`, `performCompleteMaintenance`
|
|
170
|
+
- **Import / Export** โ `exportRegionAsJSON`, `exportRegionAsPMTiles`, `exportRegionAsMBTiles`, `importRegion`
|
|
171
|
+
- **Storage utilities** โ `dbPromise`, `OfflineMapDBVersionError`, `resetOfflineMapDB`, `loadAllStoredRegions`, `resourceKeyBelongsToStyle`
|
|
458
172
|
|
|
459
|
-
-
|
|
460
|
-
- ES2015+ JavaScript
|
|
461
|
-
- Async/await support
|
|
462
|
-
- Web Workers (optional, for background tasks)
|
|
173
|
+
See the **[full API reference](https://map-gl-offline.netlify.app/docs/api-reference)** and **[examples](https://map-gl-offline.netlify.app/docs/examples)** for every option and pattern.
|
|
463
174
|
|
|
464
|
-
##
|
|
175
|
+
## Browser compatibility
|
|
465
176
|
|
|
466
|
-
|
|
177
|
+
Chrome 51+ ยท Firefox 45+ ยท Safari 10+ ยท Edge 79+ ยท modern mobile browsers. Requires IndexedDB and ES2015+.
|
|
467
178
|
|
|
468
|
-
|
|
179
|
+
## Contributing
|
|
469
180
|
|
|
470
181
|
```bash
|
|
471
|
-
# Clone repository
|
|
472
182
|
git clone https://github.com/muimsd/map-gl-offline.git
|
|
473
|
-
cd map-gl-offline
|
|
474
|
-
|
|
475
|
-
#
|
|
476
|
-
npm
|
|
477
|
-
|
|
478
|
-
# Run development server
|
|
479
|
-
npm run dev
|
|
480
|
-
|
|
481
|
-
# Run tests
|
|
482
|
-
npm test
|
|
483
|
-
|
|
484
|
-
# Build library
|
|
485
|
-
npm run build
|
|
486
|
-
|
|
487
|
-
# Run MapLibre example app
|
|
488
|
-
cd examples/maplibre
|
|
489
|
-
npm install
|
|
490
|
-
npm run dev
|
|
183
|
+
cd map-gl-offline && npm install
|
|
184
|
+
npm run dev # dev harness
|
|
185
|
+
npm test # unit tests
|
|
186
|
+
npm run build # library
|
|
491
187
|
```
|
|
492
188
|
|
|
493
|
-
|
|
189
|
+
Issues and PRs welcome. See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
494
190
|
|
|
495
|
-
|
|
496
|
-
map-gl-offline/
|
|
497
|
-
โโโ src/
|
|
498
|
-
โ โโโ managers/ # Core offline manager
|
|
499
|
-
โ โโโ services/ # Tile, font, sprite services
|
|
500
|
-
โ โโโ storage/ # IndexedDB management
|
|
501
|
-
โ โโโ ui/ # UI components & controls
|
|
502
|
-
โ โ โโโ translations/ # i18n (English, Arabic)
|
|
503
|
-
โ โโโ utils/ # Utilities & helpers
|
|
504
|
-
โ โโโ types/ # TypeScript definitions
|
|
505
|
-
โโโ bin/ # CLI (map-gl-offline init) & Vite plugin
|
|
506
|
-
โโโ examples/
|
|
507
|
-
โ โโโ maplibre/ # MapLibre GL JS example app
|
|
508
|
-
โ โโโ mapbox-gl/ # Mapbox GL JS example app
|
|
509
|
-
โโโ docs/ # Docusaurus documentation site
|
|
510
|
-
โโโ tests/ # Test suites
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
## ๐ Support & Links
|
|
514
|
-
|
|
515
|
-
- ๐ [Documentation](https://map-gl-offline.netlify.app)
|
|
516
|
-
- ๐ฎ [Live Demo](https://map-gl-offline-demo.netlify.app)
|
|
517
|
-
- ๐ [Report Issues](https://github.com/muimsd/map-gl-offline/issues)
|
|
518
|
-
- ๐ฌ [Discussions](https://github.com/muimsd/map-gl-offline/discussions)
|
|
519
|
-
- โญ [Feature Requests](https://github.com/muimsd/map-gl-offline/issues/new)
|
|
520
|
-
- ๐ [Star on GitHub](https://github.com/muimsd/map-gl-offline)
|
|
521
|
-
|
|
522
|
-
## ๐ Recent Updates
|
|
523
|
-
|
|
524
|
-
See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
|
525
|
-
|
|
526
|
-
**v0.5.5 (Latest):** Extra tile source selection for offline regions โ pick additional vector/raster layers to download alongside the style's own sources.
|
|
527
|
-
|
|
528
|
-
**v0.5.3:** 28% bundle size reduction (783 KB to 565 KB), 20+ bug fixes, XSS prevention, import atomicity, `@/` path alias for all imports.
|
|
529
|
-
|
|
530
|
-
**v0.5.2:** CLI command (`npx map-gl-offline init`), Vite plugin for Service Worker, Mapbox GL example app.
|
|
531
|
-
|
|
532
|
-
**v0.5.0:** Mapbox GL JS support, Standard style with 3D/terrain, day/night presets, rain/snow, i18n (English & Arabic with RTL).
|
|
533
|
-
|
|
534
|
-
## ๐ Acknowledgments
|
|
535
|
-
|
|
536
|
-
- [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) - Open-source map rendering engine
|
|
537
|
-
- [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) - Commercial map rendering engine
|
|
538
|
-
- [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) - Browser storage API
|
|
539
|
-
- [Tilebelt](https://github.com/mapbox/tilebelt) - Tile coordinate utilities
|
|
540
|
-
- [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework
|
|
541
|
-
|
|
542
|
-
## ๐ License
|
|
191
|
+
## License
|
|
543
192
|
|
|
544
193
|
MIT ยฉ [Muhammad Imran Siddique](https://github.com/muimsd)
|
|
545
194
|
|
|
@@ -547,10 +196,8 @@ MIT ยฉ [Muhammad Imran Siddique](https://github.com/muimsd)
|
|
|
547
196
|
|
|
548
197
|
<div align="center">
|
|
549
198
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
[๐ Documentation](https://map-gl-offline.netlify.app) โข [๐ฎ Live Demo](https://map-gl-offline-demo.netlify.app) โข [โญ Star on GitHub](https://github.com/muimsd/map-gl-offline)
|
|
199
|
+
[๐ Docs](https://map-gl-offline.netlify.app) ยท [๐ฎ Demo](https://map-gl-offline-demo.netlify.app) ยท [โญ GitHub](https://github.com/muimsd/map-gl-offline)
|
|
553
200
|
|
|
554
|
-
<a href="https://www.buymeacoffee.com/muimsd" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height:
|
|
201
|
+
<a href="https://www.buymeacoffee.com/muimsd" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 45px;"></a>
|
|
555
202
|
|
|
556
203
|
</div>
|
package/dist/index.d.ts
CHANGED
|
@@ -34,14 +34,23 @@
|
|
|
34
34
|
* ```typescript
|
|
35
35
|
* const offlineManager = new OfflineMapManager();
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
37
|
+
* // downloadRegion runs the full pipeline: style (if missing) โ sprites โ glyphs โ tiles โ metadata.
|
|
38
|
+
* // addRegion on its own only stores metadata; use downloadRegion to actually fetch assets.
|
|
39
|
+
* await offlineManager.downloadRegion(
|
|
40
|
+
* {
|
|
41
|
+
* id: 'sf',
|
|
42
|
+
* name: 'San Francisco',
|
|
43
|
+
* bounds: [[-122.5, 37.7], [-122.3, 37.9]],
|
|
44
|
+
* minZoom: 10,
|
|
45
|
+
* maxZoom: 14,
|
|
46
|
+
* styleUrl: 'https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY',
|
|
47
|
+
* },
|
|
48
|
+
* {
|
|
49
|
+
* onProgress: ({ phase, completed, total, percentage }) => {
|
|
50
|
+
* console.log(`[${phase}] ${completed}/${total} (${percentage.toFixed(1)}%)`);
|
|
51
|
+
* },
|
|
52
|
+
* }
|
|
53
|
+
* );
|
|
45
54
|
* ```
|
|
46
55
|
*/
|
|
47
56
|
export { OfflineMapManager } from './managers/offlineMapManager';
|
|
@@ -49,6 +58,7 @@ export * from './services/tileService';
|
|
|
49
58
|
export * from './services/fontService';
|
|
50
59
|
export * from './services/glyphService';
|
|
51
60
|
export * from './services/spriteService';
|
|
61
|
+
export * from './services/modelService';
|
|
52
62
|
export * from './services/cleanupService';
|
|
53
63
|
export * from './services/styleService';
|
|
54
64
|
export * from './services/regionService';
|