QGIS 自動化進階篇:從視覺標註 (POC2) 到空間分析 (POC3) 的進化歷程

在實現 QGIS 專案「一鍵生成」的路上,我們不僅要讓圖層「開得出來」,更要讓它「具備研究價值」。 這篇文章記錄了我們從 POC2 (樣式與標籤整合) 進化到 POC3 (空間分析與 SDM 紀律) 的完整心路歷程。這兩個階段分別解決了製圖中的「美學溝通」與「科學建模」兩大核心問題。 🎨 POC2:視覺精煉與動態標籤 (Visualization & Labeling) 目的:讓地圖「會說話」,透過自動化配置解決手動製圖中最繁瑣的排版工作。 在 POC1 成功掛載圖層後,我們發現原始的點位與線條在混亂的歷史底圖(如台灣堡圖)上極難辨識。POC2 的核心目標即是透過腳本,賦予地圖專業的視覺外觀。 1. 幾何感知的自動標註 (Geometry-aware placement) 我們實現了腳本對圖層類型的自動識別與對應配置: 點位圖層 (遺址):自動採用 Centroid 配置,讓標籤精確落在遺址中心。 線段圖層 (河流):自動切換為 Around Line (平行排列),並預設帶有 0.5mm 的偏移量。這讓河名能順著水流蜿蜒,展現專業製圖感。 2. 跨平台字型與視覺消隱 (The macOS Fix) 我們在 POC2 中遭遇了字型相容性的重擊。原本預設的字型在 Mac 上會毀掉專案解析。最終,我們強制切換為 PingFang TC (萍方-繁),並自動啟用了 0.7mm 白色光暈 (Text Buffer)。這小小的光暈,是讓地圖文字從花綠底圖中「浮現」出來的關鍵。 🛡️ POC3:空間分析中樞與 SDM 紀律 (Advanced Analysis & SDM) 目的:從「資料顯示」進化為「規律開發」,在不污染原始數據的前提下,自動生成分析模型。 當地圖變漂亮後,下一步就是「看見模式」。POC3 挑戰的是自動產出具有研究深度的分析圖層(如海拔區間與熱點圖)。 1. 恪守 SDM 紀律:絕對不改動原始資料 (Data Hub Snapshot) 開發者常為了分析方便,在原始 SQLite 裡建 View。但對歷史數據來說,原始碼應該是神聖的 (Holy Source)。 我們實作了「局部資料中樞 (Local Data Hub)」:腳本在產出專案的一瞬間,自動建立一個 poc_data_hub.db 快照。所有的 JSON 萃取、高程分區計算都只存在於這份快照中。原始 HGIS 資料庫保持 100% 乾淨,專案檔則是「可打包、可重製、可拋棄」的。 ...

2026-03-07 · 1 min · 176 words · Wuulong

軟體定義地圖:從 SQLite 到一鍵生成的 QGIS 專案自動化 (SDM)

在進行 HGIS(歷史地理資訊系統)研究時,我發現最耗時的往往不是分析數據,而是「準備地圖」。每次要開啟一個新研究,總要重複掛載百年歷史底圖、設定 KML 座標、調整 SQLite 的 SQL 篩選器…。 為了打破這個瓶頸,我發起了一場實驗:能不能像寫程式一樣,用「寫」的出一份 QGIS 專案? 這就是我最近在推動的 軟體定義地圖 (Software-Defined Maps, SDM) 概念。 🏗️ 為什麼要自動化? 如果你手動配置過 QGIS,你一定遇過這些崩潰瞬間: URL 特殊字元破壞專案:WMTS 的 URL 裡一堆 &,手動貼上 XML 常報錯。 座標系統 (CRS) 位移:明明設定了 WGS84,圖層卻飄到非洲西岸海面(座標 0,0)。 重複定義的勞力:每個專案都要重新分組(底圖、水理、點位),這應該交給機器人。 透過 Python + Jinja2 模板,我們可以把地圖專案變成可程式化的零件。 🛠️ 基本作法:Jinja2 零件化 核心思路是將 .qgs 專案檔(實質是 XML)拆解為模板。 我們建立了一個 base_project.qgs.j2,裡面預留了動態注入的內容: 層級化圖層樹:自動生成的分組。 資料源設定:SQL 篩選指令與檔案路徑。 空間參考系統:自動注入對應的 AuthID、Proj4 與 SRID。 最關鍵的「零件庫」是 GIS_KNOWLEDGE.yaml。它充當了 GIS 資源的「DNS 伺服器」,記錄了所有中研院底圖、本機 KML 與資料庫的 Metadata。 🚀 第一階段 POC:曾文溪 HGIS 數位中樞 在這次的 POC 中,我們成功實現了 Zengwen_HGIS_Hub.qgs 的一鍵產出: ...

