基盤地図情報 DEMデータ作成

土木・建設分野に関連する仕事や研究を行われている方は、
基盤地図情報ダウンロードサービスhttps://service.gsi.go.jp/kiban/app/
から数値標高モデルをダウンロードし、地形図(DEMデータ)を作成することが多いと思います。

今回私の好きな福岡県朝倉市を例に、ダウンロードデータを処理し、geotiffデータに変換するPythonコードを紹介します
QGIS等で活用できるデータですので、ご参考になれば幸いです。

1.データダウンロード

まずは、上記のサイトからデータをダウンロードします。
今回は航空レーザ測量で得られた5mメッシュの地形データを入手します。
基盤地図情報でデータをダウンロードするためには、会員登録(ログイン)が必須ですので、登録の上、進めて下さい。

ダウンロードしたデータはすべて展開し、一つのフォルダに保存してください
(中々面倒な作業ですので、こちらもPythonで自動化できるかもしれません)

2.geotiffデータ作成

さっそくpythonで以下のコードに作業ディレクトリを追加し、実行してください。
(pythonの環境構築については、今後ご紹介します。)
実行すると、出力フォルダに画像のような結果が出力されます!
**注意** 試行中のコードですので、あくまで自己責任のもと参考にしていただければと思います。

# -*- coding: utf-8 -*-
import re
import numpy as np
from osgeo import gdal
gdal.UseExceptions()  # 例外を有効にする
from os.path import join,relpath
from glob import glob

def main():
    #XMLを格納するフォルダ
    #path = "C:\\●●\\●●\\●●"  #ここにディレクトリを入れてください!
    #path = "C:\\●●\\●●\\●●"  #ここにディレクトリを入れてください!
    path = "C:\\●●\\●●\\●●"  #ここにディレクトリを入れてください!
    #GeoTiffを出力するフォルダ
    geopath = "C:\\●●\\●●\\●●"  #ここにディレクトリを入れてください!
    #ファイル名取得
    files = [relpath(x,path) for x in glob(join(path,'*'))]

    for fl in files:
        xmlFile = join(path,fl)
        #XMLを開く
        with open(xmlFile, "r", encoding = "utf-8") as f:
            r = re.compile("<gml:lowerCorner>(.+) (.+)</gml:lowerCorner>")
            for ln in f:
                m = r.search(ln)
                #検索パターンとマッチした場合、スタートポジションを格納
                if m != None:
                    lry = float2(m.group(1))
                    ulx = float2(m.group(2))
                    break

            r = re.compile("<gml:upperCorner>(.+) (.+)</gml:upperCorner>")

            for ln in f:
                m = r.search(ln)

                #検索パターンとマッチした場合、スタートポジションを格納
                if m != None:
                    uly = float2(m.group(1))
                    lrx = float2(m.group(2))
                    break


            # 検索パターンをコンパイル
            r = re.compile("<gml:high>(.+) (.+)</gml:high>")
            for ln in f:
                m = r.search(ln)
                #検索パターンとマッチした場合、縦横の領域を格納
                if m != None:
                    xlen = int(m.group(1)) + 1
                    ylen = int(m.group(2)) + 1
                    break

            startx = starty = 0

            # 検索パターンをコンパイル
            r = re.compile("<gml:startPoint>(.+) (.+)</gml:startPoint>")
            for ln in f:
                m = r.search(ln)
                #検索パターンとマッチした場合、スタートポジションを格納
                if m != None:
                    startx = int(m.group(1))
                    starty = int(m.group(2))
                    break

        #numpy用にデータを格納しておく
        with open(xmlFile, "r", encoding = "utf-8") as f:
            src_document = f.read()
            lines = src_document.split("\n")
            num_lines = len(lines)
            l1 = None
            l2 = None
            for i in range(num_lines):
                if lines[i].find("<gml:tupleList>") != -1:
                    l1 = i + 1
                    break
            for i in range(num_lines - 1, -1, -1):
                if lines[i].find("</gml:tupleList>") != -1:
                    l2 = i - 1
                    break

        #セルのサイズを算出
        psize_x = (lrx - ulx) / xlen
        psize_y = (lry - uly) / ylen

        geotransform = [ulx, psize_x, 0, uly, 0, psize_y]
        create_options = [] #空のままでOKっぽい
        driver = gdal.GetDriverByName("GTiff")

        #拡張子を変更する(ファイル名はそのまま)
        dst = fl.replace('.xml', '.tif')
        tiffFile = join(geopath,dst)
        dst_ds = driver.Create(tiffFile, xlen, ylen, 1, gdal.GDT_Float32, create_options)

        dst_ds.SetProjection('GEOGCS["JGD2000",DATUM["Japanese_Geodetic_Datum_2000",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6612"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4612"]]')
        dst_ds.SetGeoTransform(geotransform)
        rband = dst_ds.GetRasterBand(1)

        narray = np.empty((ylen, xlen), np.float32)
        narray.fill(0)

        num_tuples = l2 - l1 + 1

        #スタートポジションを算出
        start_pos = starty*xlen + startx

        i = 0
        sx = startx

        #標高を格納
        for y in range(starty, ylen):
            for x in range(sx, xlen):
                if i < num_tuples:
                    vals = lines[i + l1].split(",")
                    if len(vals) == 2 and vals[1].find("-99") == -1:
                        narray[y][x] = float(vals[1])
                    i += 1
                else:
                    break
            if i == num_tuples: break
            sx = 0

        rband.WriteRaster(0, 0, xlen, ylen, narray.tobytes())
        dst_ds.FlushCache()

def float2(str):
    lc = ""
    for i in range(len(str)):
        c = str[i]
        if c == lc:
            renzoku += 1
            if renzoku == 6:
                return float(str[:i+1] + c * 10)
        else:
            lc = c
            renzoku = 1
        return float(str)

if __name__ == "__main__":
    main()

3.結合

次に結合させます。
こちらもpythonで実行しましたが、今回は結果のみご紹介
QGISのツールを使ってもできるので、やってみてください。
結合したgeotiffをQGISに出力すると画像のような感じに!

4.まとめ

このような形で、少し気楽にgeotiffデータを作成できます。
今回はあまり詳しくご紹介できていませんが、geotiffを作成するコードの紹介がメインということでご了承ください。(不備があったら申し訳ございません。)
少しずつ、gis関連で紹介していけたらと思います。

タイトルとURLをコピーしました