├── README.md ├── public ├── logo.webp ├── favicon-64.png ├── fonts │ ├── Ubuntu-R.woff2 │ ├── CascadiaMono.woff2 │ └── DINPro-Medium.woff2 └── favicon.svg ├── src ├── env.d.ts ├── styles │ ├── base.css │ └── markdown.css ├── pages │ ├── about.astro │ ├── 404.astro │ ├── index.astro │ ├── blog.astro │ ├── blog │ │ └── [...slug].astro │ └── syy.astro ├── components │ ├── mdx │ │ └── Blockquote.astro │ ├── icons │ │ ├── moon.astro │ │ ├── sun.astro │ │ └── date.astro │ ├── Nav.astro │ ├── PostsMenu.astro │ ├── PostList.astro │ ├── Footer.astro │ ├── CodeCopy.astro │ ├── DoubanItem.astro │ ├── vue │ │ ├── TileDebug.vue │ │ ├── TileCalc.vue │ │ ├── StrongholdClac.vue │ │ └── gmapstyle.vue │ └── DarkThemeSwitch.astro ├── layouts │ ├── MarkdownLayout.astro │ └── BaseLayout.astro └── content │ ├── config.ts │ └── blog │ ├── pip 换源.md │ ├── Termux 安装 Mitmproxy.md │ ├── fx-es模拟器编译及使用.md │ ├── 末地传送门坐标计算.mdx │ ├── 树莓派ImmortalWrt旁路由设置.md │ ├── 耕地等别计算.md │ ├── 自定义谷歌XYZ瓦片地图样式.mdx │ ├── Termux 安装 Matplotlib.md │ ├── 瓦片计算.mdx │ ├── Markdown基本语法.md │ ├── 在Windows上编译Mdbtools.md │ └── Python绘制风玫瑰图.md ├── .gitignore ├── tsconfig.json ├── tailwind.config.mjs ├── package.json └── astro.config.mjs /README.md: -------------------------------------------------------------------------------- 1 | # liuxs.pro 2 | 3 | My Website Build With Astro 4 | -------------------------------------------------------------------------------- /public/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxspro/liuxs.pro/main/public/logo.webp -------------------------------------------------------------------------------- /public/favicon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxspro/liuxs.pro/main/public/favicon-64.png -------------------------------------------------------------------------------- /public/fonts/Ubuntu-R.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxspro/liuxs.pro/main/public/fonts/Ubuntu-R.woff2 -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /public/fonts/CascadiaMono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxspro/liuxs.pro/main/public/fonts/CascadiaMono.woff2 -------------------------------------------------------------------------------- /public/fonts/DINPro-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxspro/liuxs.pro/main/public/fonts/DINPro-Medium.woff2 -------------------------------------------------------------------------------- /src/styles/base.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "din"; 3 | src: url(/fonts/DINPro-Medium.woff2) format("woff2"); 4 | font-display: swap; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/about.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/BaseLayout.astro"; 3 | --- 4 | 5 | 6 |

我的 Astro 网站

7 |
8 | -------------------------------------------------------------------------------- /src/components/mdx/Blockquote.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const props = Astro.props; 3 | --- 4 | 5 |
6 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /src/pages/404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | --- 4 | 5 | 6 |
7 |

Sorry, Nothing Here