2026-03-07 · 1 min · 144 words · Wuulong

從混亂至秩序:鳥鳴音訊資料庫的 AI 自動化改裝實錄

面對數百首從 CD 轉錄、檔名雜亂、標籤缺失的鳥鳴音訊檔,你會選擇手動一首首修改,還是開發一套具備「生物學智商」的自動化系統? 我起初的想法很單純:在野外走動時,如果聽到鳥聲,我希望能有機會辨識出那是哪隻鳥。於是,企鵝給了我一大包鳥鳴音檔。我只是想找個容易的方式,能隨時翻出想聽的聲音來學習,結果為了這個簡單的願望,就乾脆擼出了這套工具。 這篇文章記錄了 birdsong-processing-kit 的誕生過程:我們如何利用 Gemini AI 與 iNaturalist 生物資料庫,將 816 首混亂的 MP3,轉化為具備「綱、目、科、屬」專業階層、內嵌高清封面與詳細標籤的數位資產。 🎯 核心目標:建立聲音的「數位孿生」 我們擁有的原始資料非常雜碎:有的檔名是「3-55」,有的標籤是日文,有的則是空白。這次自動化改裝的核心目標有三: 結構化分類:按生物學階層(綱/目/科/屬)重新組織目錄。 資訊厚化:自動注入 iNaturalist 的標準中文名、學名、以及物種封面圖片。 溯源管理:在 ID3 標籤中保留原始路徑,確保搬移後依然能追蹤來源。 🛠️ 實作方法:身分校對三部曲 (Identity Cascade) 在開發過程中,我們建立了一套名為「識別決策瀑布」的邏輯,以達到準確度與成本的平衡: 1. 文字優先 (Text-First Discovery) 過度依賴 AI 聽音辨位既昂貴又緩慢。系統會先遍歷原始 ID3 標籤與檔名,清洗掉序號與雜訊,產生候選清單,並優先查詢 iNaturalist API。只要文字比對能獲得精確分類,就不啟動 AI。 2. AI 聽聲辨位 (Gemini 2.5-flash) 針對完全沒有名稱資訊的音軌,系統會自動上傳音訊至 Gemini 2.5-flash。透過 Flash 模型強大的多模態理解力,讓 AI「聽完」後回傳 JSON 格式的識別報告,作為識別的最強墊底方案。 3. 分類中文化校正 iNaturalist 的高階分類(如目、科)往往只有英文或拉丁文。我們另外調用了 Gemini 2.5-flash 針對識別出的學名清單進行批次翻譯,確保目錄結構呈現如「鴞形目/鴟鴞科」這樣的純中文專業觀感。 🚧 遇到的挑戰與克服之道 挑戰 A:AI 成本與處理速度的矛盾 困境:全量 800 多筆若全部上傳辨識,不僅 Token 消耗大,且速度緩慢。 克服:實施「文字預檢」機制。透過 mutagen 深度挖掘 ID3 標籤中的「隱藏資訊」,將 80% 的檔案在文字階段就完成對合,剩餘的 20% 才交由 AI 處理。 ...

2026-02-25 · 1 min · 126 words · Wuulong

