Skip to content

Instantly share code, notes, and snippets.

@minorua
Last active May 17, 2021 04:49
Show Gist options
  • Save minorua/4993166 to your computer and use it in GitHub Desktop.
Save minorua/4993166 to your computer and use it in GitHub Desktop.
translates DEM files of Fundamental Geospatial Data provided by GSI into a GDAL supported format (which has write access).
#!/usr/bin/env python3
# name : fgddem.py
# purpose : translates digital elevation model of Fundamental Geospatial Data provided by GSI
# into a GDAL supported format (which has write access).
# license : The MIT License
# copyright : (c) 2012, Minoru Akagi
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
script_version = "0.9"
import sys, os
import datetime
import glob
import numpy
import re
import shutil
from xml.dom import minidom
import zipfile
from osgeo import gdal
try:
progress = gdal.TermProgress_nocb
except:
progress = gdal.TermProgress
flush = sys.stdout.flush
verbose = 0
quiet = 0
USE_GDALWARP = True
def translate_jpgis_gml(text, dest_file, driver, create_options=None, replace_nodata_by_zero=False):
"""translates JPGIS (GML) DEM file into a GDAL supported format (write access)."""
create_options = create_options or []
# split document into 3 parts - header, body (tuple list) and footer
header, body = text.split("<gml:tupleList>", 1)
body, footer = body.rsplit("</gml:tupleList>", 1)
hf = header + footer
# parse xml
doc = minidom.parseString(hf)
lowerCorner = doc.getElementsByTagName("gml:lowerCorner")[0].childNodes[0].data.split(" ")
upperCorner = doc.getElementsByTagName("gml:upperCorner")[0].childNodes[0].data.split(" ")
lry = float2(lowerCorner[0])
ulx = float2(lowerCorner[1])
uly = float2(upperCorner[0])
lrx = float2(upperCorner[1])
high = doc.getElementsByTagName("gml:high")[0].childNodes[0].data.split(" ")
xsize = int(high[0]) + 1
ysize = int(high[1]) + 1
startPoint = doc.getElementsByTagName("gml:startPoint")[0].childNodes[0].data.split(" ")
startX = int(startPoint[0])
startY = int(startPoint[1])
psize_x = (lrx - ulx) / xsize
psize_y = (lry - uly) / ysize
geotransform = [ulx, psize_x, 0, uly, 0, psize_y]
# create a new dataset
dst_ds = driver.Create(dest_file, xsize, ysize, 1, gdal.GDT_Float32, create_options)
if dst_ds is None:
return "Cannot create file: " + dest_file
# WKT is from: http://epsg.io/6668
dst_ds.SetProjection('GEOGCS["GRS 1980(IUGG, 1980)",DATUM["unknown",SPHEROID["GRS80",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["epsg","6668"]]')
dst_ds.SetGeoTransform(geotransform)
rband = dst_ds.GetRasterBand(1)
if replace_nodata_by_zero:
nodata_value = 0.
else:
nodata_value = -9999.
rband.SetNoDataValue(nodata_value)
# create an array initialized with nodata value
narray = numpy.empty((ysize, xsize), numpy.float32)
narray.fill(nodata_value)
# read tuple list
tuples = body.strip().split("\n") # no problem with \r character at line end: float("0.01\r") = 0.01
num_tuples = len(tuples)
i = 0
sx = startX
for y in range(startY, ysize):
for x in range(sx, xsize):
if i < num_tuples:
vals = tuples[i].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
# write array
rband.WriteRaster(0, 0, xsize, ysize, narray.tostring())
# make sure that all data have been written
dst_ds.FlushCache()
if verbose:
print("file: %s" % dest_file)
print("name: %s" % doc.getElementsByTagName("gml:name")[0].childNodes[0].data)
print("fid : %s" % doc.getElementsByTagName("fid")[0].childNodes[0].data)
print("type: %s" % doc.getElementsByTagName("type")[0].childNodes[0].data)
print("mesh: %s" % doc.getElementsByTagName("mesh")[0].childNodes[0].data)
print("bounds : %f, %f - %f, %f" % (lry, ulx, uly, lrx))
print("cell size : %f, %f" % (psize_x, psize_y))
print("size : %d, %d" % (xsize, ysize))
print("start point : %d, %d\n" % (startX, startY))
return 0
def translate_zip(src_file, dst_file, driver, create_options=None, replace_nodata_by_zero=False):
if not os.path.isfile(src_file):
return "Source is not a file: " + src_file
# create temporary directory
temp_dir = os.path.splitext(dst_file)[0] + "_temp" + datetime.datetime.today().strftime("%Y%m%d%H%M%S")
os.makedirs(temp_dir)
# open zip file and translate xml files
zf = zipfile.ZipFile(src_file, mode="r")
namelist = zf.namelist()
demlist = []
if not quiet and not verbose:
progress(0.0)
for i, name in enumerate(namelist):
if name[-4:].lower() == ".xml" and not "meta" in name:
tif_name = os.path.join(temp_dir, os.path.basename(name) + ".tif")
with zf.open(name) as f:
translate_jpgis_gml(f.read().decode("utf-8"), tif_name, driver, create_options, replace_nodata_by_zero)
demlist.append(tif_name)
if not quiet and not verbose:
progress((i + 1.) / len(namelist))
zf.close()
if len(demlist) == 0:
return "Zip file includes no xml file: " + src_file
if len(demlist) == 1:
os.rename(demlist[0], dst_file)
else:
# create merge command
gdal_merge_options = ""
gdalwarp_options = ""
gdal_merge_ext = ""
if os.name != "nt": # nt: windows
gdal_merge_ext = ".py"
if quiet:
gdal_merge_options += " -q"
if not verbose:
gdalwarp_options += " -q"
if not replace_nodata_by_zero:
gdal_merge_options += " -a_nodata -9999"
gdalwarp_options += " -dstnodata -9999"
re_non_ascii = re.compile(r"[^\x20-\x7E]")
if not USE_GDALWARP and re_non_ascii.search(src_file + dst_file) is None:
# write demlist into a file
demlist_filename = os.path.join(temp_dir, 'demlist.txt')
with open(demlist_filename, 'w') as f:
f.write("\n".join(demlist))
f.write("\n")
merge_command = 'gdal_merge%s%s -o "%s" --optfile "%s"' % (gdal_merge_ext,
gdal_merge_options,
dst_file,
demlist_filename)
# TODO: testing in Linux
# Wildcards cannot be used for arguments now. See http://trac.osgeo.org/gdal/ticket/4542 (2012/04/08)
else:
merge_command = 'gdalwarp%s "%s" "%s"' % (gdalwarp_options,
os.path.join(temp_dir, "*.tif"),
dst_file)
# do merge
if not quiet:
print("merging")
flush()
if verbose:
print("execute %s" % merge_command)
os.system(merge_command)
# remove temporary directory
shutil.rmtree(temp_dir)
if not quiet:
print("temporary files removed\n")
flush()
return 0
def unzip(src_file, dest=None):
if not os.path.isfile(src_file):
return False
if dest is None:
dest = os.path.splitext(src_file)[0]
zf = zipfile.ZipFile(src_file, mode="r")
zf.extractall(dest)
zf.close()
if verbose:
print("unzipped : %s" % dest)
return True
def Usage():
print("=== Usage ===")
print("python fgddem.py [-replace_nodata_by_zero] [-f format] [-out_dir output_directory] [-q] [-v] src_files*\n")
print("src_files: The source file name(s). JPGIS(GML) DEM zip/xml files.")
return 0
def main(argv=None):
global verbose, quiet
format = "GTiff"
filenames = []
out_dir = ""
replace_nodata_by_zero = False
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO")
os.putenv("GDAL_FILENAME_IS_UTF8", "NO") # for merging process
# Parse command line arguments.
i = 1
while i < len(argv):
arg = argv[i]
if arg == "-replace_nodata_by_zero":
replace_nodata_by_zero = True
elif arg == "-f":
i += 1
format = argv[i]
elif arg == "-out_dir":
i += 1
out_dir = argv[i]
elif arg == "-v":
verbose = 1
elif arg == "-q":
quiet = 1
elif arg == "-help" or arg == "--help":
Usage()
return 0
elif arg == "-version":
print("fgddem.py version {}".format(script_version))
return 0
elif arg[:1] == "-":
return "Unrecognised command option: %s" % arg
else:
# expand wildcards
f = glob.glob(arg)
if len(f) == 0:
sys.stderr.write("File not found: %s\n" % arg)
filenames += f
i = i + 1
if len(filenames) == 0:
return "No input files selected"
# create output directory
if out_dir and os.path.exists(out_dir) == False:
os.makedirs(out_dir)
if verbose:
print("Directory has been created: %s" % out_dir)
# get gdal driver
driver = gdal.GetDriverByName(format)
if driver is None:
return "Driver %s not found" % format
dst_ext = "." + (driver.GetMetadataItem(gdal.DMD_EXTENSIONS) or "unknown").split(" ")[0]
err_count = 0
for i, src_file in enumerate(filenames):
if not quiet:
if len(filenames) > 1:
print("(%d/%d): translating %s" % (i+1, len(filenames), src_file))
else:
print("translating " + src_file)
flush()
if out_dir:
filetitle, ext = os.path.splitext(os.path.basename(src_file))
dst_root = os.path.join(out_dir, filetitle)
else:
src_root, ext = os.path.splitext(src_file)
dst_root = src_root
ext = ext.lower()
dst_file = dst_root + dst_ext
# translate zip/xml file
err = 0
if ext == ".zip":
err = translate_zip(src_file, dst_file, driver, [], replace_nodata_by_zero)
elif ext == ".xml" and not "meta" in src_file:
with open(src_file, "r", encoding="utf-8") as f:
err = translate_jpgis_gml(f.read(), dst_file, driver, [], replace_nodata_by_zero)
else:
err = "Not supported file: %s" % src_file
if err:
sys.stderr.write(err + "\n")
err_count += 1
if not quiet and err_count == 0:
print("completed")
return 0
def float2(val, min_repeat=6):
"""Increase number of decimal places of a repeating decimal.
e.g. 34.111111 -> 34.1111111111111111"""
repeat = 0
lc = ""
for i in range(len(val)):
c = val[i]
if c == lc:
repeat += 1
if repeat == min_repeat:
return float(val[:i+1] + c * 10)
else:
lc = c
repeat = 1
return float(val)
if __name__ == "__main__":
err = main(sys.argv)
if err:
sys.stderr.write(err + "\n")
Usage()
sys.exit(1)
sys.exit(0)
@echo off
SET OSGEO4W_ROOT=C:\OSGeo4W64
call %OSGEO4W_ROOT%\bin\o4w_env.bat
rem Python 3.x (need to check python version)
SET PYTHONHOME=%OSGEO4W_ROOT%\apps\Python37
PATH %PYTHONHOME%\Scripts;%PATH%
rem Qt 5
rem PATH %OSGEO4W_ROOT%\apps\Qt5\bin;%PATH%
rem set QT_PLUGIN_PATH=%OSGEO4W_ROOT%\apps\Qt5\plugins
echo on
python3 "%~dp0fgddem.py" %*
pause