8 |
9 |
10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "strictNullChecks": true, 5 | "allowJs": true, 6 | "baseUrl": ".", 7 | "paths": { 8 | "@components/*": [ 9 | "src/components/*" 10 | ], 11 | "@layouts/*": [ 12 | "src/layouts/*" 13 | ], 14 | "@styles/*": [ 15 | "src/styles/*" 16 | ] 17 | }, 18 | "jsx": "preserve" 19 | }, 20 | "exclude": [ 21 | "dist" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/layouts/MarkdownLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "./BaseLayout.astro"; 3 | import CodeCopy from "@components/CodeCopy.astro"; 4 | import "@styles/markdown.css"; 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 | -------------------------------------------------------------------------------- /src/content/config.ts: -------------------------------------------------------------------------------- 1 | // 从 `astro:content` 导入辅助工具 2 | import { z, defineCollection } from "astro:content"; 3 | // 为每一个集合定义一个 `type` 和 `schema` 4 | const postsCollection = defineCollection({ 5 | type: "content", 6 | schema: z.object({ 7 | title: z.string(), 8 | date: z.date(), 9 | tags: z.array(z.string()).default(["others"]), 10 | draft: z.boolean().default(false), 11 | }), 12 | }); 13 | // 导出一个单独的 `collections` 对象来注册你的集合 14 | export const collections = { 15 | blog: postsCollection, 16 | }; 17 | -------------------------------------------------------------------------------- /src/components/icons/moon.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class_ } = Astro.props; 3 | --- 4 | 5 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/icons/sun.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class_ } = Astro.props; 3 | --- 4 | 5 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/Nav.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import DarkThemeSwitch from "./DarkThemeSwitch.astro"; 3 | import "@styles/base.css"; 4 | --- 5 | 6 | 21 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import BaseLayout from "@layouts/BaseLayout.astro"; 3 | --- 4 | 5 | 6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 | Read My Blog 15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /src/components/PostsMenu.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import PostList from "./PostList.astro"; 3 | import type { CollectionEntry } from "astro:content"; 4 | interface Props { 5 | Posts: { [year: number | string]: CollectionEntry<"blog">[] }; 6 | } 7 | 8 | const { Posts } = Astro.props; 9 | --- 10 | 11 |
12 | { 13 | Object.keys(Posts) 14 | .reverse() 15 | .map((year) => ( 16 |
17 |
18 | {year} 19 |
20 |
{}
21 |
22 | )) 23 | } 24 |
25 | -------------------------------------------------------------------------------- /src/content/blog/pip 换源.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pip 换源 3 | tags: 4 | - Python 5 | - Pip 6 | date: 2023-06-22T10:22:35+08:00 7 | draft: false 8 | --- 9 | 10 | ## 永久换源 11 | 12 | ### 阿里云 13 | 14 | ```shell 15 | pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ 16 | ``` 17 | 18 | ```shell 19 | pip config set install.trusted-host mirrors.aliyun.com 20 | ``` 21 | 22 | > 一般可以不设置,在遇到 ssl 报错时,可以将源地址改为 http 并设置 trusted-host 23 | 24 | ### 清华大学 25 | 26 | ```shell 27 | pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 28 | ``` 29 | 30 | ```shell 31 | pip config set install.trusted-host tsinghua.edu.cn 32 | ``` 33 | 34 | ## 临时换源 35 | 36 | ```shell 37 | pip install requetst -i https://mirrors.aliyun.com/pypi/simple/ 38 | ``` 39 | -------------------------------------------------------------------------------- /tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | import defaultTheme from "tailwindcss/defaultTheme"; 2 | import daisyui from "daisyui"; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], 7 | theme: { 8 | extend: { 9 | fontFamily: { 10 | sans: ["Arial", ...defaultTheme.fontFamily.sans], 11 | seri: ["Times New Roman", ...defaultTheme.fontFamily.serif], 12 | din: ["din", ...defaultTheme.fontFamily.sans], 13 | }, 14 | }, 15 | }, 16 | plugins: [daisyui], 17 | // 自定义暗色模式选择器 https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually 18 | darkMode: ["selector", '[data-theme="dark"]'], 19 | daisyui: { 20 | themes: ["light", "dark"], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/PostList.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { CollectionEntry } from "astro:content"; 3 | import dayjs from "dayjs"; 4 | interface Props { 5 | Posts: CollectionEntry<"blog">[]; 6 | } 7 | // 渲染文章目录列表 8 | const { Posts } = Astro.props; 9 | --- 10 | 11 |
12 | { 13 | Posts.map((p) => ( 14 | 24 | )) 25 | } 26 |
27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-astro-blog", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev --host", 7 | "start": "astro dev", 8 | "build": "astro check && astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/check": "^0.8.3", 14 | "@astrojs/mdx": "^3.1.3", 15 | "@astrojs/tailwind": "^5.1.0", 16 | "@astrojs/vue": "^4.5.0", 17 | "astro": "^4.13.3", 18 | "astro-loading-indicator": "^0.3.0", 19 | "clipboard": "^2.0.11", 20 | "dayjs": "^1.11.12", 21 | "ol": "^10.0.0", 22 | "rehype-external-links": "^3.0.0", 23 | "rehype-katex": "^7.0.0", 24 | "remark-math": "^6.0.0", 25 | "vue": "^3.4.37" 26 | }, 27 | "devDependencies": { 28 | "daisyui": "^4.12.10", 29 | "tailwindcss": "^3.4.9", 30 | "typescript": "^5.5.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/icons/date.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { class_ } = Astro.props; 3 | --- 4 | 5 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import dayjs from "dayjs"; 3 | const now = dayjs(); 4 | const formattedDate = now.format("YYYY/MM/DD HH:mm:ss"); 5 | --- 6 | 7 |
8 |
9 |
10 | © 2019-2024 Liuxspro | 11 | CC-BY-NC-4.0 12 |
13 |
14 | Powered by 15 | 16 | {Astro.generator} 17 | 18 | | Source code: 19 | liuxs.pro 25 |
Build At:{formattedDate}
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | import remarkMath from "remark-math"; 3 | import rehypeExternalLinks from "rehype-external-links"; 4 | import rehypeKatex from "rehype-katex"; 5 | import tailwind from "@astrojs/tailwind"; 6 | import mdx from "@astrojs/mdx"; 7 | import vue from "@astrojs/vue"; 8 | const shikiConfig = { 9 | themes: { 10 | light: "vitesse-light", 11 | dark: "solarized-dark" 12 | }, 13 | wrap: true 14 | }; 15 | 16 | 17 | // https://astro.build/config 18 | export default defineConfig({ 19 | integrations: [tailwind(), mdx(), vue()], 20 | markdown: { 21 | remarkPlugins: [remarkMath], 22 | rehypePlugins: [rehypeKatex, [rehypeExternalLinks, { 23 | rel: ["noopener", "noreferrer"], 24 | target: ["_blank"] 25 | }]], 26 | shikiConfig, 27 | // 示例:将脚注文本翻译成另一种语言,这里是默认的英文内容 28 | remarkRehype: { 29 | footnoteLabel: "参考", 30 | footnoteBackLabel: "Back to reference 1" 31 | } 32 | }, 33 | devToolbar: { 34 | enabled: false 35 | } 36 | }); -------------------------------------------------------------------------------- /src/components/CodeCopy.astro: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /src/components/DoubanItem.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import IconDate from "@components/icons/date.astro"; 3 | 4 | interface doubanItem { 5 | title: string; 6 | date: string; 7 | pic: string; 8 | url: string; 9 | } 10 | interface Props { 11 | Items: doubanItem[]; 12 | action: string; 13 | } 14 | 15 | const { Items, action } = Astro.props; 16 | --- 17 | 18 |
19 | { 20 | Items.map((p) => ( 21 |
22 |
23 | 24 |
25 |
26 | 31 |
32 | 33 | 34 | {p.date} {action} 35 | 36 |
37 |
38 |
39 | )) 40 | } 41 |
42 | -------------------------------------------------------------------------------- /src/pages/blog.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/BaseLayout.astro"; 3 | import PostsMenu from "@components/PostsMenu.astro"; 4 | 5 | import { getCollection } from "astro:content"; 6 | import type { CollectionEntry } from "astro:content"; 7 | 8 | const allPosts = await getCollection("blog", ({ data }) => { 9 | return data.draft === false; // 仅返回 frontmatter 中 `draft: false` 的条目 10 | }); 11 | const title = "Blog"; 12 | 13 | allPosts.sort((a, b) => { 14 | const a_date = new Date(a.data.date).valueOf(); 15 | const b_date = new Date(b.data.date).valueOf(); 16 | return b_date - a_date; 17 | }); 18 | 19 | type GroupedPosts = { 20 | [year: number | string]: CollectionEntry<"blog">[]; 21 | }; 22 | 23 | const grouped_by_year: GroupedPosts = {}; 24 | allPosts.forEach((item) => { 25 | const item_date = new Date(item.data.date); 26 | const item_year = item_date.getFullYear(); 27 | if (!grouped_by_year[item_year]) { 28 | grouped_by_year[item_year] = []; 29 | } 30 | grouped_by_year[item_year].push(item); 31 | }); 32 | --- 33 | 34 | 35 |
38 | 归档 39 |
40 | 41 |
42 | -------------------------------------------------------------------------------- /src/components/vue/TileDebug.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 41 | 54 | -------------------------------------------------------------------------------- /src/pages/blog/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from "astro:content"; 3 | import MarkdownLayout from "@layouts/MarkdownLayout.astro"; 4 | import dayjs from "dayjs"; 5 | 6 | export async function getStaticPaths() { 7 | const blogEntries = await getCollection("blog"); 8 | return blogEntries.map((entry) => ({ 9 | params: { slug: entry.slug }, 10 | props: { entry }, 11 | })); 12 | } 13 | 14 | import IconDate from "@components/icons/date.astro"; 15 | const { entry } = Astro.props; 16 | const { Content } = await entry.render(); 17 | --- 18 | 19 | 20 |
21 |
22 |
23 | {entry.data.title} 24 |
25 |
26 |
27 |
28 | 29 | {dayjs(entry.data.date).format("YYYY-MM-DD HH:mm:ss")} 30 |
31 |
32 | {entry.data.tags.map((t) =>
{t}
)} 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | -------------------------------------------------------------------------------- /src/pages/syy.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import MarkdownLayout from "@layouts/MarkdownLayout.astro"; 3 | import DoubanItemList from "@components/DoubanItem.astro"; 4 | const watching_resp = await fetch("https://api.liuxs.pro/douban/watching?userid=53470329"); 5 | const watching = await watching_resp.json(); 6 | 7 | // const status_resp = await fetch(""); 8 | // const status = await status_resp.json(); 9 | 10 | const wish_resp = await fetch("https://api.liuxs.pro/douban/wish?userid=53470329"); 11 | const wish = await wish_resp.json(); 12 | --- 13 | 14 | 15 |

我的书影音

16 |
17 | 18 | 19 | 26 |
27 | 28 |
29 | 30 | 38 |
39 | 40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /src/content/blog/Termux 安装 Mitmproxy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Termux 安装 Mitmproxy" 3 | tags: ["Termux", "Mitmproxy"] 4 | date: 2023-06-22T10:22:35+08:00 5 | draft: false 6 | --- 7 | 8 | > 2023 年 6 月 22 日 9 | > 当前`Python`版本`3.11.4` 10 | > 当前`mitmproxy`版本`9.0.1` 11 | 12 | 使用`pip install mitmproxy`直接安装会卡在 cryptography 很久,把 cryptography 包先拎出来按安装好。 13 | 14 | `cryptography`的安装编译需要有 C 编译器、Rust 编译器等,参考在 Debian/Ubuntu 上安装时需要的软件包[^1]。 15 | 16 | ```bash 17 | sudo apt-get install build-essential libssl-dev libffi-dev python3-dev cargo pkg-config 18 | ``` 19 | 20 | 在`termux`上安装,最简单的方法是`apt install python-cryptography`,官方的软件源中已经内置了这个包。但是官的软件源中的版本是最新版本 (41.0.1) ,而`mitmproxy 9.0.1`版本依赖的是 38.0.4 版本。因此,还是需要编译安装这个包 🤣。 21 | 22 | 安装好 rust 等依赖后,使用这个命令[^2]安装 cryptography 38.0.4 版本: 23 | 24 | ```bash 25 | export RUSTFLAGS=" -C lto=no" && export CARGO_BUILD_TARGET="$(rustc -vV | sed -n 's|host: ||p')" && pip install cryptography==38.0.4 26 | ``` 27 | 28 | 然后安装 mitmproxy 29 | 30 | ```bash 31 | pip install mitmproxy 32 | ``` 33 | 34 | 如果不想编译,可以试试我在 termux 上编译后导出的 whl 包,**依次下载**安装: 35 | 36 | 下载地址: 37 | 38 | ```bash 39 | pip install cryptography-38.0.4-cp311-cp311-linux_aarch64.whl 40 | pip install mitmproxy_wireguard-0.1.23-cp37-abi3-linux_aarch64.whl 41 | pip install ruamel.yaml.clib-0.2.7-cp311-cp311-linux_aarch64.whl 42 | 43 | pip install mitmproxy 44 | ``` 45 | 46 | 参考 47 | 48 | [^1]: [Installation — Cryptography 42.0.0.dev1 documentation](https://cryptography.io/en/latest/installation/) 49 | 50 | [^2]: [Cant install cryptography for python · Issue #9982 · termux/termux-packages (github.com)](https://github.com/termux/termux-packages/issues/9982#issuecomment-1369107679) 51 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | page_title: string; 4 | } 5 | 6 | import Navigation from "@components/Nav.astro"; 7 | import Footer from "@components/Footer.astro"; 8 | import { ViewTransitions } from "astro:transitions"; 9 | import LoadingIndicator from "astro-loading-indicator/component"; 10 | const { page_title } = Astro.props; 11 | --- 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 32 | {page_title} 33 | 34 | 35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /src/content/blog/fx-es模拟器编译及使用.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "fx-ES 模拟器编译及使用" 3 | tags: ["fx-ES","📦Archived"] 4 | date: 2024-08-24T11:01:36+08:00 5 | draft: false 6 | --- 7 | 8 | > Casio fx-ES 系列计算器是卡西欧公司生产的一系列科学计算器,广泛应用于教育和专业领域。该系列以其高性能、易用性和多功能性著称,适合各种数学和科学计算需求。 9 | 10 | ## 源码编译 11 | 12 | 源代码来自: [user202729/fxesplus (github.com)](https://github.com/user202729/fxesplus) 13 | 14 | Clone 源代码 15 | 16 | ```bash 17 | git clone --recurse-submodules https://github.com/user202729/fxesplus --depth=1 18 | ``` 19 | 20 | ```bash 21 | cd CasioEmu/emulator/ 22 | ``` 23 | 24 | 安装依赖 25 | 26 | ```bash 27 | pacman -S mingw-w64-ucrt-x86_64-gcc 28 | pacman -S mingw-w64-ucrt-x86_64-meson 29 | pacman -S mingw-w64-ucrt-x86_64-cmake 30 | pacman -S mingw-w64-ucrt-x86_64-lua53 31 | pacman -S mingw-w64-ucrt-x86_64-SDL2 32 | pacman -S mingw-w64-ucrt-x86_64-SDL2_image 33 | ``` 34 | 35 | 编译 36 | 37 | ```bash 38 | meson build && cd build 39 | meson configure -Dwerror=true -Dwarning_level=3 -Dcpp_std=c++11 40 | ninja 41 | ./emulator ../../models/fx570esplus 42 | ``` 43 | 44 | 提取dll 45 | 46 | ```bash 47 | #!/bin/bash 48 | 49 | # 目标目录 50 | TARGET_DIR=extracted_dlls 51 | # 创建目标目录 52 | mkdir -p $TARGET_DIR 53 | # 获取依赖于ucrtbase.dll的DLL文件列表 54 | DEPENDENT_DLLS=$(ldd emulator.exe | grep "/ucrt64" | awk '{print $3}') 55 | # 复制依赖的DLL文件到目标目录 56 | for DLL in $DEPENDENT_DLLS; do 57 | cp $DLL $TARGET_DIR/ 58 | done 59 | echo "提取的DLL文件存储在$TARGET_DIR目录中。" 60 | ``` 61 | 62 | ## 使用 63 | 64 | 通过指定models目录,运行对应的模拟器 65 | 66 | > `CasioEmu/models` 目录下不含`rom.bin`文件, 需要复制`fxesplus`目录下的rom文件到对应文件夹内。 67 | 68 | ```bash 69 | .\emulator.exe model=.\models\fx991cnx 70 | ``` 71 | 72 | 还可以指定运行时的脚本等属性 73 | 74 | ```bash 75 | .\emulator.exe model=.\models\fx991cnx script=.\lua-emu-init.lua resizable=true width=340 height=716 76 | ``` 77 | 78 | 写一个启动脚本 79 | 80 | ```bat 81 | @echo off 82 | REM 获取当前目录 83 | set CURRENT_DIR=%cd% 84 | 85 | REM 将dll目录添加到PATH环境变量中 86 | set PATH=%CURRENT_DIR%\dlls;%PATH% 87 | 88 | REM 启动emulator.exe 89 | emulator.exe model=.\models\fx991cnx 90 | ``` 91 | -------------------------------------------------------------------------------- /src/content/blog/末地传送门坐标计算.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Minecraft 末地传送门坐标计算(两次末影之眼定位)" 3 | tags: ["Minecraft"] 4 | date: 2022-07-04T21:51:55+08:00 5 | draft: false 6 | --- 7 | 8 | 末影之眼(Eye of Ender)是一种用于定位并激活要塞里面的末地传送门的可合成物品。[^1] 手持末影之眼并按下使用键,末影之眼会飞向最近的要塞,飞过大约 12 个方块的距离,穿过必要的方块,并在其经过的地方留下一条带有紫色粒子的痕迹。根据两次末影之眼的路径,即可推算出末地传送门的位置。在前期末影之眼缺乏的情况下,可以比较高效的寻找到末地传送门。 9 | 10 | 设两次末影之眼指向的坐标分别为 $P_{1}(z_{1},x_{1},\theta_{1})$ 、$P_{2}(z_{2},x_{2},\theta_{2})$ 11 | 12 | 过$P_{1}$且与$z$轴夹角为$\theta_{1}$的直线方程为:$x=tan\theta_{1}z+x_{1}-z_{1}tan\theta_{1}$ 13 | 14 | 过$P_{2}$且与$z$轴夹角为$\theta_{2}$的直线方程为:$x=tan\theta_{2}z+x_{2}-z_{2}tan\theta_{2}$ 15 | 16 | ![我的世界坐标系](https://drive.liuxs.pro/api/raw/?path=/Images/blog/我的世界坐标系.webp "我的世界坐标系") 17 | 两式联立得: 18 | 19 | $$ 20 | z=\frac{z_{1}tan\theta_{1}-z_{2}tan\theta_{2}+x_{2}-x_{1}}{tan\theta_1-tan\theta_2} 21 | $$ 22 | 23 | $x=tan\theta_1+x_1-z_1tan\theta_1$ 或者 $x=tan\theta_2+x_2-z_2tan\theta_2$ 24 | 25 | ![记录坐标](https://drive.liuxs.pro/api/raw/?path=/Images/image-20220703182439270.webp "投掷末影之眼,记录坐标") 26 | 27 | 根据三次记录的坐标及角度,计算得到末地传送门坐标`-1940, -1351` 28 | 29 | ```javascript 30 | p1 = { x: -673.5, z: 29.5, degree: 137.2 }; 31 | p2 = { x: -517.646, z: -195.914, degree: 129.2 }; 32 | p3 = { x: -512.716, z: -342.93, degree: 125.1 }; 33 | 34 | averagePoint(solve([p1, p2, p3])); 35 | // {x: -1940.025735148354, z: -1351.8912451408003} 36 | ``` 37 | 38 | 验证(世界种子为`-2858310909016843760` ),误差并不大。 39 | 40 | ![验证](https://drive.liuxs.pro/api/raw/?path=/Images/image-20220703182923242.webp "验证") 41 | 42 | 使用[最小覆盖圆算法](https://lintx.github.io/minecraft/calc.html)计算出坐标为`-1930 -1340`,差距也不是很大。 43 | 44 | ![使用最小圆覆盖算法计算出的坐标](https://cdn.jsdelivr.net/gh/liuxsdev/bed@main/markdown/image-20220703183223162.png "使用最小圆覆盖算法计算出的坐标") 45 | 46 | {/* 我也写了一个简单的网页计算器:[https://stronghold-clac.vercel.app](https://stronghold-clac.vercel.app) */} 47 | 48 | ### 传送门计算器 49 | 50 | import StrongholdClac from "@components/vue/StrongholdClac.vue" 51 | 52 | 53 | 54 | 其他参考资料: 55 | 56 | [MCBBS - 两次末影之眼定位要塞,附原理&源代码&程序](https://www.mcbbs.net/thread-799313-1-1.html) 57 | 58 | [Python 学习日记 1---简单的 Minecraft 末地要塞坐标计算器](https://blog.csdn.net/RiKler/article/details/104063951) 59 | 60 | [^1]: [末影之眼 - Minecraft Wiki,最详细的我的世界百科](https://minecraft.fandom.com/zh/wiki/末影之眼) 61 | -------------------------------------------------------------------------------- /src/content/blog/树莓派ImmortalWrt旁路由设置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "树莓派 ImmortalWrt 旁路由设置" 3 | tags: ["Openwrt", "Rpi4", "旁路由"] 4 | date: 2023-07-29T23:24:09+08:00 5 | draft: false 6 | --- 7 | 8 | ## 镜像下载 9 | 10 | 之前一直使用`SuLingGG`的 [OpenWrt-Rpi](https://github.com/SuLingGG/OpenWrt-Rpi) 项目的 Openwrt 镜像,但是现在已经停止更新了。于是使用推荐的 [immortalwrt](https://github.com/immortalwrt/immortalwrt)[^1] 项目的官方固件。 11 | 12 | 打开固件选择器 [ImmortalWrt Firmware Selector](https://firmware-selector.immortalwrt.org/)[^2] 选择机型 `Raspberry Pi 4B/400/4CM (64bit)`,下载固件。 13 | 14 | ImmortalWrt 也提供了自定义预安装软件包和首次启动配置脚本. 15 | 16 | ![ImmortalWrt Firmware Selector](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Snipaste_2023-07-30_10-10-06.png) 17 | 18 | 常用的几个软件包: 19 | 20 | - `luci-theme-argon` argon 主题 21 | - `luci-app-argon-config` argon 主题设置 22 | - `openssh-sftp-server` sftp server, 用于 winscp 传文件 23 | - `luci-app-ttyd` `luci-i18n-ttyd-zh-cn` 网页终端及其中文语言包 24 | 25 | 选择下载`💿FACTORY (EXT4)`镜像, 使用 [Raspberry Pi Imager](https://www.raspberrypi.com/software/) 或其他工具如 [balena Etcher](https://etcher.balena.io/) 写入镜像到 SD 卡。 26 | 27 | ## 旁路由设置 28 | 29 | 旁路由设置可参考`SuLingGG`写的 [自编译 OpenWrt 系列 - 旁路由设置指南](https://mlapp.cn/1008.html)[^3],非常详尽。 30 | 由于镜像不同,有些地方的设置有细微差别。关键步骤整理如下。 31 | 32 | - 插入写好镜像的 SD 卡,连接电源,启动树莓派,此时不需要插网线 33 | - 连接树莓派发出的 wifi 信号 🛜`ImmortalWrt`,此时树莓派 的 IP 为 192.168.1.1,访问该地址进入 Openwrt 控制面板(用户名 root,无密码) 34 | - 更改 Lan 口 IP: 可在网页终端(如果装了 luci-app-ttyd 的话)或者 ssh 登录到树莓派执行 35 | 36 | ```bash 37 | uci set network.lan.ipaddr=192.168.31.200 38 | uci commit network 39 | /etc/init.d/network restart 40 | ``` 41 | 42 | IP 更改后,Wifi 连接应该会自动断开,再重新连接 Wifi,这时候要访问 192.168.31.200,也就是上一步设置的 IP 地址 43 | 44 | - 更改 Lan 口参数: 在网络接口中设置网关和 DNS 为上级路由器 IP,并勾选忽略此接口/不在此接口提供 DHCP 服务 45 | - 将树莓派连接到上级路由器上 46 | 47 | 如果无法访问网络,尝试在“网络 - 防火墙 - 自定义规则”中新增一行 iptables 规则并重启防火墙 48 | 49 | ```plaintext 50 | iptables -t nat -I POSTROUTING -j MASQUERADE 51 | ``` 52 | 53 | 如果设备可以正常联网,但opkg update无法更新软件源,尝试在终端运行 54 | 55 | ```bash 56 | opkg update --no-check-certificate 57 | ``` 58 | 59 | [^1]: [immortalwrt/immortalwrt: An opensource OpenWrt variant for mainland China users.](https://github.com/immortalwrt/immortalwrt) 60 | 61 | [^2]: [ImmortalWrt Downloads](https://downloads.immortalwrt.org/) 62 | 63 | [^3]: [自编译 OpenWrt 系列 - 旁路由设置指南](https://mlapp.cn/1008.html) 64 | -------------------------------------------------------------------------------- /src/content/blog/耕地等别计算.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "耕地质量等别评定报告" 3 | tags: ["工作","耕地质量等别评定"] 4 | date: 2024-07-20T09:17:55+08:00 5 | draft: true 6 | --- 7 | 8 | ## 前言 9 | 10 | 前段时间,完成了一份耕地质量等别评定报告,其中涉及一些计算方法和软件的使用,记录一下以供后期查阅。 11 | 12 | 耕地质量等别评定主要是依据[《农用地质量分等规程》(GB/T 28407-2012)](https://nynct.jiangsu.gov.cn/art/2020/5/22/art_13476_9155248.html) 13 | 14 | ## 评价指标 15 | 16 | ### 资料收集 17 | 18 | 1. 某市2019年度耕地质量等别年度更新评价成果; 19 | 2. 项目区2022年度国土变更调查数据成果; 20 | 3. 项目立项材料、项目规划资料、项目竣工图、自验报告等; 21 | 4. 其他相关资料。 22 | 23 | ### 评定底图确定 24 | 25 | 对于新增耕地项目,可以利用竣工图为评定底图来评定耕地图斑的质量等别 26 | 27 | ### 评定单元划分 28 | 29 | ### 确定基本参数和评定因素 30 | 31 | 根据项目区所在县级行政区已有的耕地质量等别评定成果,依次确定本项目新增耕地质量等别评定所需要的参数。 32 | 33 | | 序号 | 参数 | | 34 | | ---- | ------------------------ | ---------------------------------------- | 35 | | 1 | 二级指标区 | 黄淮平原区 | 36 | | 2 | 分等因素指标区 | 徐淮平原区 | 37 | | 3 | 标准耕作制度 | 小麦-水稻、小麦-玉米 | 38 | | 4 | 作物光温(气候)生产潜力 | 一季稻2046,冬小麦1131,夏玉米2258 | 39 | | 5 | 产量比系数 | 水稻1.00,小麦1.30,玉米0.8 | 40 | | 6 | 自然质量参评因素及权重 | 见表1 | 41 | | 7 | 记分规则表 | 参考《耕地质量等级成果补充完善技术报告》 | 42 | 43 | > 注:基本参数需与之前的工作成果保持一致,此处直接引用《A市耕地质量等级成果补充完善技术报告》中的参数。 44 | 45 |

表1 耕地质量等级评定自然质量参评因素及权重

46 | 47 | | 指标区因素 | 水稻权重 | 小麦权重 | 玉米权重 | 48 | | ---------------- | ---- | ---- | ---- | 49 | | 耕作层厚度 | 10.3 | 10.4 | 10.4 | 50 | | 障碍层距地表深度 | 5 | 4.8 | 4.8 | 51 | | 表层土壤质地 | 10.9 | 10.6 | 10.6 | 52 | | 土壤有机质含量 | 17.8 | 17.4 | 17.4 | 53 | | 土壤pH值 | 5.5 | 5.6 | 5.6 | 54 | | 灌溉保证率 | 22.9 | 19.8 | 19.8 | 55 | | 排水条件 | 10.4 | 12.6 | 12.6 | 56 | | 土壤盐渍化程度 | 11.8 | 12.7 | 12.7 | 57 | | 土壤侵蚀程度 | 5.4 | 6.1 | 6.1 | 58 | 59 | ### 外业调查 60 | 61 | 调查内容主要包括由农田基础条件变化引起的因素(如排水条件、灌溉保证率、有效土层厚度等)和新增耕地力因素值(如土壤有机质、pH值等)。同时根据《农用地质量分等规程》要求,评定对象是针对长期耕种、肥力相对稳定的耕地,新增耕地土壤需通过培肥措施才能达到正常种植的条件,新增耕地地力因素值部分直接引用了原地块或邻近地块的数值 62 | 63 | | 评价单元编号 | 样点面积(公顷) | 表层土壤质地 | 耕作层厚度(cm) | 障碍层距地表层深度(cm) | 灌溉保证率(%) | 排水条件 | 土壤pH值 | 土壤有机质含量(‰) | 64 | | ------------ | ---------------- | ------------ | ---------------- | ------------------------ | --------------- | -------- | -------- | ------------------- | 65 | | FXXCTXX-01 | 0.0935 | 砂壤 | 13 | 58 | 80 | 良 | 7.28 | 8.5 | 66 | -------------------------------------------------------------------------------- /src/components/DarkThemeSwitch.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import IconSun from "@components/icons/sun.astro"; 3 | import IconMoon from "@components/icons/moon.astro"; 4 | --- 5 | 6 |
7 | 12 |
13 | 14 | 66 | -------------------------------------------------------------------------------- /src/content/blog/自定义谷歌XYZ瓦片地图样式.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 自定义谷歌 XYZ 瓦片地图样式 3 | tags: 4 | - GIS 5 | - MDX 6 | - Google Map 7 | date: 2024-08-07T15:52:35+08:00 8 | draft: false 9 | --- 10 | 11 | ### 常用的谷歌瓦片地址 12 | 13 | 网上流传的谷歌地图瓦片地址一般为:`https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}` 14 | 15 | 其中`lyrs`为地图类型, 主要有以下几种: 16 | 17 | ```text 18 | https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z} // 纯卫星影像 19 | https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z} // 纯卫星影像(带路网和助记) 20 | https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z} // 街道 21 | https://mt1.google.com/vt/lyrs=t&x={x}&y={y}&z={z} // 地形晕染(灰度图) 22 | https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z} // 地形晕染(带路网和助记) 23 | https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z} // 路网和助记 24 | ``` 25 | 26 | 众所周知, 谷歌地图的路网和助记使用的是火星坐标系(GCJ-02),会有一定的偏移, 而且会有行政区划标记错误的问题, 所以我一般只使用不含路网和助记的纯影像图。 27 | 28 | 纯卫星影像图很容易获得, 地形晕染, 因为它默认是带路网和助记的, 我一直以为无法获得纯地形晕染图(谷歌的地形晕染还是很漂亮的). 29 | 30 | 直到某天,群友发了类似这样一串链接: 31 | ```text 32 | http://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}&apistyle=s.e:l|p.v:off%2Cs.t:1|s.e.g|p.v:off%2Cs.t:3|s.e.g|p.v:off%2Cs.t:2|s.e.g|p.v:off%2Cs.t:4|s.e.g|p.v:off 33 | ``` 34 | 35 | 通过它,就能得到谷歌纯地形晕染图了。 36 | 37 | 怎么实现的呢? 38 | 39 | 原来谷歌地图的 XYZ 瓦片是可以通过 apistyle 自定义样式的。 40 | 41 | 似乎官方也没有文档说明,在 stackoverflow 的这篇帖子[^1]中,有较为详细的介绍。 42 | 43 | 比如这个代码 44 | ```text 45 | s.t:3|s.e:l|p.v:off 46 | ``` 47 | 48 | `s.t:3` 要素类型t, 3 表示 `road` 路网 49 | `s.e:l` 元素类型e, l 表示 `lables` 标签 50 | `p.v:off` 定义样式 , v 表示 `visibility` 可见性 51 | 52 | 连起来的意思就是 路网、路网标签不显示 53 | 54 | ### 通过在线工具自定义样式 55 | 56 | 通过 apistyle 可以控制元素、标签是否显示,还可以自定义颜色。 57 | 58 | 谷歌有一个定制地图样式的网站: https://mapstyle.withgoogle.com/ ,可以可视化的自定义地图样式 59 | 60 | 定义好样式后, 会得到一个样式json文件, 如: 61 | 62 | ```json 63 | [ 64 | { 65 | "featureType": "road", 66 | "stylers": [ 67 | { 68 | "visibility": "off" 69 | } 70 | ] 71 | } 72 | ] 73 | ``` 74 | 75 | 再通过这个工具 GMaps API Style Encoder[^2] ,可以将样式 style 转换为 apistyle 代码, 应用到 XYZ 瓦片中。 76 | 77 | 78 | ### 简易自定义样式工具 79 | 80 | 简单的控制标签、行政区划、POI、水系等是否显示。 81 | 82 | ```javascript 83 | label: "s.e:l|p.v:off", // 隐藏标签 84 | administrative: "s.t:1|s.e.g|p.v:off", // 隐藏行政区划 85 | road: "s.t:3|s.e.g|p.v:off", // 隐藏路网 86 | poi: "s.t:2|s.e.g|p.v:off", // 隐藏POI 87 | water: "s.t:6|s.e.l|p.v:off", // 隐藏水系 88 | ``` 89 | 90 | import Map from "@components/vue/gmapstyle.vue" 91 | 92 | 93 | 94 | 95 | [^1]: [stackoverflow.com: customizing-google-map-tile-server-url](https://stackoverflow.com/questions/29692737/customizing-google-map-tile-server-url) 96 | 97 | [^2]: [julienben/gmaps-apistyle-encoder](https://github.com/julienben/gmaps-apistyle-encoder) 98 | 99 | -------------------------------------------------------------------------------- /src/content/blog/Termux 安装 Matplotlib.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Termux 安装 Matplotlib" 3 | tags: ["Termux", "Matplotlib", "Python"] 4 | date: 2023-07-30T08:23:36+08:00 5 | draft: false 6 | --- 7 | 8 | > 2023 年 7 月 30 日 9 | > 当前`Python`版本`3.11.4` 10 | > Matplotlib 版本: `3.7.2` 11 | 12 | 两种安装方式,一种使用 pip 安装,另一种直接使用 apt 安装(termux 源里有 Matplotlib),但这两种方式,目前都有一些问题。 13 | 14 | ## 1 使用 pip 安装 ❌ 15 | 16 | ```bash 17 | pip install matplotlib 18 | ``` 19 | 20 | ### 安装 numpy 21 | 22 | Termux 官方软件源中有 Numpy 最新版本 1.25.1, 但是 Matplotlib 使用的仍是老版本 numpy==1.23.2。 23 | 24 | 自 3.7.0 版本开始 Matplotlib 开始使用`oldest-supported-numpy`来构建[^1],`oldest-supported-numpy`在给定的 Python 版本和平台上提供最旧的 NumPy 版本的作为构建时依赖项。 25 | 26 | 在 Python 3.11 下,numpy 版本被设为 1.23.2[^2] 27 | 28 | 也就是说,即使安装好了 Numpy,Matplotlib 也会去安装该平台上所支持的 Numpy 的最旧版本。 29 | 30 | 所以,还是避免不了去安装编译 numpy。 31 | 32 | > [its-pointless](https://github.com/its-pointless/its-pointless.github.io/tree/master/files/24/dists/termux/extras/binary-aarch64) 仓库也不维护了,里面的 numpy 版本为 1.20.3 33 | 34 | Numpy 的安装这里不论 35 | 36 | 假如成功解决了 Numpy 的依赖问题,后面还有个麻烦的包要编译,在下一节详细讲。 37 | 38 | 所以使用 pip 安装 Matplotlib 暂时失败 39 | 40 | ## 2 使用 apt 安装 ✔️ 41 | 42 | Termux 官方软件源中是有 Matplotlib 的,输入 43 | 44 | ```bash 45 | ❌ 先别使用这个命令安装 46 | apt install matplotlib 47 | ``` 48 | 49 | 在安装的最后阶段,会去安装一些 pip 包,其中`contourpy`安装报错 50 | 51 | ```bash 52 | pip setup... 53 | Writing to /data/data/com.termux/files/usr/etc/pip.conf 54 | Setting up python-pillow (10.0.0) ... 55 | Setting up matplotlib (3.7.2) ... 56 | Installing dependencies through pip. This may take a while... 57 | Collecting contourpy>=1.0.1 58 | ``` 59 | 60 | `contourpy` 1.1.0 版本需要`ninja`来构建,安装过程中会报一堆错误 61 | 62 | 详见 github 上关于这个问题的 issue: [pip install contourpy -U (and pip install ninja -U) fail · Issue #17474](https://github.com/termux/termux-packages/issues/17474) 63 | 64 | Matplotlib 需求的 contourpy 最低为 1.0.1[^3],而 1.0.1~1.0.7 版本都是可以正常使用 pip 安装的,安装完 contourpy 就可以顺利的安装 Matplotlib 了 65 | 66 | ```bash 67 | pip install contourpy==1.0.7 68 | ``` 69 | 70 | 另外,还有一个 pillow 库也需要提前通过 apt 安装 71 | 72 | 总结一下: 73 | 74 | ```bash 75 | 按照顺序安装 76 | apt install python-numpy 77 | apt install python-pillow 78 | pip install contourpy==1.0.7 79 | apt install matplotlib 80 | ``` 81 | 82 | [^1]: [github_stats_3.7.0.rst](https://github.com/matplotlib/matplotlib/blob/8c58e422129b00cf203eeb4d9a1c7bd2327a41fc/doc/users/prev_whats_new/github_stats_3.7.0.rst?plain=1#L472) 83 | 84 | [^2]: [oldest-supported-numpy/setup.cfg](https://github.com/scipy/oldest-supported-numpy/blob/66810804478e971dd08c8ea3936bca2757a1fbcb/setup.cfg#L65C4-L65C87) 85 | 86 | [^3]: [matplotlib/setup.py](https://github.com/matplotlib/matplotlib/blob/c36a03fbe6e64d8540891d705a74c328015b567e/setup.py#L331) 87 | -------------------------------------------------------------------------------- /src/content/blog/瓦片计算.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 瓦片地图及其计算 3 | tags: 4 | - GIS 5 | - XYZ 6 | - MDX 7 | date: 2024-09-15T18:10:03+08:00 8 | draft: false 9 | --- 10 | 11 | ## 什么是瓦片地图 12 | 13 | 瓦片地图(Tiled web map[^1])一种通过分块技术将大范围地图按固定大小的图像或数据块(称为“瓦片”)分割,方便地图在网络或其他应用中快速加载和显示的技术。 14 | 15 | 标准的切片 Web 地图的属性主要包括: 16 | 17 | - 图块一般为 256x256 像素 18 | - 最小层级 0 情况下,可以在单个地图图块中渲染整个世界 19 | - 每个缩放级别在 XY 两个维度上都加倍,因此在放大时,单个图块将替换为 4 个图块 20 | - 使用 Web Mercator 投影(EPSG:3857),纬度限制约为85° 21 | 22 | ![XYZ_Tile](https://drive.liuxs.pro/api/raw/?path=/Images/blog/XYZ_Tiles.png) 23 | > 图片来自: https://en.wikipedia.org/wiki/File:XYZ_Tiles.png 24 | 25 | 26 | ### 切片方法 27 | 28 | #### 1.Google Maps / OpenStreetMap 29 | 30 | 大多数平铺 Web 地图都遵循某些 Google 地图惯例,事实上的 OpenStreetMap 标准,称为 Slippy Map Tilenames 或 XYZ. 31 | 32 | 原点坐标在**左上角** 33 | 34 | #### 2.Tile Map Service(TMS) 35 | 36 | OpenLayers 的早期标准,与XYZ的区别是 y 轴在 TMS 中向北为正, 即原点在**左下角**. 37 | 38 | 可通过以下公式计算得到 XYZ 的 Y 值 39 | 40 | $$ 41 | Y = 2^z - 1 - Y_{tms} 42 | $$ 43 | 44 | js代码如下: 45 | ```javascript 46 | // 在编程语言中,左移运算较快 47 | // y = Math.pow(2, z) - y - 1; 48 | y = (1 << z) - y - 1; 49 | ``` 50 | 51 | > 参考阅读: [The difference between XYZ and TMS tiles and how to convert between them (github.com)](https://gist.github.com/tmcw/4954720) 52 | 53 | #### 3.QuadTrees 54 | 55 | Bing Map 使用 Quadkeys 进行寻址[^2]。 56 | 57 | ### 缩放级别 58 | 59 | 缩放级别(zoom)一般为 0~18,zoom level 为 0 时,需要 1 张瓦片,zoom level 为 n 时,需要 $2^2n$ 张瓦片 60 | 61 | > 一般地图服务商提供地图的最大分辨率为亚米级,18 级时影像分辨率为 0.5972m,所以 zoom 一般为18. 62 | 63 | Openlayer 提供了调试瓦片,可以显示每个瓦片的坐标[^3]: 64 | 65 | import Map from "@components/vue/TileDebug.vue"; 66 | 67 | 68 | 69 | ## 瓦片编号的计算 70 | 71 | 可根据以下公式计算[^4]: 72 | 73 | $ x=\left \lfloor \frac{lon+180}{360} \cdot 2^{z} \right \rfloor $ 74 | $ y=\left \lfloor (1-\frac{ \ln (\tan (lat\cdot \frac{\pi }{180} )+\frac{1}{\cos(lat\cdot \frac{\pi}{180} )} )}{\pi}) \cdot 2^{z-1} \right \rfloor $ 75 | $ lon=\frac{x}{2^z} \cdot 360-180 $ 76 | $ lat=\arctan (\sinh(\pi-\frac{y}{2^z} ))\cdot \frac{180}{\pi} $ 77 | 78 | import TileCalc from "@components/vue/TileCalc.vue"; 79 | 80 | 81 | 82 | ## 相关参考资料 83 | 84 | [OpenLayers 3 Primer - 瓦片计算](http://primer.openlayers.cn/ol3-primer/ch06/06-02.html) 85 | **flutter_map**的 Demo: [Tile Builder](https://demo.fleaflet.dev/tile_builder) 86 | 87 | 88 | [^1]: [Tiled web map - Wikipedia](https://en.wikipedia.org/wiki/Tiled_web_map) 89 | [^2]: [Bing Maps Tile System](https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system) 90 | [^3]: [ol/source/TileDebug - OpenLayers API](https://openlayers.org/en/latest/apidoc/module-ol_source_TileDebug-TileDebug.html) 91 | [^4]: [Slippy map tilenames](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) 92 | 93 | -------------------------------------------------------------------------------- /src/components/vue/TileCalc.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 82 | -------------------------------------------------------------------------------- /src/content/blog/Markdown基本语法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Markdown 基本语法" 3 | date: 2019-11-27 4 | draft: false 5 | --- 6 | 7 | ## 1 段落 8 | 9 | 永和[^1]九年,岁在癸丑,暮春之初,会于会稽山[^2]阴之兰亭,修禊事也。群贤毕至,少长咸集。此地有崇山峻岭,茂林修竹,又有清流激湍,映带左右,引以为流觞曲水,列坐其次。虽无丝竹管弦之盛,一觞一咏,亦足以畅叙幽情。 10 | 11 | 是日也,天朗气清,惠风和畅。仰观宇宙之大,俯察品类之盛,所以游目骋怀,足以极视听之娱,信可乐也。 12 | 13 | 夫人之相与,俯仰一世。或取诸怀抱,悟言一室之内;或因寄所托,放浪形骸之外。虽趣舍万殊,静躁不同,当其欣于所遇,暂得于己,快然自足,不知老之将至;及其所之既倦,情随事迁,感慨系之矣。向之所欣,俯仰之间,已为陈迹,犹不能不以之兴怀,况修短随化,终期于尽!古人云:“死生亦大矣。”岂不痛哉! 14 | 15 | 每览昔人兴感之由,若合一契,未尝不临文嗟悼,不能喻之于怀。固知一死生为虚诞,齐彭殇为妄作。后之视今,亦犹今之视昔,悲夫!故列叙时人,录其所述,虽世殊事异,所以兴怀,其致一也。后之览者,亦将有感于斯文。 16 | 17 | ## 2 行内样式 18 | 19 | **强调** | _斜体_ | **_强调和斜体_** | `行间代码code` | 下划线 | ~~删除线~~ 20 | 21 | ## 3 引用 22 | 23 | > 《兰亭集序》,又题为《临河序》、《禊帖》、《三月三日兰亭诗序》等。晋穆帝永和九年(公元 353)三月三日,时任会稽内史的王羲之与友人谢安、孙绰等四十一人会聚兰亭,赋诗饮酒。王羲之将诸人名爵及所赋诗作编成一集,并作序一篇,记述流觞曲水一事,并抒写由此而引发的内心感慨。这篇序文就是《兰亭集序》。此序受石崇《金谷诗序》影响很大,其成就又远在《金谷诗序》之上。 24 | 25 | ### 嵌套引用 26 | 27 | > 文章首先记述了集会的时间、地点及与会人物,言简意赅。接着描绘兰亭所处的自然环境和周围景物,语言简洁而层次井然。描写景物,从大处落笔,由远及近,转而由近及远,推向无限。先写崇山峻岭,渐写清流激湍,再顺流而下转写人物活动及其情态,动静结合。然后再补写自然物色,由晴朗的碧空和轻扬的春风,自然地推向寥廓的宇宙及大千世界中的万物。意境清丽淡雅,情调欢快畅达。兰亭宴集,真可谓“四美俱,二难并”。 28 | > 29 | > > 第一至第二自然段,记叙了集会的时间、地点、事由、人物,由“此地有崇山峻岭”引出四周环境及场面的铺叙,最后由“是日也”领起描写游人的心境,抒发集会的心情。本文第一、二自然段作者对这次宴集环境的描述素淡雅致,摄其神韵,天朗气清,惠风和畅,这些都看出作者快乐的心情和对自然美的热爱之情。 30 | > > 用于在文档中引用其他来源的内容块. 31 | 32 | ## 4 列表 33 | 34 | ### 无序列表 35 | 36 | - Lorem ipsum dolor sit amet 37 | - Consectetur adipiscing elit 38 | - Integer molestie lorem at massa 39 | 40 | ### 有序列表 41 | 42 | 1. Lorem ipsum dolor sit amet 43 | 2. Consectetur adipiscing elit 44 | 3. Integer molestie lorem at massa 45 | 46 | ### 任务列表 47 | 48 | - [x] Write the press release 49 | - [ ] Update the website 50 | - [ ] Contact the media 51 | 52 | ## 5 代码 53 | 54 | ### 缩进代码 55 | 56 | line 1 of code 57 | line 2 of code 58 | line 3 of code 59 | 60 | ### 代码块 61 | 62 | ```javascript 63 | /** @type {import('tailwindcss').Config} */ 64 | const defaultTheme = require("tailwindcss/defaultTheme"); 65 | module.exports = { 66 | content: ["./templates/**/*.html"], 67 | theme: { 68 | fontFamily: { 69 | ubuntu: ["ubuntu", ...defaultTheme.fontFamily.sans], 70 | din: ["din", ...defaultTheme.fontFamily.sans], 71 | serif: [...defaultTheme.fontFamily.serif], 72 | sans: ["din", defaultTheme.fontFamily.sans], 73 | mono: ["CascadiaMono", ...defaultTheme.fontFamily.mono], 74 | }, 75 | extend: {}, 76 | }, 77 | plugins: [require("@tailwindcss/typography")], 78 | darkMode: "class", 79 | }; 80 | ``` 81 | 82 | ## 6 表格 83 | 84 | | Left-Aligned | Center Aligned | Right Aligned | 85 | | :------------ | :-------------: | ------------: | 86 | | col 3 is | some wordy text | $1600 | 87 | | col 2 is | centered | $12 | 88 | | zebra stripes | are neat | $1 | 89 | 90 | ## 7 图片 91 | 92 | ![img](https://picsum.photos/600/400/?random) 93 | 94 | ## 8 公式 95 | 96 | $P_{1}(z_{1},x_{1},\theta_{1})$ 97 | 98 | $$ 99 | e^{i \pi}+1=0 100 | $$ 101 | 102 | ## Reference 103 | 104 | [^1]: 永和:东晋皇帝司马聃(晋穆帝)的年号,从公元 345—356 年共 12 年。永和九年上巳节,王羲之与谢安,孙绰等 41 人。举行禊礼,饮酒赋诗,事后将作品结为一集,由王羲之写了这篇序总述其事。 105 | 106 | [^2]: 郡名,今浙江绍兴 107 | -------------------------------------------------------------------------------- /src/components/vue/StrongholdClac.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 140 | 141 | 144 | -------------------------------------------------------------------------------- /src/styles/markdown.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E"); 7 | --icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E"); 8 | } 9 | 10 | /* 字体颜色 */ 11 | @layer components { 12 | /* 正文字体颜色 */ 13 | .primary-text-color { 14 | @apply text-gray-800 dark:text-gray-300; 15 | } 16 | .info-text-color { 17 | @apply text-gray-700 dark:text-gray-400; 18 | } 19 | .title-text-color { 20 | @apply text-gray-800 dark:text-blue-400; 21 | } 22 | } 23 | /* 正文字体 */ 24 | p { 25 | @apply text-base/8 my-2 font-medium; 26 | } 27 | 28 | /* 设置颜色 */ 29 | h2, 30 | h3, 31 | li, 32 | p { 33 | @apply primary-text-color; 34 | } 35 | 36 | /* 标题 */ 37 | h1 { 38 | @apply text-2xl font-semibold; /* 字体设置 */ 39 | @apply my-6; 40 | } 41 | h2 { 42 | @apply my-4 text-lg font-bold leading-7; 43 | @apply mt-6 mb-6 pt-8 text-2xl border-t border-gray-300 dark:border-gray-500; 44 | } 45 | 46 | h3 { 47 | @apply my-4 font-bold leading-5; 48 | @apply border-l-4 px-2 border-orange-700 dark:border-orange-400; 49 | } 50 | 51 | article a { 52 | @apply text-blue-500 font-semibold hover:underline; 53 | } 54 | 55 | /* 代码框 */ 56 | pre { 57 | @apply p-4 rounded-md overflow-auto text-sm dark:border-blue-800; 58 | } 59 | 60 | pre.astro-code { 61 | @apply my-2 relative border dark:border-zinc-700; 62 | } 63 | 64 | /* 复制按钮 */ 65 | button.code_copy { 66 | @apply absolute top-1 right-1 p-4 w-6 h-6; 67 | background-image: var(--icon-copy); 68 | background-size: 1.5em; 69 | background-repeat: no-repeat; 70 | background-position: 50%; 71 | opacity: 0; 72 | transition: opacity 1s; 73 | } 74 | 75 | pre.astro-code:hover > button.code_copy { 76 | transition: opacity 0.25s; 77 | opacity: 1; 78 | } 79 | button.copied { 80 | background-image: var(--icon-copied); 81 | } 82 | button.copied::before { 83 | content: "Copied"; 84 | height: 20px; 85 | position: absolute; 86 | left: -4em; 87 | top: 0.5em; 88 | } 89 | 90 | /* 引用 */ 91 | blockquote { 92 | @apply dark:border-gray-500 text-base border-l-4 px-3; 93 | } 94 | 95 | blockquote p { 96 | @apply text-gray-500 text-sm font-medium mb-4; 97 | @apply dark:text-gray-400; 98 | } 99 | 100 | /* blockquote 里面的 `行内代码` */ 101 | blockquote p code { 102 | @apply px-2 py-px; 103 | } 104 | 105 | /* code `行内代码` */ 106 | p code, 107 | li code { 108 | /* white-space: nowrap; */ 109 | /* 110 | box-decoration-break 属性用来定义当元素跨多行、多列或多页时,元素的片段应如何呈现 111 | https://developer.mozilla.org/zh-CN/docs/Web/CSS/box-decoration-break 112 | */ 113 | -webkit-box-decoration-break: clone; 114 | box-decoration-break: clone; 115 | @apply px-1.5 py-1 rounded-md bg-gray-900/10 mx-2 text-orange-700 text-sm; 116 | @apply dark:bg-gray-100/20 dark:text-orange-300; 117 | } 118 | 119 | /* 脚注 */ 120 | 121 | sup { 122 | top: -0.5em; 123 | padding-left: 0.4em; 124 | } 125 | 126 | sup a { 127 | @apply after:content-[']'] before:content-['[']; 128 | @apply mr-1 decoration-sky-600 text-sky-600; 129 | @apply dark:text-sky-300 dark:decoration-sky-300; 130 | @apply font-din; 131 | } 132 | 133 | /* 无序列表 */ 134 | 135 | article ul { 136 | @apply list-disc list-inside; 137 | } 138 | 139 | article ul li { 140 | @apply py-[2px]; 141 | } 142 | 143 | /* 有序列表 */ 144 | 145 | ol { 146 | @apply list-decimal pl-6; 147 | } 148 | 149 | /* 脚注设置 */ 150 | section.footnotes p { 151 | @apply text-sm; 152 | } 153 | section.footnotes ol { 154 | @apply text-sm font-din; 155 | } 156 | 157 | table { 158 | @apply w-full; 159 | } 160 | 161 | th, 162 | td { 163 | @apply border p-1 text-center text-sm; 164 | } 165 | 166 | [data-theme="dark"] pre.astro-code-themes { 167 | color: var(--shiki-dark) !important; 168 | background-color: var(--shiki-dark-bg) !important; 169 | /* 可选,用于定义字体样式 */ 170 | font-style: var(--shiki-dark-font-style) !important; 171 | font-weight: var(--shiki-dark-font-weight) !important; 172 | text-decoration: var(--shiki-dark-text-decoration) !important; 173 | } 174 | -------------------------------------------------------------------------------- /src/components/vue/gmapstyle.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 147 | 148 | 161 | -------------------------------------------------------------------------------- /src/content/blog/在Windows上编译Mdbtools.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 在 Windows 上编译 Mdbtools 3 | tags: [] 4 | date: 2024-04-16T10:15:09+08:00 5 | draft: true 6 | --- 7 | 8 | ## 前言 9 | 10 | MDB Tools[^1]是一套开源的程序和库,它允许用户在 Unix-like 系统上读取、导出和操作 Microsoft Access 数据库文件(.mdb 或.accdb)。 11 | 12 | MDB Tools 提供了一系列命令行工具,每个工具都有特定的用途,例如: 13 | 14 | - `mdb-ver`: 显示数据库文件的版本信息。 15 | - `mdb-schema`: 输出指定表的 DDL(数据定义语言)语句。 16 | - `mdb-export`: 将数据表导出为 CSV 或 SQL 格式。 17 | - `mdb-json`: 将数据表导出为 JSON 格式。 18 | - `mdb-tables`: 列出数据库中的所有表名。 19 | - `mdb-count`: 计算并显示表中的行数。 20 | - `mdb-sql`: 提供一个简单的 SQL 命令行接口。 21 | - `mdb-queries`: 列出数据库中存储的所有查询。 22 | 23 | 在 Linux 系统上安装 MDB Tools 很简单,各系统的软件包内基本都包含此软件,例如在 Debian 中直接使用`apt install mdbtools`即可安装。 24 | 25 | 但是官方并没有给出 Windows 版,因此需要自行编译,比较出名的一个编译版本是 [lsgunth/mdbtools-win](https://github.com/lsgunth/mdbtools-win)[^2],这也是 QGIS SLYR[^3] 插件推荐安装的 Windows 版 MDB Tools。 26 | 27 | > SLYR 提供了完整的 ArcMap/ArcGIS Pro 到 QGIS 的兼容性套件。这个套件旨在帮助用户将 ESRI 的地理信息系统产品中的项目和文档转换为 QGIS 可以识别和处理的格式,从而实现在这两个流行的 GIS 平台之间的无缝转换和互操作性。 28 | > SLYR 使用 MDB Tools 来读取 ArcMap Style 样式等文件(style 文件是以 Access 数据库格式保存的) 29 | 30 | lsgunth 版的 Mdb Tools 目前在 Windows 上和 SLYR 插件不能正常交互,可能是由于一些奇怪的编码问题 31 | 32 | ## 编译 Mdb Tools 33 | 34 | 首先安装 MSYS2,使用官方推荐的 UCRT 环境 35 | 36 | 安装 gcc 等编译工具 37 | 38 | ```bash 39 | pacman -Sy autoconf automake libtool base-devel mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-glib2 40 | ``` 41 | 42 | 接下来生成`configure`脚本和`Makefile`文件 43 | 44 | ```bash 45 | autoreconf -i -f 46 | ``` 47 | 48 | 运行`configure`脚本 49 | 50 | ```bash 51 | ./configure 52 | ``` 53 | 54 | 最后 make 编译 55 | 56 | ```bash 57 | make 58 | ``` 59 | 60 | > 可能会有报错,见下一节 61 | 62 | 生成的 exe 文件路径为`./src/utils/*.exe`,独立运行的时候,需要复制依赖的 dll 文件到 exe 文件的同目录下 63 | 根据,需要的 dll 文件包括: 64 | 65 | ```bash 66 | cp -p /mdbtools/src/util/.libs/*.exe /mdbtools-win/ 67 | cp -p /mdbtools/src/sql/.libs/*.dll /mdbtools-win/ 68 | cp -p /mdbtools/src/libmdb/.libs/*.dll /mdbtools-win/ 69 | cp -p /mingw32/bin/libgcc*.dll /mdbtools-win/ 70 | cp -p /mingw32/bin/libiconv*.dll /mdbtools-win/ 71 | cp -p /mingw32/bin/libreadline*.dll /mdbtools-win/ 72 | cp -p /mingw32/bin/libtermcap*.dll /mdbtools-win/ 73 | cp -p /mingw32/bin/libwinpthread*.dll /mdbtools-win/ 74 | ``` 75 | 76 | ## 一些修改 77 | 78 | ### 修改编译配置 79 | 80 | 使用最新的源码在 UCRT 环境下编译报错,可能是跟这次提交有关[Fix windows GH action (#399)](https://github.com/mdbtools/mdbtools/commit/0b96ecaff1c543feb39b7e855fa61e6651a01203),官方的 Windows Build Action 能正常运行(但是 MSYS 环境,exe 文件似乎不能拿出来独立使用),因此进行了一些修改进行编译,还原了原来的设置。 81 | 82 | ```diff 83 | diff --git a/configure.ac b/configure.ac 84 | index 1383545..cb52cca 100644 85 | --- a/configure.ac 86 | +++ b/configure.ac 87 | @@ -86,7 +86,7 @@ AC_SUBST(LFLAGS) 88 | CFLAGS="$CFLAGS -Wall -Werror" 89 | LOCALE_T=locale_t 90 | AS_CASE([$host], 91 | - [*mingw*], [LDFLAGS="$LDFLAGS -no-undefined" CFLAGS="$CFLAGS -D_spawnv=spawnv"], []) 92 | + [*mingw*], [LDFLAGS="$LDFLAGS -no-undefined" LOCALE_T=_locale_t], []) 93 | AC_SUBST(LOCALE_T) 94 | 95 | dnl See if iconv is present and wanted 96 | ``` 97 | 98 | ### 修改提示符 99 | 100 | Mdb Tools 使用了一个特殊的字符`…`[^4],在 Windows 上会显示乱码(也许仅仅是中文系统乱码??),导致 SLYR 插件无法正常与 Mdb Tools 交互 101 | 102 | ![显示乱码](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Pasted%20image%2020240416114428.png) 103 | 104 | 修改`src/libmdb/fakeglib.c`,但是编译不起作用,需要禁用`glib2`才能生效 105 | 106 | ```diff 107 | - "Usage:\n %s [OPTION\xE2\x80\xA6] %s\n\n", appname, context->desc); 108 | + "Usage:\n %s [OPTION...] %s\n\n", appname, context->desc); 109 | ``` 110 | 111 | ```bash 112 | ./configure --disable-glib 113 | ``` 114 | 115 | 禁用 glib 的情况下,只需要以下几个 dll 116 | 117 | - `libmdb-3.dll` 118 | - `libmdbsql-3.dll` 119 | - `libgcc_s_seh-1.dll` 120 | - `libwinpthread-1.dll` 121 | 122 | > 可以使用 `ldd mdb-export.exe` 、`ldd libmdb-3.dll`查看程序依赖的 dll 123 | 124 | 这个符号似乎是由 Glib2 GOptionEntry 标准命令行引起的,有空再研究 125 | 126 | 相关资料: 127 | 128 | - [GLib – 2.0: Commandline Option Parser (gtk.org)](https://docs.gtk.org/glib/goption.html) 129 | - [GNOME / GLib · GitLab](https://gitlab.gnome.org/GNOME/glib) 130 | 131 | ## 编译结果 132 | 133 | 由于 MSYS2 系统是滚动发布的,软件包更新后可能会有新的问题导致编译失败,因此编译了一份最新(mdtools commit hash: `7e057e3`)修改的 MDB Tools: [点击下载](https://github.com/liuxsdev/mdbtools-win-build-action/releases/download/v1.0.0/mdbtools-build-win-ucrt64.zip) 134 | 135 | 修改后: 136 | ![显示正常](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Pasted%20image%2020240416114459.png) 137 | 138 | 使用 SLYR 插件转换 Arcgis Style 文件无中文乱码: 139 | 140 | ![SLYR 插件转换正常](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Pasted%20image%2020240416114723.png) 141 | 142 | [^1]: [mdbtools/mdbtools: MDB Tools - Read Access databases on \*nix (github.com)](https://github.com/mdbtools/mdbtools) 143 | [^2]: [lsgunth/mdbtools-win: MDBTools built for 32bit windows using MSYS2 (github.com)](https://github.com/lsgunth/mdbtools-win) 144 | [^3]: [SLYR: the ESRI to QGIS Compatibility Suite – North Road (north-road.com)](https://north-road.com/slyr/) 145 | [^4]: [Horizontal Ellipsis 水平省略号](https://unicode-table.com/cn/2026/) 146 | -------------------------------------------------------------------------------- /src/content/blog/Python绘制风玫瑰图.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Python 绘制风玫瑰图 3 | tags: ["Python", "风玫瑰图"] 4 | date: 2023-05-14T21:48:00+08:00 5 | --- 6 | 7 | ## 1. 前言 8 | 9 | 风玫瑰图(Wind Rose Plot)用来简单描述某一地区风向风速的分布,可分为**风向玫瑰图**和**风速玫瑰图**。在风向玫瑰图的极坐标系上,每一部分的长度表示该风向出现的频率,最长的部分表示该风向出现的频率最高。风玫瑰图通常分 16 个方向,也有的再细分为 32 个方向。[^1] 10 | 11 | 玫瑰图上所表示的风的吹向,是指从外部吹向地区中心的方向。一般来说,在风玫瑰图中,用粗实线表示全年风向频率,细实线表示按 12、1、2 月三个月统计的冬季风向频率,虚线表示按 6、7、8 月三个月统计的夏季风向频率。 12 | 13 | ## 2. 数据准备 14 | 15 | 绘制风玫瑰图需要某地区多年风向统计数据,我们根据中国地面气候资料日值数据集(V3.0)中某地区的风向风速数据。经过统计,得到该地区 1994 年 8 月~2015 年 2 月的风向数据。 16 | 17 | 风向一共分为 16 个方位,以北向为起始点,每隔 22.5° 确定一个风向,分别为北、东北偏北、东北、东北偏东、东、东南偏东、东南、东南偏南、南、西南偏南、西南、西南偏西、西、西北偏西、西北、西北偏北。根据风向数据,依次同统计各个风向出现的次数,统计出频率,具体见下表 18 | 19 | | 编码 | 风向 | 风向字母 | 风向频率 | 20 | | :--: | :------: | :------: | :------: | 21 | | 1 | 北 | N | 6.48 | 22 | | 2 | 东北偏北 | NNE | 3.50 | 23 | | 3 | 东北 | NE | 5.89 | 24 | | 4 | 东北偏东 | ENE | 1.54 | 25 | | 5 | 东 | E | 7.69 | 26 | | 6 | 东南偏东 | ESE | 10.95 | 27 | | 7 | 东南 | SE | 8.77 | 28 | | 8 | 东南偏南 | SSE | 8.25 | 29 | | 9 | 南 | S | 7.04 | 30 | | 10 | 西南偏南 | SSW | 7.35 | 31 | | 11 | 西南 | SW | 6.98 | 32 | | 12 | 西南偏西 | WSW | 3.15 | 33 | | 13 | 西 | W | 1.86 | 34 | | 14 | 西北偏西 | WNW | 2.04 | 35 | | 15 | 西北 | NW | 8.07 | 36 | | 16 | 西北偏北 | NNW | 10.43 | 37 | 38 | ## 3. 绘制风玫瑰图 39 | 40 | 下面使用 Python 的`matplotlib`包进行风玫瑰图的绘制。 41 | 42 | 首先,定义角度和风频数据。角度通过`np.deg2rad`转换为弧度 43 | 44 | ```python 45 | # 定义角度和风频数据 46 | deg_angles = [0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180, 202.5, 225, 247.5, 270, 292.5, 315, 337.5, 0] 47 | data = [6.48, 3.50, 5.89, 1.54, 7.69, 10.95, 8.77, 8.25, 7.04, 7.35, 6.98, 3.15, 1.86, 2.04, 8.07, 10.43, 6.48] 48 | angles = np.deg2rad(deg_angles) 49 | labels = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'] 50 | ``` 51 | 52 | 然后创建 plot 使用极坐标(polar)。分别设置 X、Y 坐标轴和网格 53 | 54 | ```python 55 | # Create the plot 56 | fig = plt.figure(figsize=(8, 8)) 57 | ax = fig.add_subplot(111, polar=True) 58 | # 设置X轴 59 | ax.set_xticks(angles) 60 | ax.set_xticklabels(labels) 61 | # 设置Y轴 62 | ax.set_yticks([4, 6, 8, 10, 12]) 63 | ax.set_yticklabels(['4%', '6%', '8%', '10%', '12%']) 64 | # 设置网格 65 | ax.xaxis.grid(color='gray', linestyle='-', linewidth=1) 66 | ax.yaxis.grid(color='gray', linestyle='--', linewidth=1) 67 | ``` 68 | 69 | 此时,创建的坐标系如图所示。显然,默认的极坐标系并不能满足我们的需求。需要设置 0 度位置为正北("N")[^2],并设置角度增加方向为逆时针,设置完如图 2 所示。 70 | 71 | ```python 72 | # 设置theta的零的位置为正北。 73 | ax.set_theta_zero_location('N') 74 | # 设置角度增加的方向为逆时针 75 | ax.set_theta_direction(-1) 76 | ``` 77 | 78 | | ![图一](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Snipaste_2023-05-21_22-47-03.png) | ![图二](https://drive.liuxs.pro/api/raw/?path=/Images/blog/Snipaste_2023-05-21_22-47-20.png) | 79 | | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | 80 | | 图 1 | 图 2 | 81 | 82 | 最后绘制风频玫瑰图 83 | 84 | ```python 85 | # 风频玫瑰图 86 | ax.plot(angles, data, '.-', linewidth=1, color="blue") 87 | ax.fill(angles, data, alpha=0.3, color='blue') 88 | ``` 89 | 90 | 91 | 92 | ## 4. 绘制 CAD 风格风玫瑰图 93 | 94 | 我们经常见到的一直风玫瑰图是黑白相间,并且带指北箭头的(可以称之为 CAD 风格玫瑰图?),可以使用`matplotlib`的路径`Path`去绘制风玫瑰的形状及箭头,最后隐藏掉坐标轴和网格。 95 | 96 | > 关于`matplotlib`中`Path`的使用,可以参考此教程:[Path Tutorial — Matplotlib 3.7.1 documentation](https://matplotlib.org/stable/tutorials/advanced/path_tutorial.html)。 97 | > 这是翻译的版本:[路径教程 · Matplotlib 用户指南 @飞龙 (gitbooks.io)](https://wizardforcel.gitbooks.io/matplotlib-user-guide/content/3.8.html) 98 | 99 | 完整代码如下: 100 | 101 | ```python 102 | import math 103 | 104 | import matplotlib.pyplot as plt 105 | import numpy as np 106 | from matplotlib.patches import PathPatch, FancyArrowPatch 107 | from matplotlib.path import Path 108 | 109 | 110 | def create_polygons(angles: list, data: list): 111 | """创建风玫瑰黑白色填充图形""" 112 | black_vertices = [] 113 | black_codes = [] 114 | white_vertices = [] 115 | white_codes = [] 116 | for i in range(0, 16, 2): 117 | black_codes += [Path.MOVETO] + [Path.LINETO] * 2 + [Path.CLOSEPOLY] 118 | black_vertices += [(0, 0), (angles[i], data[i]), (angles[i + 1], data[i + 1]), (0, 0)] 119 | 120 | for i in range(1, 16, 2): 121 | white_codes += [Path.MOVETO] + [Path.LINETO] * 2 + [Path.CLOSEPOLY] 122 | white_vertices += [(0, 0), (angles[i], data[i]), (angles[i + 1], data[i + 1]), (0, 0)] 123 | 124 | black_path = Path(black_vertices, black_codes) 125 | white_path = Path(white_vertices, white_codes) 126 | black_pathpatch = PathPatch(black_path, facecolor='black') 127 | white_pathpatch = PathPatch(white_path, facecolor='white') 128 | return black_pathpatch, white_pathpatch 129 | 130 | 131 | def create_axis(h_start, h_end, v_start, v_end): 132 | """创建坐标轴""" 133 | arrowstyle = '-|>' # 箭头样式 134 | mutation_scale = 25 # 箭头大小 135 | linewidth = 2 # 箭头边框宽度 136 | edgecolor = 'black' # 箭头边框颜色 137 | facecolor = 'black' # 箭头填充颜色 138 | 139 | h_axis = FancyArrowPatch( 140 | h_start, # 起点坐标 141 | h_end, # 终点坐标 142 | arrowstyle=arrowstyle, 143 | mutation_scale=mutation_scale, 144 | linewidth=linewidth, 145 | edgecolor=edgecolor, 146 | facecolor=facecolor 147 | ) 148 | 149 | v_axis = FancyArrowPatch( 150 | v_start, # 起点坐标 151 | v_end, # 终点坐标 152 | arrowstyle=arrowstyle, 153 | mutation_scale=mutation_scale, 154 | linewidth=linewidth, 155 | edgecolor=edgecolor, 156 | facecolor=facecolor 157 | ) 158 | return h_axis, v_axis 159 | 160 | 161 | # 定义角度和风频数据 162 | deg_angles = [0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180, 202.5, 225, 247.5, 270, 292.5, 315, 337.5, 0] 163 | data = [6.48, 3.50, 5.89, 1.54, 7.69, 10.95, 8.77, 8.25, 7.04, 7.35, 6.98, 3.15, 1.86, 2.04, 8.07, 10.43, 6.48] 164 | max_data = math.ceil(max(data)) 165 | angles = np.deg2rad(deg_angles) 166 | labels = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'] 167 | 168 | # Create the plot 169 | fig = plt.figure(figsize=(8, 8)) 170 | ax1 = fig.add_subplot(121, polar=True) 171 | ax2 = fig.add_subplot(122, polar=True) 172 | 173 | # 设置theta的零的位置为正北。 174 | # https://matplotlib.org/stable/api/projections/polar.html#matplotlib.projections.polar.PolarAxes.set_theta_zero_location 175 | ax1.set_theta_zero_location('N') 176 | # 设置角度增加的方向为逆时针 177 | ax1.set_theta_direction(-1) 178 | 179 | # 设置标题 180 | # ax.set_title('Windrose', fontsize=14) 181 | # 设置X轴 182 | ax1.set_xticks(angles) 183 | ax1.set_xticklabels(labels, fontsize=22, fontname="Times New Roman") 184 | ax1.tick_params(axis='x', which='major', pad=18) 185 | # 设置Y轴 186 | ax1.set_yticks([4, 6, 8, 10, 12]) 187 | ax1.set_ylim(0, max_data + 2) 188 | ax1.set_yticklabels(['4%', '6%', '8%', '10%', '12%']) 189 | # 设置网格 190 | ax1.xaxis.grid(color='gray', linestyle='-', linewidth=1) 191 | ax1.yaxis.grid(color='gray', linestyle='--', linewidth=1) 192 | # ax.grid(True) 193 | 194 | black, white = create_polygons(angles, data) 195 | 196 | h_axis, v_axis = create_axis( 197 | (angles[8], max_data + 1), (angles[0], max_data + 1), 198 | (angles[12], max_data + 1), (angles[4], max_data + 1) 199 | ) 200 | 201 | ax1.plot(angles, data, '.-', linewidth=1, color="blue") 202 | ax1.fill(angles, data, alpha=0.3, color='blue', ) 203 | 204 | # CAD风格风玫瑰图 205 | ax2.set_theta_zero_location('N') 206 | ax2.set_theta_direction(-1) 207 | 208 | ax2.set_xticks(angles) 209 | ax2.set_ylim(0, max_data + 2) 210 | ax2.add_patch(black) 211 | ax2.add_patch(white) 212 | ax2.add_patch(h_axis) 213 | ax2.add_patch(v_axis) 214 | ax2.text(angles[0] + 0.1, max_data + 0.5, "N", ha="center", va="center", fontsize=22) 215 | ax2.grid(False) 216 | ax2.set_axis_off() 217 | 218 | plt.show() 219 | 220 | ``` 221 | 222 | ![](https://drive.liuxs.pro/api/raw/?path=/Images/blog/image-2023-05-21_22-17-17.webp) 223 | 224 | [^1]: [风玫瑰图 - 维基百科,自由的百科全书 (wikipedia.org)](https://zh.wikipedia.org/wiki/%E9%A3%8E%E7%8E%AB%E7%91%B0%E5%9B%BE) 225 | [^2]: [matplotlib.projections.polar — Matplotlib 3.7.1 documentation](https://matplotlib.org/stable/api/projections/polar.html#matplotlib.projections.polar.PolarAxes.set_theta_zero_location) 226 | --------------------------------------------------------------------------------