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

    走讀

     

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

    2020-09-06

     Google 洪水預報團隊進度 20200903


    Google 洪水預報團隊原先與印度政府合作透過手機,提供洪水預報資訊 。

    今年度 擴大預報範圍以涵蓋在印度與孟加拉 (河川水預報區域涵蓋7500萬人),同時預測時間長度增加為一天 (讓民眾可以有更充足的應變時間)。
    並透過與耶魯大學合作,了解當地民眾對於洪水災害資訊需求, 進行使用者介面調整。
    同時與當地紅十字會合作進行災害資訊的傳遞,
    將洪水訊息提供給沒有手機的居民。

    (連結1)



    而在淹水預報的技術上

    首先改善河川水位預報準確度,並且發展HydroNets模式。使用在ML技術中的LSTM 方式進行推估。
    概念上將每一流域點位視為較小的類神經網路架構,依照水系與集水區架構不斷串接成為大型網路架構。
    每一個結點可以輸出水位與損失函數,在依序傳遞下去。

    目前在預報流域集水區的預報水位,以預報水位90cm 為例,對應預報水位誤差小於15cm。

    當水位流量資料準確後。接著就是將河川水位預報轉換為淹水模式成果上。

    發展“形態淹水模型(morphological inundation model)”,該方法,使用物理建模與機器學習(ML)相互結合,以提供大範圍淹水成果,並且可保持一定準確度。

    改善
    1.複雜演算的問題
    (大面積範圍且需要具有一定空間解析度的需求)
    2.河床水面與水體下,對應的斷面資訊
    (模式要準,水面下斷面資料需要掌握)
    3.現有資料錯誤問題
    (監測資料、圖資內容有問題或圖徵缺少不完整)

    先透過非物理模式的ML推估 水位變化下可能淹水的影響範圍(斷面水位變化影響範圍)搭配圖學常用的Flood fill演算法為基礎,搭配河川流量與衛星遙測偵測的淹水範圍進行比對。以改善模式預測準確度。

    但資料不足的地方上述方法仍有一定難度。因此而在形態淹水模型尚未涵蓋區域,則利用 end-to-end ML-based 使用水位資料、衛星資料 低水位時圖資進行可能淹水範圍推估。

    連結1
    Google擴大印度與孟加拉的洪水預報範圍
    https://blog.google/technology/ai/flood-forecasts-india-bangladesh/

    連結2
    Google 改進洪水預報技術提升大範圍空間準確度
    https://ai.googleblog.com/2020/09/the-technology-behind-our-recent.html

    論文1
    HydroNets論文
    https://ai4earthscience.github.io/iclr-2020-workshop/papers/ai4earth04.pdf

    論文2
    HydroNets參考的LSTM 論文
    https://hess.copernicus.org/articles/23/5089/2019/

    #Google
    #洪水預報發展
    #HydroNets
    #水文模式
    #淹水模式
    #ML
    #衛星遙測
    #淹水

    QGIS Raster to Vector use Python console step2

    資料處理步驟: 1.讀取 Raster 並計算 25 等級等值線級距 2.用 GDAL 產生等值線(Contour Lines) 3.Polygonize:Raster 轉等值面多邊形 4.將多邊形圖層載入 QGIS 5.為每個 polygon 計算其 VALUE 所屬級距(LE...