fgddem.py

概要

国土地理院の基盤地図情報 数値標高モデル JPGIS(GML)形式をGeoTIFFに変換するPythonスクリプト。

基盤地図情報サイト: http://www.gsi.go.jp/kiban/index.html

動作環境

WindowsではOSGeo4Wでpython3-gdalパッケージをインストールしておく。 Linuxではpython3-gdalパッケージをインストールしておく。

インストール・操作方法

  • バッチファイルへのドラッグ&ドロップによる方法(Windowsのみ)
  1. fgddem.py.droptarget.batをテキストエディタで開き,2行目のOSGEO4W_ROOTの値をOSGeo4Wがインストールされているフォルダパスに変更する。
  2. 国土地理院のダウンロードサイトからダウンロードしたZIPファイルをfgddem.py.droptarget.batにドラッグ&ドロップする。

コマンドの説明

fgddem.py [-replace_nodata_by_zero][-out_dir output_directory] src_files

GeoTIFFファイルは入力ファイルと同じファイルタイトルで出力される。

  • src_files:

JPGIS(GML)形式のXMLファイルまたはそれを含むZIPファイル。ZIPファイルを指定した時は含まれるメッシュがすべて結合されて出力される。複数指定も可能で,ワイルドカードを用いた場合マッチするファイル毎にGeoTIFFファイルが出力される。

  • -replace_nodata_by_zero:

