diff --git a/assets/js/map.ts b/assets/js/map.ts
index d957844..afe172b 100644
--- a/assets/js/map.ts
+++ b/assets/js/map.ts
@@ -204,13 +204,61 @@ let MapAPRSMap = {
}
}
- // Add OpenStreetMap tile layer
+ // Add OpenStreetMap tile layer with improved settings
try {
- const tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
- attribution:
- '© OpenStreetMap contributors | APRS.me',
+ // Tile provider options
+ const tileProviders = {
+ osm: {
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ attribution: '© OpenStreetMap contributors | APRS.me',
+ subdomains: ['a', 'b', 'c']
+ },
+ osmDE: {
+ url: "https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png",
+ attribution: '© OpenStreetMap contributors | APRS.me',
+ subdomains: ['a', 'b', 'c']
+ },
+ carto: {
+ url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
+ attribution: '© OpenStreetMap contributors © CARTO | APRS.me',
+ subdomains: ['a', 'b', 'c', 'd']
+ }
+ };
+
+ // Use OSM by default, but could be configured
+ const provider = tileProviders.osm;
+
+ const tileLayer = L.tileLayer(provider.url, {
+ attribution: provider.attribution,
maxZoom: 19,
+ subdomains: provider.subdomains,
+ tileSize: 256,
+ keepBuffer: 2,
+ updateWhenZooming: false,
+ updateInterval: 200,
+ errorTileUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
});
+
+ // Add event handler for tile errors with retry logic
+ let retryCount = new Map();
+ tileLayer.on('tileerror', function(error: any) {
+ const src = error.tile.src;
+ const count = retryCount.get(src) || 0;
+
+ if (count < 3) {
+ console.warn(`Tile load error (attempt ${count + 1}/3):`, src);
+ retryCount.set(src, count + 1);
+
+ // Exponential backoff
+ setTimeout(() => {
+ error.tile.src = src + (src.includes('?') ? '&' : '?') + '_retry=' + Date.now();
+ }, Math.pow(2, count) * 500);
+ } else {
+ console.error('Tile failed after 3 attempts:', src);
+ retryCount.delete(src);
+ }
+ });
+
tileLayer.addTo(self.map);
} catch (error) {
self.errors!.push("Tile layer failed: " + (error instanceof Error ? error.message : error));
diff --git a/lib/aprsme_web/router.ex b/lib/aprsme_web/router.ex
index 053e151..2f1e4b2 100644
--- a/lib/aprsme_web/router.ex
+++ b/lib/aprsme_web/router.ex
@@ -17,7 +17,7 @@ defmodule AprsmeWeb.Router do
plug :put_secure_browser_headers, %{
"content-security-policy" =>
- "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.sentry-cdn.com https://unpkg.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https: blob:; font-src 'self' data:; connect-src 'self' wss: https://*.ingest.sentry.io https://*.sentry.io https://nominatim.openstreetmap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org; media-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; frame-src 'self'; manifest-src 'self'; worker-src 'self' blob:"
+ "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.sentry-cdn.com https://unpkg.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https: http: blob:; font-src 'self' data:; connect-src 'self' wss: https://*.ingest.sentry.io https://*.sentry.io https://nominatim.openstreetmap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org https://*.tile.openstreetmap.de https://*.basemaps.cartocdn.com; media-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; frame-src 'self'; manifest-src 'self'; worker-src 'self' blob:"
}
plug :fetch_current_user