從地名到建構 HGIS 的數位鏈金術 (3):【文本】教 AI 讀古書——《臺灣通史》的百萬字結構化工程

上一篇我們用 Python 打造了能解讀歷史座標的「時光羅盤」。但羅盤只會告訴你地名,無法告訴你歷史。真正的歷史,藏在文言文的汪洋大海中。 因為我本身對台灣史完全沒有概念,為了建立自己的知識體系,我首先向 AI 提問,得到了這份極具啟發性的「台灣方志演化樹」: 【根】 全域性/起源 (Early & Comprehensive) ║ ╚══ 1684《福建通志‧臺灣府》(清代最早的官方紀錄) ║ ╠══ 1694《臺灣府志》(確立台灣史志的基本架構) ║ ╠══ 1920《臺灣通史》(連橫著,民間史學集大成之作) ║ ╚══【幹】 區域行政中心 (以北台灣為例) ║ ╚══ 1871《淡水廳志》(新竹史料之源,具行政與大租分配紀載) ║ ╠══【枝】 縣級史志 (新竹設縣後) ║ ║ ║ ╠══ 1892《新竹縣志初稿》(新竹正式設縣後的首部地方志) ║ ║ ║ ╠══ 1895《新竹縣制度考》(日治初期清理清代制度之專作) ║ ║ ║ ╚══ 1907《新竹廳志》(結合日治調查與清代舊志) ║ ╚══【葉】 地方/微觀採訪 (Local & Micro-geographic) ║ ╠══ 1894《新竹縣採訪冊》(最細微的點位紀錄,如黃金洞、御史崎) ║ ╠══ 1898《樹杞林志》(專注於新竹東部山區與竹東拓墾) ║ ╚══ 1980+《新竹縣/市志》(現代修訂版本) 有了這張圖,我就知道該去哪裡尋寶了。於是,我花了一點時間想辦法把這些史書從網路上抓了下來(大部分都順利搞定了)。 ...

2026-02-22 · 1 min · 116 words · Wuulong

從地名到建構 HGIS 的數位鏈金術 (2):【開箱】時光羅盤:解析中研院古圖資與「GPS 反查百年地名」的魔法

上一篇提到,為了驗證地形圖上的「黃金洞」與歷史水利的關聯,我決定打造一個「歷史空間感知引擎」。要建構這套系統,第一步就是要獲得一把能夠解開空間座標的鑰匙。 我將目光轉向了中央研究院的 GIS 寶庫。 寶藏開箱:1920 年代的台灣地理切片 幸運的是,台灣學界已經將日治時期(1920 年代)的行政區劃轉換為數值圖資。我順利取得了當時的「街庄(1920b_1.shp)」與更細緻的村落級別「大字(1920a_1.shp)」Shapefile 檔案。 這兩份檔案就像是兩張巨大的網子,完整罩住了百年前的台灣島。 不過,要將百年前的網子跟現代的手機 GPS 疊合,遭遇了第一個技術難關:座標系的跨時空翻譯。歷史圖資通常使用 TWD67(台灣二度分帶),而我們現在的手機或 Google Maps 則是使用 WGS84 經緯度系統。利用 Python 的 Geopandas 套件,我寫了一段優美的投影轉換代碼,成功讓百年前的地圖「滑」進了現代的經緯網格中。 魔法實做 1:GPS 腳下檢索 (Point in Polygon) 有了吻合的圖資後,我開發了第一個功能。這是一個純粹的數學運算,俗稱 Point in Polygon (點在多邊形內)。 現在,只要我輸入一組現代的 GPS 座標(或許是我走在竹北街頭,或是站在台南的田野),程式就會瞬間吃進這組座標,穿越時空,告訴我:「你腳下站的地方,在 1920 年屬於新竹州新竹郡新竹街!」 魔法實做 2:方圓百里的時光雷達 (Spatial Proximity) 光知道腳下叫什麼名字還不夠,我想要有「導遊」的感覺。 於是我加入了 Buffer(緩衝區)的概念,實作了「附近古地名查詢」功能。想像你站在定點,按下啟動鍵,程式會以你為中心,發射出半徑數公里的虛擬雷達波。雷達波不僅會掃過 1920 年代的各級行政區,更進一步結合了內政部釋出的 3 萬多筆「古地名/歷史聚落」空間圖資。這樣一疊加,所有被涵蓋在範圍內的聚落名稱(甚至是已經消失的微小地名)全部都會列印出來。 這套被我戲稱為「時光羅盤」的模組,讓我們具備了強大的 空間反查能力。 執行例子: python scripts/query_hgis_point.py 24.785150 120.967825 在 1920 年屬於: 🏛️ 州廳: 新竹州 🏰 郡市: 新竹郡 🏘️ 街庄: 香山庄 但問題是,地圖上的地名是冷的。這個古地名背後曾經發生過什麼驚心動魄的故事?有哪個先賢曾經在這裡開圳引水? ...