このオプションを指定した場合,「データなし」の値が0に置き換えられます。「データなし」の値は設定されません。 このオプションを指定しなければ「データなし」として-9999が設定されます。

  • -out_dir:

出力先フォルダ。指定がないときは入力ファイルと同じフォルダに出力される。

更新履歴

  • 2019/01/01 Version 0.9 マージにgdalwarpを使用 (gdal_merge.pyはOSGeo4Wのpython3-gdalパッケージに含まれない)。
  • 2018/11/03 Version 0.8 Python 3に移行。
  • 2016/05/21 Version 0.7 ライセンスをMITに変更。コードクリーニング等。
  • 2014/08/17 Version 0.6 xmlファイルが1つのときはマージを行わない。
  • 2014/08/01 Version 0.5 コードの整理。
  • 2014/01/28 Version 0.4 実行終了時に生じることのあったエラーを修正。
  • 2013/12/15 Version 0.3 基盤地図情報DEMデータファイルの更新に対応。コードの整理。
  • 2012/12/18 Version 0.2 非ASCII文字を含むファイルパスのサポート。
  • 2012/06/11 Version 0.1 データ読込時間短縮。fgddem_droptarget.batに対する複数ファイルのドラッグ&ドロップ。

ベータ版

  • 2012/03/10 公開。

Copyright (c) 2013, Minoru Akagi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment