luci-app-adblock: replace the CARTO basemap with local outlines

CARTO now requires an API key for the raster basemaps at basemaps.cartocdn.com and watermarks every unauthenticated tile request, so the GeoIP map currently renders "API KEY REQUIRED" across the whole viewport.
The basemap is now drawn from country outlines that ship with the package: Natural Earth 1:110m, public domain, all attributes stripped, simplified to 36 kB (~10 kB in the apk).

Signed-off-by: Dirk Brenken <dev@brenken.org>
This commit is contained in:
Dirk Brenken committed 2026-08-27 13:43:53 +02:00
1 parent 01b60d0fc7
commit 7ce67a99d9
3 files changed
+173 -24

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ LUCI_TITLE:=LuCI support for Adblock
LUCI_DEPENDS:=+luci-base +luci-lib-uqr +adblock
PKG_VERSION:=4.5.7
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
@@ -9,6 +9,25 @@
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
<style>
html {
color-scheme: light dark;
--adb-water: #d6e3ec;
--adb-land: #f4f1ea;
--adb-coast: #a9a396;
--adb-grid: #c2d2de;
--adb-label: #97a3ad;
}
@media (prefers-color-scheme: dark) {
html {
--adb-water: #151b21;
--adb-land: #2a3138;
--adb-coast: #4d5862;
--adb-grid: #232b33;
--adb-label: #5d6a75;
}
}
html,
body {
height: 100%;
@@ -20,6 +39,22 @@
width: 100%;
}
.leaflet-container {
background: var(--adb-water);
}
/* Leaflet positions the icon element itself, so center on the inner span */
.continent span {
position: absolute;
transform: translate(-50%, -50%);
white-space: nowrap;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--adb-label);
}
.title {
display: block;
text-align: center;
@@ -56,8 +91,31 @@
/* maximum number of domains listed in a shared popup */
const MAX_DOMAINS = 5;
/* zoom level applied when the view is centered on a single point */
const POINT_ZOOM = 6;
/* zoom level of the initial view */
const OVERVIEW_ZOOM = 3;
/* continent labels are dropped as soon as the outlines carry the orientation */
const LABEL_ZOOM = 4;
/* The outlines are shipped without attributes, so the labels are placed here */
const CONTINENTS = [
{ name: 'North America', latlng: [48, -100] },
{ name: 'South America', latlng: [-13, -58] },
{ name: 'Europe', latlng: [56, 26] },
{ name: 'Africa', latlng: [4, 20] },
{ name: 'Asia', latlng: [46, 92] },
{ name: 'Oceania', latlng: [-25, 137] },
{ name: 'Antarctica', latlng: [-72, 15] }
];
/* country outlines shipped with the package, Natural Earth 1:110m, public domain */
const WORLD_FILE = 'world.json';
/* optional higher detail outlines, not shipped, see the adblock README */
const WORLD_FILE_HD = 'world-50m.json';
/* spacing of the graticule in degrees */
const GRID_STEP = 20;
/* mapData key holding the geo data of the local uplink(s) */
const HOME_KEY = 'homeIP';
@@ -69,13 +127,113 @@
const map = L.map('map', {
zoom: 3,
minZoom: 2,
maxZoom: 18,
maxZoom: 6,
center: [50, 10]
});
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
map.attributionControl.addAttribution('<a href="https://www.naturalearthdata.com">Natural Earth</a>');
/* the basemap is drawn from local country outlines instead of a tile service */
map.createPane('basemap');
map.getPane('basemap').style.zIndex = 200;
const basemapRenderer = L.canvas({ pane: 'basemap' });
function cssColor(name) {
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
function landStyle() {
return { color: cssColor('--adb-coast'), weight: 0.7, opacity: 1.0, fillColor: cssColor('--adb-land'), fillOpacity: 1.0 };
}
function gridStyle() {
return { color: cssColor('--adb-grid'), weight: 0.6, opacity: 1.0 };
}
/* the empty ocean is hard to read at low zoom levels, so add a plain graticule */
function graticule() {
const lines = [];
let value;
for (value = -80; value <= 80; value += GRID_STEP)
lines.push([[value, -180], [value, 180]]);
for (value = -180; value <= 180; value += GRID_STEP)
lines.push([[-85, value], [85, value]]);
return lines;
}
const gridLayer = L.polyline(graticule(), Object.assign({
pane: 'basemap',
renderer: basemapRenderer,
interactive: false
}, gridStyle())).addTo(map);
let landLayer = null;
const labelLayer = L.layerGroup(CONTINENTS.map(function (item) {
return L.marker(item.latlng, {
pane: 'basemap',
interactive: false,
keyboard: false,
icon: L.divIcon({
className: 'continent',
html: '<span>' + item.name + '</span>',
iconSize: [0, 0]
})
});
}));
function updateLabels() {
const wanted = map.getZoom() <= LABEL_ZOOM;
if (wanted !== map.hasLayer(labelLayer))
wanted ? labelLayer.addTo(map) : labelLayer.remove();
}
map.on('zoomend', updateLabels);
updateLabels();
function loadWorld(file) {
return fetch(file).then(function (response) {
if (!response.ok)
throw new Error(file + ': HTTP ' + response.status);
return response.json();
});
}
/* prefer the optional outlines, their absence is the normal case */
loadWorld(WORLD_FILE_HD)
.catch(function () {
return loadWorld(WORLD_FILE);
})
.then(function (world) {
landLayer = L.geoJSON(world, {
pane: 'basemap',
renderer: basemapRenderer,
interactive: false,
smoothFactor: 1.2,
style: landStyle
}).addTo(map);
})
.catch(function (error) {
/* the markers stay usable on the plain background */
console.error('adblock: unable to load the basemap,', error);
});
/* restyle in place when the browser switches between the light and dark scheme */
const colorScheme = window.matchMedia('(prefers-color-scheme: dark)');
if (colorScheme.addEventListener)
colorScheme.addEventListener('change', function () {
gridLayer.setStyle(gridStyle());
if (landLayer)
landLayer.setStyle(landStyle());
});
/* feature groups, so getBounds() is available for the initial view */
const blockedLayer = L.featureGroup().addTo(map);
@@ -252,20 +410,6 @@
return result;
}
function fitLayer(layer) {
const layers = layer.getLayers();
if (!layers.length)
return false;
if (layers.length === 1)
map.setView(layers[0].getLatLng(), POINT_ZOOM);
else
map.fitBounds(layer.getBounds(), { maxZoom: POINT_ZOOM, padding: [30, 30] });
return true;
}
/* load the data handed over by the adblock report page */
const mapData = sessionStorage.getItem('mapData');
let parsedData = null;
@@ -290,9 +434,13 @@
addMarker(homeLayer, entry, HOME_STYLE, homePopup);
});
/* center on the local uplink(s), fall back to the blocked domains */
if (!fitLayer(homeLayer))
fitLayer(blockedLayer);
/* The overall view stays zoomed out, center on the local uplink(s) */
const home = homeLayer.getLayers();
if (home.length)
map.setView(home[0].getLatLng(), OVERVIEW_ZOOM);
else if (blockedLayer.getLayers().length)
map.setView(blockedLayer.getBounds().getCenter(), OVERVIEW_ZOOM);
}
</script>
</body>
@@ -0,0 +1 @@
{"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[178.13,-17.5],[177.67,-17.38],[177.38,-18.16],[178.55,-18.15],[178.13,-17.5]]]},{"type":"Polygon","coordinates":[[[33.9,-0.95],[30.42,-1.13],[30.47,-2.41],[29.34,-4.5],[30.74,-8.34],[32.76,-9.23],[34.56,-11.52],[36.51,-11.72],[40.32,-10.32],[39.19,-8.49],[39.2,-4.68],[37.7,-3.1],[33.9,-0.95]]]},{"type":"Polygon","coordinates":[[[-8.67,27.66],[-11.39,26.88],[-13.89,23.69],[-14.75,21.5],[-17.02,21.42],[-17.06,21],[-12.93,21.33],[-11.94,23.37],[-11.97,25.93],[-8.69,25.88],[-8.68,27.4],[-8.67,27.66]]]},{"type":"MultiPolygon","coordinates":[[[[-122.84,49],[-116.05,49],[-107.05,49],[-95.16,49],[-91.64,48.14],[-88.38,48.3],[-82.55,45.35],[-82.44,41.68],[-76.82,43.63],[-74.87,45],[-71.51,45.01],[-69.24,47.45],[-67.14,45.14],[-65.36,43.55],[-61.04,45.27],[-64.47,46.24],[-65.06,49.23],[-60.03,50.24],[-55.68,52.15],[-57.33,54.63],[-59.57,55.2],[-64.58,60.34],[-69.59,61.06],[-73.84,62.44],[-78.11,62.32],[-78.52,58.8],[-76.54,56.53],[-81.4,52.16],[-82.27,55.15],[-85.01,55.3],[-90.9,57.28],[-94.63,60.11],[-93.16,62.02],[-81.39,67.11],[-81.28,69.16],[-88.02,68.62],[-95.21,71.92],[-98.44,67.78],[-109.95,67.98],[-127.45,70.38],[-136.5,68.9],[-140.99,69.71],[-141,60.31],[-135.48,59.79],[-130.54,54.8],[-127.44,50.83],[-122.84,49]]],[[[-55.6,51.32],[-59.27,47.6],[-53.07,46.66],[-53.48,49.25],[-55.6,51.32]]],[[[-78.77,72.35],[-82.32,73.75],[-90.21,72.24],[-84.94,69.97],[-77.29,69.77],[-72.65,67.28],[-77.9,65.31],[-71.02,62.91],[-66.17,61.93],[-65.32,64.38],[-62.16,66.16],[-66.97,69.19],[-78.77,72.35]]],[[[-121.54,74.45],[-125.93,71.87],[-123.09,70.9],[-115.51,73.48],[-121.54,74.45]]],[[[-106.52,73.08],[-115.19,73.31],[-119.4,71.56],[-113.31,68.54],[-105.96,69.18],[-106.52,73.08]]],[[[-91.59,81.89],[-85.09,79.35],[-89.49,76.47],[-80.56,76.18],[-61.89,82.36],[-79.31,83.13],[-91.59,81.89]]]]},{"type":"MultiPolygon","coordinates":[[[[-122.84,49],[-124.69,48.18],[-123.9,45.52],[-124.53,42.77],[-123.73,38.95],[-120.37,34.45],[-117.13,32.54],[-111.02,31.33],[-106.51,31.75],[-104.46,29.57],[-100.96,29.38],[-99.02,26.37],[-97.53,25.84],[-97.37,27.38],[-94.69,29.48],[-89.18,30.32],[-83.71,29.94],[-81.17,25.2],[-80.06,26.88],[-81.49,30.73],[-78.55,33.86],[-75.73,35.55],[-75.72,37.94],[-73.98,40.63],[-70.64,41.48],[-70.12,43.68],[-67.14,45.14],[-69.24,47.45],[-71.51,45.01],[-74.87,45],[-76.82,43.63],[-82.44,41.68],[-82.55,45.35],[-88.38,48.3],[-91.64,48.14],[-95.16,49],[-107.05,49],[-116.05,49],[-122.84,49]]],[[[-140.99,69.71],[-161.91,70.33],[-168.11,65.67],[-164.56,63.15],[-166.12,61.5],[-157.72,57.57],[-148.02,59.98],[-139.87,59.54],[-134.08,58.12],[-130.54,54.8],[-135.48,59.79],[-141,60.31],[-140.99,69.71]]]]},{"type":"Polygon","coordinates":[[[87.36,49.21],[83.38,51.07],[80.04,50.86],[76.53,54.18],[74.38,53.55],[69.07,55.39],[61.44,54.01],[59.97,51.96],[55.72,50.62],[50.77,51.69],[47.55,50.45],[46.47,48.39],[49.1,46.4],[53.04,46.85],[50.89,44.03],[52.5,41.78],[55.97,41.31],[55.93,45],[58.5,45.59],[62.01,43.5],[64.9,43.73],[66.71,41.17],[70.96,42.27],[74.21,43.3],[80.26,42.35],[79.97,44.92],[85.16,47],[87.36,49.21]]]},{"type":"Polygon","coordinates":[[[55.97,41.31],[58.63,42.75],[60.08,41.43],[66.52,37.36],[67.83,37.14],[69.33,40.73],[71.01,40.24],[70.96,42.27],[66.71,41.17],[64.9,43.73],[62.01,43.5],[58.5,45.59],[55.93,45],[55.97,41.31]]]},{"type":"Polygon","coordinates":[[[141,-2.6],[141.03,-9.12],[142.63,-9.33],[144.74,-7.63],[147.91,-10.13],[149.78,-10.39],[144.58,-3.86],[141,-2.6]]]},{"type":"MultiPolygon","coordinates":[[[[141,-2.6],[138.33,-1.7],[134.42,-2.77],[133.99,-0.78],[132.38,-0.37],[131.99,-2.82],[137.93,-5.39],[138.67,-7.32],[141.03,-9.12],[141,-2.6]]],[[[117.88,4.14],[115.87,4.31],[114.62,1.43],[110.51,0.77],[109.66,2.01],[109.09,-0.46],[110.22,-2.93],[116,-3.66],[117.52,-0.8],[117.88,4.14]]],[[[122.93,0.88],[120.04,0.57],[118.77,-2.8],[120.31,-2.93],[120.18,0.24],[122.93,0.88]]],[[[108.49,-6.42],[108.28,-7.77],[113.46,-8.35],[112.61,-6.95],[108.49,-6.42]]],[[[104.37,-1.08],[101.66,2.08],[100.64,2.1],[97.48,5.25],[95.38,4.97],[98.6,1.82],[102.58,-4.22],[105.82,-5.85],[106.11,-3.06],[104.37,-1.08]]]]},{"type":"Polygon","coordinates":[[[-57.63,-30.22],[-53.65,-26.92],[-54.63,-25.74],[-56.49,-27.55],[-57.78,-25.16],[-60.85,-23.88],[-62.69,-22.25],[-66.27,-21.83],[-67.11,-22.74],[-68.42,-24.52],[-68.3,-26.9],[-69.66,-28.46],[-70.54,-31.37],[-69.82,-34.19],[-71.12,-36.66],[-72.15,-42.25],[-71.55,-45.56],[-73.33,-50.38],[-71.91,-52.01],[-68.57,-52.3],[-69.14,-50.73],[-65.64,-47.24],[-64.38,-42.87],[-65.12,-41.06],[-62.15,-40.68],[-62.34,-38.83],[-59.23,-38.72],[-56.79,-36.9],[-58.43,-33.91],[-57.63,-30.22]]]},{"type":"Polygon","coordinates":[[[-69.59,-17.58],[-70.37,-18.35],[-70.09,-21.39],[-70.91,-27.64],[-71.44,-32.42],[-73.17,-37.12],[-74.69,-45.76],[-75.64,-46.65],[-74.95,-52.26],[-72.56,-53.53],[-68.57,-52.3],[-71.91,-52.01],[-73.33,-50.38],[-71.55,-45.56],[-72.15,-42.25],[-71.12,-36.66],[-69.82,-34.19],[-70.54,-31.37],[-69.66,-28.46],[-68.3,Line truncated