2026-02-22 · 1 min · 77 words · Wuulong

「Skill 優先」的 AI 協作新實踐:從需求定義到知識點萃取的自動化進化

「Skill 優先」的 AI 協作新實踐:從需求定義到知識點萃取的自動化進化 🚀 序言:一個新的協作節奏 在過去的 AI 協作中,我通常是先遇到問題,發出指令,解決之後再考慮是否要將它整理成工具。但這一次,我試著調換順序:先說出一個「Skill」的構想,定義它的通用流程與腳本格式,然後直接用它來演練並修正。 這次的目標是:如何高效地將長達一個多小時的「哈爸實驗室雙周會」錄影,拆解成一個個有價值的「知識點 (Knowledge Points)」。 🏗️ 第一步:Skill 的定義與構建 (Defining the Skill) 與其說「幫我切影片」,我對 AI 下達的指令是:「我們來做一個 knowledge-point-distiller Skill」。這個 Skill 必須具備以下完整閉環: 影音處理自動化:能根據時間清單自動切割影片,並同步產出適合 NotebookLM (容量 < 20MB) 的優化 MP3。 資源歸檔結構化:每一個知識點 (KP) 都有自己的資料夾,包含 MP4 片段、MP3 音訊與 metadata.md。 SOP 化:將整套流程寫入 .agent/skills/ 目錄下的 SKILL.md,讓 AI 與我都能隨時查閱操作標準。 這種「先建工具,後打仗」的做法,讓我在還沒開始處理影片前,就已經擁有了整套「生產線」。 🧪 第二步:實戰演練與即時修正 (Practice & Refinement) 有了工具後,我們直接拿「2/6 哈爸實驗室雙周會」進行測試。這個階段的「修正」是重點: 全場音訊的需求:在切割子片段的過程中,我發現除了個別知識點,我也需要一份「全場完整錄音」上傳到 NotebookLM 進行全局綜整。於是,我們立刻將這個功能補進 Skill 的 SOP 中。 與 Git 的整合:歸檔不是終點,提交才是。我們在 Skill 流程中補上了 GitHub 的 Commit 與 Push 指引。 跨 Skill 調度:在這個過程中,新的 Skill 自動調用了先前的 media-processor Skill 來處理高品質音訊壓縮,展現了技術模組之間的協同能力。 💡 關鍵體驗:為什麼「先說出 Skill」很有用? 這是我第一次嘗試「先定義、再運作、後修正」的模式,心得如下: ...

2026-02-07 · 1 min · 145 words · Wuulong

影像豐富化之路:從雜訊清洗到 AI 自動化的 POI 補完實錄

