2026-06-30

QGIS 圖資自動輸出與簡報產製流程說明

 

QGIS 圖資自動輸出與簡報產製流程說明

一、目的說明

本流程主要目的,是將既有的 GIS 圖資與分析成果,透過 QGIS 專案與版面配置設定,自動輸出為圖片,並依序套用到簡報檔案中,減少人工截圖、貼圖與排版時間。

透過此方式,可快速產製多個行政區、鄉鎮、市區或指定範圍的成果圖,並保持每一頁簡報的地圖樣式、圖例、標題與表格格式一致,方便後續成果展示、會議簡報、災害分析或報告彙整使用。與圖冊概念類似但目的不同


二、整體流程概念

整體流程可分為四個主要階段:

  1. 準備 GIS 圖資與分析成果

    先將需要展示的圖層整理至 QGIS 專案中,例如行政區邊界、鄉鎮界、道路、水系、淹水範圍、統計成果或其他分析圖層。

  2. 建立 QGIS 專案與 Layout 樣板

    在 QGIS 中設定好圖層配色、標籤、圖例、比例尺、指北針與版面配置。後續自動化程式會沿用此專案樣式,不需要每次重新設計地圖。

  3. 依指定範圍自動輸出圖片

    程式可依據鄉鎮邊界或指定圖徵,自動計算每個範圍的外框,並保留適當邊界空間,例如上下左右各保留約 5% 的留白,使輸出的地圖範圍不會過於緊貼邊界。

  4. 自動貼入簡報或文件

    輸出的地圖圖片可依序貼入 PowerPoint 或 Word 樣板中,並同步更新頁面標題、鄉鎮名稱、統計數值或說明文字,形成一致格式的成果簡報或報告。


三、QGIS 專案與 Layout 設定建議

為了讓後續能快速出圖,建議先建立固定的 QGIS 專案樣板。

QGIS 專案中應預先設定:

  • 圖層順序

  • 圖層配色

  • 行政區或鄉鎮邊界樣式

  • 淹水範圍或分析成果圖層樣式

  • 道路、水系、地標等輔助圖層

  • 圖例、比例尺、指北針

  • 標題與必要註記

Layout 版面中建議保留固定位置:

  • 主地圖區

  • 標題區

  • 圖例區

  • 統計表格區

  • 資料來源與製作日期

  • 小範圍位置示意圖,若有需要可加入

後續自動化程式只需要控制主地圖的顯示範圍與輸出圖片,不需要重新設定版面,因此可以確保每張圖成果一致。


四、單一鄉鎮快速出圖概念

若後續資料需要依鄉鎮、市區或行政區逐一產製成果圖,建議以「單一鄉鎮聚焦」方式輸出。

此方式的優點是:

  • 每張圖可清楚呈現該鄉鎮內的分析結果

  • 適合呈現細部淹水範圍、道路影響或空間分布

  • 可搭配統計表格呈現該區的成果數值

  • 適合大量產製簡報頁面

若需要保留整體位置概念,可在 Layout 角落加入小型位置圖,例如縣市全圖,並用框線標示目前展示的鄉鎮位置。如此可同時兼顧細部成果與整體位置說明。


五、圖片輸出格式建議

自動輸出的地圖圖片建議使用 PNG 格式。

PNG 格式適合地圖輸出,原因包括:

  • 圖面文字較清楚

  • 邊界線與圖例不易失真

  • 適合貼入簡報或 Word 文件

  • 可維持良好的圖面品質

若簡報檔案大小需要控制,可依需求調整輸出解析度。一般簡報用途可採用 150 至 200 DPI;若需要列印或放大展示,可提高解析度。


六、簡報自動產製方式

簡報自動產製可使用既有 PowerPoint 作為樣板。

樣板中可先設計好:

  • 封面或章節頁

  • 每頁固定的地圖位置

  • 標題文字框

  • 統計表格

  • 說明文字區

  • 頁尾資訊

程式執行時,可依序完成下列工作:

  1. 讀取鄉鎮或指定範圍清單

  2. 依序切換 QGIS 地圖顯示範圍

  3. 輸出每個範圍的地圖圖片

  4. 複製簡報樣板頁

  5. 將圖片貼入指定位置

  6. 更新頁面標題與統計資訊

  7. 輸出完整簡報檔案

如此可將原本需要手動處理的重複工作,自動轉換為批次作業。


七、前置資料準備注意事項

為了讓後續能順利進行快速出圖,建議先確認以下項目。

1. 圖資命名要清楚

各圖層名稱建議清楚標示用途,例如:

  • 鄉鎮邊界

  • 淹水範圍

  • 道路圖層

  • 水系圖層

  • 統計結果

  • 底圖或參考圖層

