【Obsidian】Dataview + 自定义 CSS 天气信息和摄影时刻

在Obsidian中,通过Dataview插件可以创建实用的摄影辅助工具,直接在笔记中动态显示天气数据和最佳拍摄时间。以下是实现方法:

城市天气实时显示

  • 调用wttr.in天气API获取数据
  • 自动转换英文天气描述为中文+Emoji
  • 显示温度范围、风速、湿度等关键信息
const container = dv.container;
const nameKey = "my_location_city";
let city = localStorage.getItem(nameKey) || "shanghai";

// 读取所有 --geo- 开头的自定义属性
function getAllCityKeys() {
  const keys = [];
  for (let sheet of document.styleSheets) {
    let rules;
    try { rules = sheet.cssRules; }
    catch { continue; }
    if (!rules) continue;
    for (let rule of rules) {
      if (rule.selectorText === ":root") {
        for (let i = 0; i < rule.style.length; i++) {
          const prop = rule.style[i];
          if (prop.startsWith("--geo-")) {
            keys.push(prop.replace("--geo-", "").trim());
          }
        }
      }
    }
  }
  return keys;
}

// 根据拼音获取城市信息
function getCityInfo(pinyin) {
  const varName = `--geo-${pinyin}`;
  const raw = getComputedStyle(document.documentElement)
    .getPropertyValue(varName)
    .trim()
    .replace(/["']/g, "");
  if (!raw) return null;
  const [lat, lon, name] = raw.split(",");
  return { lat: +lat, lon: +lon, name: name.trim() };
}

// 天气图标映射
function weatherIcon(desc) {
  const d = desc.toLowerCase();
  if (d.includes("thunder")) return "⛈️ 雷阵雨";
  if (d.includes("snow"))    return "❄️ 下雪";
  if (d.includes("mist")||d.includes("fog")||d.includes("haze")) return "🌫️ 雾霾";
  if (d.includes("rain"))    return "🌧️ 雨";
  if (d.includes("cloud")||d.includes("overcast")||d.includes("partly")) return "☁️ 多云";
  if (d.includes("sun")||d.includes("clear")) return "☀️ 晴";
  return "🌈 其他";
}

// 渲染主内容
const info = getCityInfo(city);
if (!info) {
  container.innerHTML = `<span>⚠️ 无法识别城市 ${city}</span>`;
} else {
  const { name } = info;
  const today = new Date().toLocaleDateString();

  container.innerHTML = `
    <div class="loc-sun-header" id="locHeader">
      <span>📅 ${today}</span>
      <span id="toggleFormBtn" class="loc-sun-btn" tabindex="0">📍${name}</span>
      <span id="weatherInfo">☁️ 加载天气中...</span>
    </div>
    <div id="geoForm" style="display:none; margin-top:8px;">
      <label style="display:flex; align-items:center; gap:6px;">
        城市拼音:
        <input type="text" id="locCity" list="cityAuto"
               placeholder="如 beijing" style="width:160px;" />
        <button id="saveBtn">保存</button>
      </label>
      <datalist id="cityAuto"></datalist>
    </div>
  `;

  // 获取所有城市键和对应信息
  const keys = getAllCityKeys();
  const cityInfos = keys.map(k => ({ key: k, name: getCityInfo(k)?.name || "" }));

  // 填充 datalist
  const datalist = container.querySelector("#cityAuto");
  cityInfos.forEach(ci => {
    const opt = document.createElement("option");
    opt.value = ci.key;
    opt.label = `${ci.key} - ${ci.name}`;
    datalist.appendChild(opt);
  });

  // 获取并显示天气
  fetch(`https://wttr.in/${encodeURIComponent(name)}?format=j1`)
    .then(res => res.json())
    .then(data => {
      const w = data.weather[0].hourly[4];
      const icon = weatherIcon(w.weatherDesc[0].value);
      const wind = w.windspeedKmph;
      const dir = w.winddir16Point;
      const humidity = w.humidity;
      const minT = data.weather[0].mintempC;
      const maxT = data.weather[0].maxtempC;
      const windMap = {
        N:"⬆️正北",NE:"↗️东北",E:"➡️正东",SE:"↘️东南",
        S:"⬇️正南",SW:"↙️西南",W:"⬅️正西",NW:"↖️西北"
      };
      const windText = windMap[dir]||dir;
      container.querySelector("#weatherInfo").innerText =
        `${icon} ${minT}°C~${maxT}°C 🍃${wind}km/h ${windText} 💧${humidity}%`;
    })
    .catch(() => {
      container.querySelector("#weatherInfo").innerText = `❌ 天气获取失败`;
    });

  // 设置交互逻辑
  setTimeout(() => {
    const toggleBtn = container.querySelector("#toggleFormBtn");
    const form = container.querySelector("#geoForm");
    const cityInput = container.querySelector("#locCity");
    const saveBtn = container.querySelector("#saveBtn");

    // 点击城市名称,切换表单显示并聚焦
    toggleBtn.onclick = () => {
      form.style.display = form.style.display === "none" ? "block" : "none";
      if (form.style.display === "block") cityInput.focus();
    };

    // 输入联想过滤
    cityInput.addEventListener("input", () => {
      const val = cityInput.value.toLowerCase();
      datalist.innerHTML = "";
      cityInfos
        .filter(ci => ci.key.includes(val))
        .forEach(ci => {
          const opt = document.createElement("option");
          opt.value = ci.key;
          opt.label = `${ci.key} - ${ci.name}`;
          datalist.appendChild(opt);
        });
    });

    // 保存逻辑
    saveBtn.onclick = () => {
      const inp = cityInput.value.trim().toLowerCase().replace(/[^\w]/g, "");
      if (!keys.includes(inp)) {
        alert("❌ 未识别城市拼音,请输入如 beijing、shanghai 等");
        cityInput.focus();
        return;
      }
      localStorage.setItem(nameKey, inp);
      location.reload();
    };

    cityInput.addEventListener("keypress", e => {
      if (e.key === "Enter") saveBtn.click();
    });
  }, 0);
}

相关摄影时刻&日出日落事件显示

  • 基于经纬度的天文算法
  • 自动计算黄金时刻和蓝色时刻
  • 直观的时间段可视化展示
const container = dv.container;
const nameKey = "my_location_city";
let city = localStorage.getItem(nameKey) || "shenzhen";

function getCityInfo(pinyin) {
  const raw = getComputedStyle(document.documentElement).getPropertyValue(`--geo-${pinyin}`).trim();
  if (!raw) return null;
  const [lat, lon, name] = raw.replace(/"/g, "").split(",");
  return { lat: parseFloat(lat), lon: parseFloat(lon), name };
}

function getSunTimes(date, lat, lon) {
  const rad = Math.PI / 180;
  const day = Math.floor((Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 86400000);
  const lngHour = lon / 15;
  const t_rise = day + ((6 - lngHour) / 24);
  const t_set = day + ((18 - lngHour) / 24);

  function calcTime(t) {
    const M = (0.9856 * t - 3.289) % 360;
    let L = M + (1.916 * Math.sin(M * rad)) + (0.020 * Math.sin(2 * M * rad)) + 282.634;
    L = (L + 360) % 360;
    let RA = Math.atan(0.91764 * Math.tan(L * rad)) / rad;
    RA = (RA + 360) % 360;
    RA += Math.floor(L / 90) * 90 - Math.floor(RA / 90) * 90;
    RA /= 15;
    const sinDec = 0.39782 * Math.sin(L * rad);
    const cosDec = Math.cos(Math.asin(sinDec));
    const cosH = (Math.cos(90.833 * rad) - sinDec * Math.sin(lat * rad)) / (cosDec * Math.cos(lat * rad));
    if (cosH > 1 || cosH < -1) return null;
    const H = (t === t_rise ? 360 - Math.acos(cosH) / rad : Math.acos(cosH) / rad) / 15;
    const T = H + RA - 0.06571 * t - 6.622;
    const UT = (T - lngHour + 24) % 24;
    return UT;
  }

  function toLocal(ut) {
    const d = new Date(date);
    d.setUTCHours(0, 0, 0, 0);
    d.setUTCMinutes(ut * 60);
    return d;
  }

  const riseUT = calcTime(t_rise);
  const setUT = calcTime(t_set);
  return {
    sunrise: toLocal(riseUT),
    sunset: toLocal(setUT),
  };
}

function fmt(d) {
  return d.getHours().toString().padStart(2, "0") + ":" + d.getMinutes().toString().padStart(2, "0");
}

const info = getCityInfo(city);
if (!info) {
  container.innerHTML = `<span>⚠️ 无法识别城市 ${city}</span>`;
} else {
  const { lat, lon } = info;
  const today = new Date();
  const times = getSunTimes(today, lat, lon);
  if (!times.sunrise || !times.sunset) {
    container.innerHTML = `<span>⚠️ 当前城市无法计算日出/日落</span>`;
  } else {
    const sr = times.sunrise, ss = times.sunset;
    const make = ms => new Date(ms);
    const blueMorningStart = make(sr - 45 * 60000), blueMorningEnd = make(sr - 15 * 60000);
    const goldenMorningStart = make(sr - 15 * 60000), goldenMorningEnd = make(sr + 30 * 60000);
    const goldenEveningStart = make(ss - 60 * 60000), goldenEveningEnd = make(ss + 15 * 60000);
    const blueEveningStart = make(ss + 15 * 60000), blueEveningEnd = make(ss + 45 * 60000);

    container.innerHTML = `
      <div class="loc-sun-line">
        <span>🟦 ${fmt(blueMorningStart)} - ${fmt(blueMorningEnd)}</span>
        <span>🟨 ${fmt(goldenMorningStart)} - ${fmt(goldenMorningEnd)}</span>
        <span>🌅 ${fmt(sr)}</span>
        <span>🌇 ${fmt(ss)}</span>
        <span>🟨 ${fmt(goldenEveningStart)} - ${fmt(goldenEveningEnd)}</span>
        <span>🟦 ${fmt(blueEveningStart)} - ${fmt(blueEveningEnd)}</span>
      </div>
    `;
  }
}

自定义 CSS 样式

下面是两个css文件,包含了显示样式和亿些城市的经纬度 文件资源管理器中导航至Vault\.obsidian\snippets,将css文件放在里面 然后在Obsidian中设置-外观-自定义CSS代码启用相关css代码

geo-cities
分享内容:geo-cities.css等2项 分享链接:http://ug.link/RSEGordon-NAS/filemgr/share-download/?id=8945fe8506a74daba0dda2568c4f3b62

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