影像豐富化之路:從雜訊清洗到 AI 自動化的 POI 補補完實錄 📸 前言:一張圖勝過千言萬語 在開發 WalkGIS 的過程中,我們發現一個「有溫度的點位」必須包含視覺元素。然而,當點位數量來到數百甚至上千個時,手動去一張張找圖、貼連結是不可能的任務。更具規戰性的是,原始資料中的點位名稱往往帶有大量管理雜訊(如「住-」、「推薦-」、「集章處-」),這些「髒資料」會讓搜尋引擎徹底失焦。 本文記錄了我們如何利用 AI 協作,從 0 到 1 打造出一套自動化的「POI 影像豐富化」流程,並將之封裝為一套可重複使用的技術 Skill。 🛠 演進工序:解決問題的三大步 1. 構建「階梯式」搜尋隊列 (Tiered Retrieval) 單一來源往往無法滿足多樣的地景需求。我們設計了一套「階梯式」策略: 第一層:Wikipedia PageImage API —— 針對權威性景點(如「台江國家公園」),直接抓取維基百科條目圖片,準確度最高。 第二層:Wikimedia Commons API —— 利用共享資源庫,透過多重名稱變體(如 File:{Name}, {Name} Taiwan)進行廣泛搜尋。 第三層:Google Places API (最強後援) —— 當開源資源都失效時,調用 Google Places Photo。這確保了即便是在山區的小型「工作站」或「民宿」,也能有高質量的實景照片。 2. 語意理解:LLM 驅動的名稱清洗 (Name Sanitization) 這是最核心的突破。面對 住-護照優惠-特富野民宿 這種名稱,傳統的正則表達式很難清乾淨。 LLM 預處理:我們讓 AI 批量讀取點位名稱,並產出「搜尋關鍵字陣列 (Array of Search Seeds)」。 映射機制:例如將 山海驛站-官田工作站 自動對應到 ["官田工作站", "農田水利署官田工作站"]。 規一化匹配:為了避免 mapping 時因為全半形符號(如 | vs |)或多餘空白導致失敗,我們在腳本中實現了名稱規一化演算法。 3. 工程化與 Skill 化 為了讓這套邏輯不再只是「一次性腳本」,我們完成了以下優化: ...

2026-02-03 · 1 min · 144 words · Wuulong

數位考古實作:三種方法挖掘隱藏在 Log 中的賦能數據

定義了考古地層後,接下來就是技術實作。本文分享我如何運用三種不同的技術手段,分別處理「手動存檔」、「碎裂 Log」與「協作筆記」,從數萬條紀錄中量化出我的 AI 賦能證據。

2026-01-27 · 1 min · 135 words · Wuulong

[WalkGIS] 當 GIS 數據遇上旅行魂:AI 協助規劃濁水溪四日遊與美食導航