清楚的命名可讓程式自動讀取指定圖層,也方便後續維護。

2. 行政區欄位要一致

若要依鄉鎮批次輸出,鄉鎮邊界資料中應至少包含:

  • 縣市名稱

  • 鄉鎮名稱

  • 行政區代碼,若有

  • 面積或其他統計欄位,若有需要

這些欄位可用來產生簡報標題、排序出圖順序與填入統計表格。

3. 輸出範圍要有固定規則

每張圖建議依據指定範圍的邊界自動計算外框,並保留一定比例的留白,例如 5%。

這樣可以避免地圖太貼近邊界,也能讓每一頁圖面看起來更穩定。

4. QGIS 樣式需先確認

自動輸出會沿用 QGIS 專案內的圖層樣式,因此在執行前應先確認:

  • 圖層是否正確開啟

  • 配色是否清楚

  • 標籤是否過密

  • 圖例是否正確

  • 底圖是否會影響判讀

  • 重要圖層是否被其他圖層遮住

5. Layout 版面需固定

簡報批次輸出時,最重要的是版面一致。建議地圖框、標題、圖例、統計表格等位置都先固定在樣板中,避免每次產製結果不同。

6. 統計表格資料需先整理

若簡報中需要呈現各鄉鎮統計值,例如面積、比例、最大值或排名,建議先整理成 CSV 或 Excel 表格,並與鄉鎮名稱或代碼對應。

程式即可依據鄉鎮名稱自動抓取對應數值,填入簡報表格。

7. 檔案路徑建議固定

建議建立固定資料夾架構,例如:

project/
├─ qgis_project/
├─ input_data/
├─ layout_template/
├─ exported_images/
├─ pptx_template/
└─ output_pptx/

如此可避免因檔案路徑變動造成程式無法讀取資料。


八、適用情境

此流程適合用於下列資料應用情境:

  • 鄉鎮、市區或行政區成果圖批次產製

  • 淹水模擬成果展示

  • 災害潛勢圖快速輸出

  • 道路或建物影響範圍展示

  • 水文、水理或空間分析成果簡報

  • 定期報告或會議簡報自動化產製

  • 多區域、多事件、多情境成果比較


九、效益說明

透過 QGIS 專案與 Layout 樣板搭配自動化程式,可帶來以下效益:

  • 減少人工截圖與貼圖時間

  • 保持圖面樣式一致

  • 快速產製大量簡報頁面

  • 降低手動排版錯誤

  • 方便套用不同事件或不同資料

  • 提升成果展示效率

  • 有利於後續建立標準化出圖流程


十、後續擴充方向

後續可依需求擴充以下功能:

  • 依淹水面積或影響程度自動排序

  • 自動產製 Top 10 或 Top 20 行政區簡報

  • 自動加入統計表格與排名資訊

  • 自動產製 Word 報告

  • 自動輸出 PNG、PDF 或 PPTX

  • 加入位置索引圖

  • 加入道路、建物或重要設施影響分析

  • 串接事件資料,快速產製單一颱風或豪雨事件成果簡報


十一、結論

本流程的核心概念,是將 GIS 圖資、QGIS 樣式設定、Layout 版面配置與簡報樣板結合,形成一套可重複使用的自動化出圖流程。

只要前期完成 QGIS 專案與簡報樣板設定,後續即可針對不同鄉鎮、不同事件或不同分析成果,快速輸出一致格式的圖片與簡報檔案,提升資料應用、成果展示與報告產製效率。






2025-11-18

