修改configuration.yaml文件
Code: [全选] [Expand/Collapse]
- # Loads default set of integrations. Do not remove.
- default_config:
- # Load frontend themes from the themes folder
- frontend:
- themes: !include_dir_merge_named themes
- # *** 添加下面2行代码 ***
- extra_module_url:
- - /local/hass_gaode.js
Code: [全选] [Expand/Collapse]
- // ========== config ==========
- const GAODE_KEY = '21b3a11e940d284eecb6b66bdd090fcc'; // ←←← 换成自己的
- const MAX_Z = 18; // 高德最大支持 18 级
- const TILE_SIZE = 256; // 标准 256px 瓦片
- // =================================
- /* 降级算法:把高 z 映射到 maxZoom,并返回缩放/偏移量 */
- function downgradeTile(x, y, z, maxZoom) {
- if (z <= maxZoom) {
- return { srcX: x, srcY: y, srcZ: z, scale: 1, dx: 0, dy: 0 };
- }
- const scale = 2 ** (z - maxZoom);
- const srcX = Math.floor(x / scale);
- const srcY = Math.floor(y / scale);
- const offsetX = (x % scale) * TILE_SIZE / scale;
- const offsetY = (y % scale) * TILE_SIZE / scale;
- return {
- srcX, srcY, srcZ: maxZoom,
- scale,
- dx: -offsetX * scale,
- dy: -offsetY * scale
- };
- }
- /* 高德使用 4 个域名做负载均衡 */
- let subDomain = 0;
- function nextSub() { subDomain = (subDomain + 1) % 4; return `wprd0${subDomain + 1}`; }
- /* 生成高德瓦片地址
- layer: vec 矢量底图 | cva 矢量注记 | img 影像底图 | cia 影像注记
- */
- function buildGaodeUrl(x, y, z, layer) {
- return `https://${nextSub()}.is.autonavi.com/appmaptile?style=7&tiletype=${layer}&x=${x}&y=${y}&z=${z}&lang=zh_cn&size=1&scale=1&key=${GAODE_KEY}`;
- }
- /* 记录已降级过的 key,防止重复绘制 */
- const doneSet = new Set();
- /* 创建注记层 <img> */
- function createCvaImg(cvaSrc, templateImg) {
- const img = new Image();
- img.src = cvaSrc;
- img.className = 'leaflet-tile';
- img.style.cssText = templateImg.style.cssText; // 复制位置/变换
- img.style.mixBlendMode = 'unset';
- img.onload = () => img.classList.add('leaflet-tile-loaded');
- return img;
- }
- /* 核心替换函数 */
- function transformCartoImg(img, extraImgs) {
- const src = img.src;
- if (!src.includes('basemaps.cartocdn.com')) return;
- const m = src.match(/\/(\d+)\/(\d+)\/(\d+)(?:@2x)?\.png/);
- if (!m) return;
- let [, z, x, y] = m.map(Number);
- /* 1. 需要降级的情况 */
- if (z > MAX_Z) {
- const { srcX, srcY, srcZ, scale, dx, dy } = downgradeTile(x, y, z, MAX_Z);
- const key = `${srcX},${srcY},${srcZ},${z}`;
- if (doneSet.has(key)) { // 已处理过,直接隐藏
- img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
- img.style.display = 'none';
- return;
- }
- doneSet.add(key);
- img.downgradeKey = key;
- x = srcX; y = srcY; z = srcZ;
- /* 叠加缩放+偏移到原有 transform 上 */
- let t = img.style.transform || '';
- if (t.includes('translate3d(')) {
- const [, tx, ty] = t.match(/translate3d\(([^,]+),\s*([^,]+)/) || [];
- if (tx !== undefined) {
- const nx = parseFloat(tx) + dx;
- const ny = parseFloat(ty) + dy;
- t = t.replace(/translate3d\([^)]+\)/, `translate3d(${nx}px, ${ny}px, 0px)`);
- }
- }
- if (!t.includes('scale(')) t += ` scale(${scale})`;
- img.style.transform = t;
- img.style.transformOrigin = 'top left';
- img.style.width = TILE_SIZE + 'px';
- img.style.height = TILE_SIZE + 'px';
- }
- /* 2. 组装高德地址 */
- const vecSrc = buildGaodeUrl(x, y, z, 'vec');
- const cvaSrc = buildGaodeUrl(x, y, z, 'cva');
- /* 3. 注入 DOM */
- if (extraImgs) { // 来自 appendChild 拦截
- const cvaImg = createCvaImg(cvaSrc, img);
- cvaImg.style.transformOrigin = 'top left';
- extraImgs.push(cvaImg);
- img.src = vecSrc;
- } else { // 直接修改
- img.style.backgroundImage = `url("${vecSrc}")`;
- img.style.backgroundSize = `${TILE_SIZE}px ${TILE_SIZE}px`;
- img.src = cvaSrc;
- }
- console.debug('[高德替换]', src, '→', vecSrc, '+注记');
- }
- /* ========== DOM 监听部分(与原脚本一致,仅函数名替换) ========== */
- function initDomObserver() {
- const _appendChild = Element.prototype.appendChild;
- function onAdd(node) {
- if (node.nodeType !== 1) return;
- if (node.classList.contains('leaflet-layer')) {
- node.appendChild = function (child) {
- if (child.classList?.contains('leaflet-tile-container')) {
- child.querySelectorAll('img').forEach(transformCartoImg);
- child.appendChild = function (frag) {
- const addArr = [];
- [...frag.children].forEach(n => n.tagName === 'IMG' && transformCartoImg(n, addArr));
- addArr.forEach(i => frag.appendChild(i));
- [...frag.children].forEach(n => n.style.display === 'none' && n.remove());
- return _appendChild.call(this, frag);
- };
- }
- return _appendChild.call(this, child);
- };
- const tc = node.querySelector('.leaflet-tile-container');
- if (tc) tc.appendChild = function (f) { [...f.children].forEach(n => n.tagName === 'IMG' && transformCartoImg(n)); return _appendChild.call(this, f); };
- } else if (node.classList?.contains('leaflet-control-attribution')) {
- node.remove(); // 隐藏原版权
- }
- if (node.shadowRoot) initShadow(node.shadowRoot);
- }
- function onRemove(node) {
- if (node.nodeType !== 1) return;
- if (node.tagName === 'IMG') {
- if (node.downgradeKey) doneSet.delete(node.downgradeKey);
- node.cvaImgRef?.remove();
- }
- }
- const ob = new MutationObserver(list => list.forEach(m => {
- m.addedNodes.forEach(onAdd);
- m.removedNodes.forEach(onRemove);
- }));
- function initShadow(root) {
- ob.observe(root, { childList: true, subtree: true });
- root.querySelectorAll?.('.leaflet-layer').forEach(onAdd);
- }
- ob.observe(document, { childList: true, subtree: true });
- const orig = Element.prototype.attachShadow;
- Element.prototype.attachShadow = function (opt) {
- const sh = orig.call(this, opt);
- initShadow(sh);
- return sh;
- };
- initShadow(document.body);
- }
- initDomObserver();