最近在整理 WalkGIS 的「濁水溪流域地圖」,資料庫裡累積了近 400 個景點 (POI)。看著這些密密麻麻的點,我突然萌生一個念頭:「能不能請 AI 幫我規劃一趟從海口走到源頭的四日遊?」 這原本只是一個隨性的提問,沒想到 AI 利用資料庫中的經度 (Longitude) 資料,展現了令我驚豔的規劃能力,甚至連車宿點和美食導航都一手包辦。 1. 驚豔的「經度切分法」 過去規劃行程,我們常是對著地圖發呆,一個個把點連起來。但這次,AI 展現了工程師的思維: 「根據資料庫中的 390 個景點,按經度由西(海口)向東(高山)排序…」 它寫了一支簡單的 Python 腳本,直接讀取 walkgis.db,將所有景點依經度排序,然後切分成四個區塊: Day 1: 海口風情與糧倉沃野 (經度 < 120.40) 起點:麥寮港 / 濁水溪出海口 重點:見證大河入海的壯闊與工業/漁村並存的景象。 推薦停靠: 麥寮拱範宮:國定古蹟,海口信仰中心。 鷺鷥生態景觀公園:觀賞濕地生態。 詔安客家文化園區:探訪崙背/二崙的詔安客家文化。 米香囍懷舊農村生活館:體驗稻米文化。 私房點:下溪墘堤防看夕陽。 Day 2: 橋樑、古鎮與分水樞紐 (經度 120.41 - 120.65) 區域:西螺、溪州、二水、林內 重點:濁水溪最精華的人文段,看大橋與分水工。 推薦停靠: 西螺大橋:必訪地標,連結雲彰兩岸。 溪州尚水 / 森林公園:體驗友善農耕與台糖鐵道歷史。 林內一號/二號進水口:看濁水溪如何被引入嘉南大圳。 二水車站:集集線起點,鐵道迷聖地。 私房點:二水堤防國聖碑。 Day 3: 集集支線與淺山聚落 (經度 120.65 - 120.85) 區域:竹山、集集、名間、水里 重點:進入丘陵區,沿著集集支線火車溯源。 推薦停靠: 竹山文化園區 / 菸葉場:認識竹藝與菸草歷史。 集集車站 / 綠色隧道:騎單車漫遊。 明潭/大觀發電廠:水里鄉的水力發電重鎮。 鹿谷鳳凰谷鳥園 / 麒麟潭:往南延伸至鹿谷茶鄉。 私房點:甘泉井及石頭公。 Day 4: 雲端秘境與源頭探索 (經度 > 120.85) 區域:信義、日月潭、仁愛、合歡山 重點:深入中央山脈,尋找濁水溪的源頭。 推薦停靠: 日月潭 (水社/伊達邵):雖是水庫,但水源來自濁水溪武界。 東埔溫泉:在陳有蘭溪畔泡湯(濁水溪最大支流)。 奧萬大國家森林遊樂區:萬大溪流經的賞楓勝地。 武界部落 (曲冰遺址):被稱為「雲的故鄉」,濁水溪最美河段之一。 終點:合歡山主峰/東峰 (遠眺濁水溪源頭霧社溪)。 這個行程剛好涵蓋了工業、農業、水利、鐵道、觀光到高山生態,是認識台灣母親之河的絕佳路線。 ...

2026-01-12 · 1 min · 182 words · Wuulong

[GIS筆記] SHP 轉檔的亂碼陷阱:從「百年舊堤」案例看編碼偵測的重要性

在將既有的 GIS 開放資料(如 QGIS 專案中的 Shapefile)整合進 WalkGIS 系統時,最常遇到的挑戰往往不是座標轉換,而是文字編碼(Encoding)。 最近在處理「濁水溪流域情報地圖」中的「百年舊堤」圖層時,我遇到了一個經典的亂碼案例。這篇文章將紀錄問題的成因、誤判的過程,以及最終的正確解法,作為未來處理 GIS 舊資料的參考。 案例背景 我試圖將一份名為 23座百年舊堤.shp 的檔案匯入我的 WalkGIS SQLite 資料庫。 1. 初次嘗試:預設轉檔的失敗 一開始,我直接將 SHP 轉為 KML/KMZ 進行預覽。結果在 KML 中,所有的堤防名稱都變成了無法辨識的亂碼,如: 偌 這些字並不是常見的「」或亂數,而是顯示為 PUA (Private Use Area) 區域的罕見字元。這是一個強烈的訊號,代表編碼解讀錯誤。 2. 直覺誤判:這一定是 Big5 台灣早期的 GIS 資料(尤其是政府開放資料),絕大多數的 .dbf 屬性表都使用 Big5 (CP950) 編碼。當看見中文變亂碼時,直覺反應通常是:「啊,這一定是工具用 UTF-8 去讀 Big5 造成的。」 於是我在 Python 腳本中強制指定了編碼: # 錯誤的嘗試 gdal.SetConfigOption("SHAPE_ENCODING", "CP950") 結果出乎意料:亂碼依然存在,甚至變成了另一種形式的亂碼。如果您嘗試用各種 Big5/Latin1 交叉解碼都無效,那問題可能比想像中複雜。 真相:雙重編碼災難 為了找出真相,我使用 ogrinfo 指令進行了雙盲測試(A/B Testing): 測試 A:指定 CP950 ogrinfo -al -so --config SHAPE_ENCODING CP950 "23座百年舊堤.shp" > Name (String) = 偌 (亂碼) 測試 B:不指定 (預設/UTF-8) ...

2026-01-11 · 1 min · 198 words · Wuulong