QGIS Raster to Vector use Python console step2

  • 資料處理步驟:
    1.讀取 Raster 並計算 25 等級等值線級距
    2.用 GDAL 產生等值線(Contour Lines)
    3.Polygonize:Raster 轉等值面多邊形
    4.將多邊形圖層載入 QGIS
    5.為每個 polygon 計算其 VALUE 所屬級距(LEVEL、MIN、MAX)
    6.套用 25 階色彩漸層分類
    7.加上級距標籤並加入 QGIS 專案


  • from osgeo import gdal, ogr, osr

    import numpy as np

    from PyQt5.QtGui import QColor

    from qgis.core import (

        QgsVectorLayer, QgsProject, QgsGraduatedSymbolRenderer, QgsRendererRange,

        QgsSymbol, QgsPalLayerSettings, QgsVectorLayerSimpleLabeling, QgsField

    )

    from qgis.PyQt.QtCore import QVariant


    # ----------------------------

    # 0. Path

    # ----------------------------

    input_raster = r"D:\download\value.tif"

    contour_line = r"D:\download\contour25.shp"

    contour_poly = r"D:\download\contour25_polygon.shp"


    # ----------------------------

    # 1. Read raster + get 25-class levels

    # ----------------------------

    ds = gdal.Open(input_raster)

    band = ds.GetRasterBand(1)

    arr = band.ReadAsArray()

    nodata = band.GetNoDataValue()


    valid = arr[arr != nodata]

    min_v = float(valid.min())

    max_v = float(valid.max())


    # 26 contour levels = 25 classes

    levels_list = np.linspace(min_v, max_v, 26).tolist()


    print("等值線級距:")

    for i in range(25):

        print(f"{i+1}: {levels_list[i]:.2f} ~ {levels_list[i+1]:.2f}")


    # ----------------------------

    # 2. Create contour lines (using raster band, correct API)

    # ----------------------------

    drv = ogr.GetDriverByName("ESRI Shapefile")

    try: drv.DeleteDataSource(contour_line)

    except: pass


    out_ds = drv.CreateDataSource(contour_line)

    srs = osr.SpatialReference()

    srs.ImportFromWkt(ds.GetProjection())


    line_lyr = out_ds.CreateLayer("contour", srs, ogr.wkbLineString)

    line_lyr.CreateField(ogr.FieldDefn("ELEV", ogr.OFTReal))


    print("⚙ 產生等值線…")


    gdal.ContourGenerate(

        band,                 # <-- 正確:使用 raster band

        ds.GetGeoTransform()[1],

        abs(ds.GetGeoTransform()[5]),

        levels_list,

        0,

        nodata,

        line_lyr,

        0,

        1

    )


    out_ds = None

    print("✔ 等值線完成:", contour_line)


    # ----------------------------

    # 3. Polygonize (convert raster → polygon)

    # ----------------------------

    try: drv.DeleteDataSource(contour_poly)

    except: pass


    poly_ds = drv.CreateDataSource(contour_poly)

    poly_lyr = poly_ds.CreateLayer("poly", srs, ogr.wkbPolygon)

    poly_lyr.CreateField(ogr.FieldDefn("VALUE", ogr.OFTReal))


    print("⚙ Polygonize…")

    gdal.Polygonize(band, None, poly_lyr, 0, [], callback=None)

    poly_ds = None

    print("✔ 等值面完成:", contour_poly)


    # ----------------------------

    # 4. Load polygon layer to QGIS

    # ----------------------------

    vl = QgsVectorLayer(contour_poly, "Geothermal_25Levels", "ogr")

    prov = vl.dataProvider()


    prov.addAttributes([

        QgsField("MIN", QVariant.Double),

        QgsField("MAX", QVariant.Double),

        QgsField("LEVEL", QVariant.Int)

    ])

    vl.updateFields()


    # ----------------------------

    # 5. Compute class (LEVEL, MIN, MAX)

    # ----------------------------

    i_min = vl.fields().indexOf("MIN")

    i_max = vl.fields().indexOf("MAX")

    i_lev = vl.fields().indexOf("LEVEL")

    i_val = vl.fields().indexOf("VALUE")


    with edit(vl):

        for f in vl.getFeatures():

            v = f["VALUE"]


            if v == nodata:

                f[i_lev] = -1

                f[i_min] = -9999

                f[i_max] = -9999

            else:

                for idx in range(25):

                    if levels_list[idx] <= v <= levels_list[idx+1]:

                        f[i_lev] = idx + 1

                        f[i_min] = levels_list[idx]

                        f[i_max] = levels_list[idx+1]

                        break


            vl.updateFeature(f)


    print("✔ LEVEL/MIN/MAX 填寫完成")


    # ----------------------------

    # 6. Style → 25-color gradient

    # ----------------------------

    ranges = []

    for idx in range(25):

        low = levels_list[idx]

        high = levels_list[idx+1]

        label = f"{low:.1f} - {high:.1f}"


        sym = QgsSymbol.defaultSymbol(vl.geometryType())

        sym.setColor(QColor.fromHsv(int(idx * 360/25), 255, 255))


        rng = QgsRendererRange(low, high, sym, label)

        ranges.append(rng)


    renderer = QgsGraduatedSymbolRenderer("VALUE", ranges)

    renderer.setMode(QgsGraduatedSymbolRenderer.Custom)

    vl.setRenderer(renderer)


    # ----------------------------

    # 7. Labels

    # ----------------------------

    label = QgsPalLayerSettings()

    label.fieldName = "LEVEL"

    label.enabled = True


    vl.setLabelsEnabled(True)

    vl.setLabeling(QgsVectorLayerSimpleLabeling(label))


    QgsProject.instance().addMapLayer(vl)

    print("🎉 全部完成:25 等級等值面 + 標籤 + 上色 已加入 QGIS")


    QGIS Raster to Vector use Python console step1

    處理步驟
    1.從顏色分類的 RGB 影像中取得所有像素

    2. 過濾白色背景區域

    3. 使用 K-Means RGB Centroid 進行色彩匹配

    4. 將像素映射為指定的 數值分類值

    5. 產生可用於 QGIS、ArcGIS、Python 的 GeoTIFF 數值影像

    6. 完整設定 NoData、座標系、像元大小



     import numpy as np

    from osgeo import gdal, gdalconst


    # ---------------------------------------------------

    # 1. 路徑設定

    # ---------------------------------------------------

    input_path = r"D:\download\87ad4ea7-158c-4f27-9911-57fa9c1ebb472_modified.tif"

    output_path = r"D:\download\value.tif"


    # ---------------------------------------------------

    # 2. 讀取 RGB 影像

    # ---------------------------------------------------

    ds = gdal.Open(input_path, gdalconst.GA_ReadOnly)

    if ds is None:

        raise Exception("❌ 無法開啟輸入 TIFF")


    R = ds.GetRasterBand(1).ReadAsArray().astype(np.float32)

    G = ds.GetRasterBand(2).ReadAsArray().astype(np.float32)

    B = ds.GetRasterBand(3).ReadAsArray().astype(np.float32)


    rows, cols = R.shape

    pixels = np.stack([R.flatten(), G.flatten(), B.flatten()], axis=1)


    # ---------------------------------------------------

    # 3. 白色背景過濾(非常重要)

    # ---------------------------------------------------

    # 白底 = RGB 皆 > 225

    bg_mask = (pixels[:,0] > 225) & (pixels[:,1] > 225) & (pixels[:,2] > 225)


    # 非背景像素

    non_bg_pixels = pixels[~bg_mask]


    print("原始像素:", len(pixels))

    print("非背景像素(有效區域):", len(non_bg_pixels))


    # ---------------------------------------------------

    # 4. 使用 K-Means 得到的 RGB centroid

    # ---------------------------------------------------

    centroids = np.array([

        [117, 249,  20],   # 群 0 → 32.5

        [237,  28,  64],   # 群 1 → 45

        [148, 202, 235],   # 群 2 → 10

        [231, 224,  30],   # 群 3 → 37.5

        [ 19,  24,  20],   # 群 4 → NoData

        [ 56, 245, 161],   # 群 5 → 27.5

        [102,  97, 227],   # 群 6 → 22.5

    ], dtype=np.float32)


    mapping = {

        0: 32.5,

        1: 45,

        2: 10,

        3: 37.5,

        4: -9999,

        5: 27.5,

        6: 22.5

    }


    # ---------------------------------------------------

    # 5. 計算 pixel → 最近 centroid

    # ---------------------------------------------------

    dist = ((pixels[:, None, :] - centroids[None, :, :]) ** 2).sum(axis=2)

    cluster_idx = np.argmin(dist, axis=1)


    # ---------------------------------------------------

    # 6. 對背景像素直接指定為 -9999

    # ---------------------------------------------------

    value_arr = np.vectorize(mapping.get)(cluster_idx)

    value_arr[bg_mask] = -9999


    value_arr = value_arr.reshape(rows, cols).astype(np.float32)


    # ---------------------------------------------------

    # 7. 輸出 GeoTIFF

    # ---------------------------------------------------

    driver = gdal.GetDriverByName("GTiff")

    out_ds = driver.Create(output_path, cols, rows, 1, gdalconst.GDT_Float32)


    out_ds.SetGeoTransform(ds.GetGeoTransform())

    out_ds.SetProjection(ds.GetProjection())


    band = out_ds.GetRasterBand(1)

    band.WriteArray(value_arr)

    band.SetNoDataValue(-9999)

    band.FlushCache()


    del out_ds

    print("🎉 已完成:輸出數值影像 →", output_path)

    2024-01-23

    Q80 和 Q90

     
    "Q80" 和 "Q90" 是與水位(水流量)相關的術語,通常用於水文學和水資源管理領域。這些術語涉及在不同的情境下對水位和水流量進行推估和應用。


    1. Q80 和 Q90 定義:

       - Q80:在某段特定的時間內,80% 的時間水位處於或高於某個特定的水位。換句話說,Q80 表示水位的80百分位數,即超過20% 的時間水位會低於該值。

       - Q90:在某段特定的時間內,90% 的時間水位處於或高於某個特定的水位。換句話說,Q90 表示水位的90百分位數,即超過10% 的時間水位會低於該值。


    2. 水位推估與應用:

       - 洪水風險評估:Q80 和 Q90 可用於評估洪水風險。例如,如果一個地區的水位在大部分時間都低於 Q90 水位,那麼這個地區可能會更容易受到洪水的影響。

       - 水資源管理:水位的變化會影響水資源的供應和分配。透過監測和預測 Q80 和 Q90 水位,可以更好地規劃用水,確保水資源的可持續利用。

       -生態保護:對於生態系統而言,水位的變化可能對動植物的生存和繁殖造成影響。通過了解 Q80 和 Q90 水位,可以更好地管理和保護生態環境。

       - 基礎建設規劃:在設計和建設水利基礎設施(如水壩、防洪工程等)時,需要考慮不同水位條件。Q80 和 Q90 可以作為設計參考,確保基礎建設能夠應對各種水位情況。

       - 災害應對 :在面臨極端天氣事件(如暴雨、颱風等)時,了解 Q80 和 Q90 水位可以幫助當局做出更快速和準確的應對措施,以減少潛在的災害風險。

    Q80 和 Q90 水位是水文學中重要的指標,對於洪水風險評估、水資源管理、生態保護以及基礎建設等方面。

    2022-06-10

    近即時的全球土地利用與覆蓋10m圖資

     Dynamic World, Near real-time global 10 m land use land cover mapping

    https://www.nature.com/articles/s41597-022-01307-4#ref-CR23 近即時的全球土地利用與覆蓋10m圖資 透過開放遙測衛星 Sentinel-2 L1C 搭配 GEE 與labelbox.com等技術 產製全球近即時的土地利用與覆蓋圖資 專家標記約 4,000筆資料 非專家標記約 20,000筆資料 #NRT Near real-time 近即時 #LULC land use land cover 土地利用土地覆蓋 在 #Zenodo 存放專家資料集 在 #PANGEA 存放非專家資料集 GEE 資料集 標註資料: ee.ImageCollection('projects/wri-datalab/dynamic_world/v1/DW_LABELS') 影像: ee.ImageCollection('GOOGLE/DYNAMICWORLD/V1') 範例code: https://code.earthengine.google.com/710e2ae9d03cd994c6e8dc9213257cbc 相關訓練資料與程式 https://doi.org/10.5281/zenodo.5602141 #土地利用 #土地覆蓋 #DynamicWorld #EarthEngine

    2022-03-22

    土壤含水量SMAP10KM

     
    土壤含水量 在進行乾旱評估中一個重要的參考指標

    利用 GEE SMAP 10KM 土壤含水量資料繪製

    特定時間段土壤含水量資訊


    var dataset = ee.ImageCollection('NASA_USDA/HSL/SMAP10KM_soil_moisture')

                      .filter(ee.Filter.date('2021-01-01', '2022-01-01'));

    var soilMoisture = dataset.select('ssm');

    var soilMoistureVis = {

      min: 0.0,

      max: 28.0,

      palette: ['0300ff', '418504', 'efff07', 'efff07', 'ff0303'],

    };

    Map.setCenter(120.5, 23.529, 10

    );

    Map.addLayer(soilMoisture, soilMoistureVis, 'Soil Moisture');





    2021-11-06

    走讀

     

    以前國小上課與回家的途中會經過農改場農田與渠道 農田旁的溝渠還可以看到螃蟹和魚 隨著都市化農田灌溉渠道慢慢的消失在生活中 排水內的水質也慢慢地由透明清澈 轉變成灰色或混濁的情況 而現在看到的大多是三面光排水 與適應力超強的吳郭魚或外來魚種 與越來越多以人為思考點的構造物 --- 最近幾年慢慢地越來越多人與團體與政府部門 開始關心所在的生活環境 花了很大的力氣才能把先前的環境稍微復原 但稍微一不注意 就又會造成另外一個外來種生態入侵問題 生態與環境破壞後 要再復原所花的心力與成本 會比原本更高更困難 關心可以由自己做起 在生活環境中盡量減少一次性的塑膠垃圾 由小開始培養關心環境相關的議題與政策

    QGIS 圖資自動輸出與簡報產製流程說明

      QGIS 圖資自動輸出與簡報產製流程說明 一、目的說明 本流程主要目的,是將既有的 GIS 圖資與分析成果,透過 QGIS 專案與版面配置設定,自動輸出為圖片,並依序套用到簡報檔案中,減少人工截圖、貼圖與排版時間。 透過此方式,可快速產製多個行政區、鄉鎮、市區或指定範圍的成果...