Progressive Web App (PWA) — это веб-приложение, которое сочетает возможности традиционного сайта и нативного мобильного приложения. Такие приложения работают через браузер, но могут устанавливаться на устройство, функционировать в офлайн-режиме, отправлять уведомления и использовать локальное хранилище.
Для картографических приложений на базе Leaflet технология PWA особенно важна, поскольку позволяет:
Типичные примеры:
Минимальная архитектура состоит из нескольких компонентов:
Leaflet Application
│
├── index.html
├── app.js
├── styles.css
├── manifest.json
├── service-worker.js
│
├── tiles/
├── geojson/
└── images/
Каждый компонент выполняет отдельную задачу.
Файл описывает параметры установки приложения.
Фоновый скрипт, отвечающий за:
Основная библиотека отображения карты и геоданных.
Файл manifest.json содержит сведения о приложении.
Пример:
{
"name": "Leaflet Maps",
"short_name": "Maps",
"start_url": ".",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3388ff",
"icons": [
{
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Основные параметры:
| Параметр | Назначение |
|---|---|
| name | Полное название |
| short_name | Короткое название |
| start_url | Стартовая страница |
| display | Режим запуска |
| theme_color | Цвет интерфейса |
| icons | Иконки приложения |
В документе необходимо добавить ссылку:
<link rel="manifest" href="manifest.json">
Дополнительно задаются параметры оформления:
<meta name="theme-color" content="#3388ff">
Регистрация выполняется после загрузки страницы.
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("service-worker.js")
.then(() => {
console.log("Service Worker зарегистрирован");
})
.catch(error => {
console.error(error);
});
}
После успешной регистрации браузер начинает использовать Service Worker для обработки запросов.
Файл service-worker.js работает отдельно от основной
страницы.
Основные события:
self.addEventListener("install", event => {
});
self.addEventListener("activate", event => {
});
self.addEventListener("fetch", event => {
});
Выполняется при первой установке.
Запускается после установки.
Перехватывает сетевые запросы.
При установке можно заранее сохранить необходимые файлы.
const CACHE_NAME = "leaflet-v1";
const ASSETS = [
"/",
"/index.html",
"/styles.css",
"/app.js",
"/manifest.json"
];
Сохранение в кэш:
self.addEventListener("install", event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(ASSETS))
);
});
После первой загрузки файлы становятся доступными без интернета.
Простейшая стратегия:
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
);
});
Алгоритм:
Создание карты не отличается от обычного приложения.
const map = L.map("map").setView(
[51.1694, 71.4491],
10
);
Подключение слоя:
L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{
maxZoom: 19
}
).addTo(map);
Однако в PWA появляется возможность сохранять полученные тайлы локально.
Наиболее важная задача картографического PWA — сохранение тайлов.
Пример обработки:
self.addEventListener("fetch", event => {
const url = event.request.url;
if (url.includes("tile.openstreetmap.org")) {
event.respondWith(
caches.open("tiles")
.then(cache => {
return cache.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request)
.then(networkResponse => {
cache.put(
event.request,
networkResponse.clone()
);
return networkResponse;
});
});
})
);
}
});
Теперь каждый просмотренный тайл сохраняется локально.
Сначала используется кэш.
Cache → Network
Преимущества:
Недостаток:
Сначала сеть.
Network → Cache
Преимущества:
Недостаток:
Комбинированный подход.
Алгоритм:
Это один из самых популярных вариантов для карт.
Часто требуется сохранять не только тайлы, но и данные объектов.
Например:
fetch("data/cities.geojson")
.then(response => response.json())
.then(data => {
L.geoJSON(data).addTo(map);
});
Файл можно предварительно добавить в список кэшируемых ресурсов:
const ASSETS = [
"/",
"/data/cities.geojson"
];
После установки приложение сможет отображать объекты даже без подключения к сети.
Для больших объёмов геоданных предпочтительнее использовать IndexedDB.
Причины:
Пример создания базы:
const request = indexedDB.open(
"LeafletDB",
1
);
request.onupgradenee ded = event => {
const db = event.target.result;
db.createObjectStore(
"features",
{
keyPath: "id"
}
);
};
Добавление объекта:
const transaction =
db.transaction(
["features"],
"readwrite"
);
const store =
transaction.objectStore(
"features"
);
store.add({
id: 1,
geometry: feature
});
Такой подход широко используется в мобильных GIS-системах.
Проверка соединения:
if (!navigator.onLine) {
console.log("Офлайн");
}
События изменения состояния:
window.addEventListener(
"online",
() => console.log("Онлайн")
);
window.addEventListener(
"offline",
() => console.log("Офлайн")
);
На практике при отключении сети приложение может автоматически переключаться на локальные данные.
Для туристических и навигационных приложений часто реализуется загрузка региона заранее.
Общая схема:
Выбор региона
↓
Загрузка тайлов
↓
Кэширование
↓
Офлайн-доступ
Пользователь выбирает область карты, после чего система последовательно скачивает необходимые тайлы всех уровней масштабирования.
Тайлы способны быстро занимать значительный объём памяти.
Пример очистки старых кэшей:
self.addEventListener(
"activate",
event => {
event.waitUntil(
caches.keys()
.then(keys => {
return Promise.all(
keys.map(key => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
);
})
);
}
);
Подобный механизм помогает избежать накопления устаревших данных.
После изменения файлов версии кэша необходимо обновлять.
Например:
const CACHE_NAME =
"leaflet-v2";
При следующем открытии браузер создаст новый кэш и удалит старый.
Это позволяет распространять обновления без публикации в магазинах приложений.
PWA поддерживает push-сообщения.
В геоинформационных приложениях они используются для:
Обработка уведомления:
self.addEventListener(
"push",
event => {
event.waitUntil(
self.registration.showNotification(
"Новое событие",
{
body: "Получены новые данные"
}
)
);
}
);
Background Sync позволяет выполнять отложенную отправку данных.
Пример сценария:
Пользователь создал объект
↓
Нет интернета
↓
Данные сохранены локально
↓
Интернет появился
↓
Автоматическая отправка
Это особенно полезно для мобильных геодезических и инспекционных приложений.
Ключевые рекомендации:
Первый запуск
↓
Установка PWA
↓
Регистрация Service Worker
↓
Кэширование ресурсов
↓
Загрузка тайлов
↓
Сохранение GeoJSON
↓
Отключение сети
↓
Работа из локального кэша
↓
Восстановление соединения
↓
Синхронизация данных
Такая архитектура позволяет создавать полнофункциональные картографические приложения на базе Leaflet, способные работать как обычные сайты, как устанавливаемые приложения и как автономные офлайн-системы с локальным хранением картографических данных и последующей синхронизацией при появлении сетевого подключения.