国土数値情報 ダウンロードサービスの利用

穴の開いた浮き出し地図を作る(xml)

 東京都の武蔵村山市の南西部です。よく見ると、南西の方角に四角い穴が開いています。ここは隣の瑞穂町の飛び地です。実際には横田基地の構内で、一般者には縁遠い場所です。ちなみに、この記事を書いている時点で東京都で穴が開いているのは小金井市(中に府中市)、武蔵村山市(中に瑞穂町)、町田市(中に相模原市)の3つです。

 浮き出し地図では、xmlの面定義部分でexteriorの項目だけを使い、外周だけを図形にしていました。

 ご利用の目的によっては、穴が開いているのも再現したいという場合もあるでしょうから、穴をあけてみました。そろそろお気づきでしょうが、何か機能を加えると、スクリプトがどんどん長くなります。ここに生pyファイルがあります。

#!BPY
import bpy
import math
from xml.etree.ElementTree import ElementTree
import glob
#
# 穴の開いた浮き出し地図を作る(xml)
#       main003.py
#       2016/11/17 Shizuka-Toikanbetsu
#
XmlName = "N03-??_13_*.xml"     # 収録されているxmlファイルを指定
AreaCode = "13223"      # 地方公共団体コードを文字列型で指定
ObjScale = 0.001        # 原寸では大きいので1000分の1
#
# 世界座標の基準緯度と世界座標の目標座標の距離をメートル単位で返す
def     GetLatOffset(pBaseLat, pLat):
        return ( pLat - pBaseLat ) * 40007000.0 / 360.0
#
# 世界座標の基準経度と世界座標の目標座標の距離をメートル単位で返す
def     GetLonOffset(pBaseLon, pLat, pLon):
        return ( pLon - pBaseLon ) * 40075000.0 * math.cos( math.radians( pLat ) ) / 360.0
#
# namespace
ns = { "ksj": "http://nlftp.mlit.go.jp/ksj/schemas/ksj-app",
                "gml": "http://www.opengis.net/gml/3.2",
                "xlink": "http://www.w3.org/1999/xlink" }
#
def     CreateRecName(pAreaCode, pAdministrativeBoundary):      # オブジェクト名を作る
        RecName = pAreaCode
        tempstr = pAdministrativeBoundary.find( "ksj:prefectureName", ns ).text
        if tempstr:
                RecName = RecName  + "_" + tempstr
        tempstr = pAdministrativeBoundary.find( "ksj:countyName", ns ).text
        if tempstr:
                RecName = RecName  + "_" + tempstr
        tempstr = AdministrativeBoundary.find( "ksj:cityName", ns ).text
        if tempstr:
                RecName = RecName  + "_" + tempstr
        return RecName
#
def     MergeRecName():
        Buff = []
        for item in bpy.data.objects:
                if item.type == "MESH":
                        temp = item.name
                        Buff.append( temp[ 0 : 5 ] )
        Buff.sort()
        AreaCode = []
        OldCode = ""
        for temp in Buff:
                if temp != OldCode:
                        AreaCode.append( temp )
                OldCode = temp
        #
        for Code in AreaCode:
                bpy.ops.object.select_all(action='DESELECT')
                flag = True
                for curve in bpy.data.objects:
                        if curve.type == 'MESH':
                                NowName = curve.name
                                NowCode = NowName[:5]
                                if NowCode == Code:
                                        if flag:
                                                RecName = NowName
                                                flag = False
                                        curve.select = True
                                        bpy.context.scene.objects.active = curve
                bpy.ops.object.join()
                bpy.ops.object.select_all(action='DESELECT')
        # 小数点の付く名前は書き換える
        for item in bpy.data.objects:
                if item.type == "MESH":
                        temp = item.name
                        try:
                                item.name = temp[ 0 : temp.index( "." ) ]
                        except ValueError:
                                pass
        #
        bpy.context.scene.objects.active = None
#
def     CreateSegVert(pText):
        LineData = pText.split( "\n" )
        XY = []
        for LineStr in LineData:
                if LineStr:
                        buff = []
                        temp = LineStr.lstrip( " \t" )
                        if len( temp ):
                                temp = temp.split( " " )
                                dy = float( temp[ 0 ] )
                                dx = float( temp[ 1 ] )
                                buff.append( dx )
                                buff.append( dy )
                                XY.append( buff )
        btm = len( XY )
        if btm:
                btm = btm - 1
                if XY[ 0 ] == XY[ btm ]:
                        del XY[ btm ]
        return XY
#
def     CreateCurve(pName, pTree, pCurveID, pInterior):
        xpath = ".//*/..[@gml:id='" + pCurveID + "']"
        Curve = pTree.find( xpath, ns )
        posList = Curve.find( './/gml:posList', ns )
        verts = CreateSegVert( posList.text )   # セグメント用の座標点リスト
        #
        CvData = bpy.data.curves.new( name = pName, type = "CURVE" )
        CvData.dimensions = "2D"
        ObjData = bpy.data.objects.new( pName, CvData )
        if pInterior:
                ObjData.location = ( 0, 0, 1 )
        else:
                ObjData.location = ( 0, 0, 0 )
        ObjData.scale[0] = ObjScale
        ObjData.scale[1] = ObjScale
        ObjData.scale[2] = ObjScale
        bpy.context.scene.objects.link( ObjData )
        polyline = CvData.splines.new( "POLY" )
        #
        Count = len( verts )
        polyline.points.add( Count - 1 )
        for ptr in range( 0, Count ):
                posX = verts[ ptr ][ 0 ]
                posY = verts[ ptr ][ 1 ]
                posX = GetLonOffset( wx, posY, posX )
                posY = GetLatOffset( wy, posY )
                Point = polyline.points[ptr]
                Point.co = ( posX, posY, 0, 0 )
        polyline.use_cyclic_u = True
        ObjData.select = True
        if pInterior:
                bpy.context.scene.objects.active = ObjData
                bpy.ops.object.modifier_add(type='SOLIDIFY')    # 厚み付け
                bpy.context.object.modifiers["Solidify"].thickness = 5.0 / ObjScale
        # カーブをメッシュオブジェクトに変換
        bpy.context.scene.objects.active = ObjData
        bpy.ops.object.convert( target = "MESH" )
        ObjData.select = False
        return ObjData
#
print( '\nStart Script' )
#
# 既存メッシュの削除
for item in bpy.context.scene.objects:
        if item.type == "MESH":
                bpy.context.scene.objects.unlink( item )
for item in bpy.data.objects:
        if item.type == "MESH":
                bpy.data.objects.remove( item )
for item in bpy.data.meshes:
        bpy.data.meshes.remove( item )
#
files = glob.glob( XmlName )
if files:
        file = files[ 0 ]       # きりがないので処理は1ファイルに限る
        tree = ElementTree()
        tree.parse( file )
        # 事前にセンターの位置を割り出す
        Xmin = 100000000.0
        Ymin = Xmin
        Xmax = -Xmin
        Ymax = Xmax
        #
        xpath = ".//*/..[ksj:administrativeAreaCode='" + AreaCode + "']"
        for AdministrativeBoundary in tree.findall( xpath, ns ):
                # 面の定義を探す
                bounds = AdministrativeBoundary.find( 'ksj:bounds', ns )
                href = bounds.get('{http://www.w3.org/1999/xlink}href')
                NowSf = href.lstrip('#')
                # 面の定義情報からカーブを取り出す
                xpath = ".//*/..[@gml:id='" + NowSf + "']"
                Surface = tree.find( xpath, ns )
                exterior = Surface.find( ".//gml:exterior", ns )
                temp = exterior.find( ".//gml:curveMember", ns )
                href = temp.get( "{http://www.w3.org/1999/xlink}href" )
                NowCv = href.lstrip('#')
                # 座標定義位置を探す
                xpath = ".//*/..[@gml:id='" + NowCv + "']"
                Curve = tree.find( xpath, ns )
                posList = Curve.find( './/gml:posList', ns )
                LineData = posList.text.split( '\n' )
                Buff = []
                for temp in LineData:
                        if temp:
                                Buff.append( temp )
                Count = len( Buff ) - 1
                for ptr in range( 0, Count ):
                        NowText = Buff[ ptr ].lstrip( ' \t' )
                        xyStr = NowText.split( ' ' )
                        posY = float( xyStr[ 0 ] )
                        posX = float( xyStr[ 1 ] )
                        Xmin = min( posX, Xmin )
                        Xmax = max( posX, Xmax )
                        Ymin = min( posY, Ymin )
                        Ymax = max( posY, Ymax )
        #
        # ↓ こんなにやって結局欲しかったのはこの2行
        wx = ( Xmin + Xmax ) / 2.0
        wy = ( Ymin + Ymax ) / 2.0
        #
        xpath = ".//*/..[ksj:administrativeAreaCode='" + AreaCode + "']"
        for AdministrativeBoundary in tree.findall( xpath, ns ):
                RecName = CreateRecName( AreaCode, AdministrativeBoundary )
                # 面の定義を探す
                bounds = AdministrativeBoundary.find( 'ksj:bounds', ns )
                href = bounds.get('{http://www.w3.org/1999/xlink}href')
                NowSf = href.lstrip('#')
                # 面の定義情報からカーブを取り出す
                xpath = ".//*/..[@gml:id='" + NowSf + "']"
                Surface = tree.find( xpath, ns )
                exterior = Surface.find( ".//gml:exterior", ns )
                temp = exterior.find( ".//gml:curveMember", ns )
                href = temp.get( "{http://www.w3.org/1999/xlink}href" )
                NowCv = href.lstrip('#')
                #
                objectdata = CreateCurve( RecName, tree, NowCv, False )
                #
                # 穴をあける処理を追加
                for interior in Surface.findall( ".//gml:interior", ns ):
                        for cv in interior.findall( ".//gml:curveMember", ns ):
                                href = cv.get( "{http://www.w3.org/1999/xlink}href" )
                                NowCv = href.lstrip('#')
                                objectdata2 = CreateCurve( "temp", tree, NowCv, True )
                                #
                                objectdata.select = True
                                bpy.context.scene.objects.active = objectdata
                                bpy.ops.object.modifier_add(type='BOOLEAN')
                                bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
                                bpy.context.object.modifiers["Boolean"].object = bpy.data.objects["temp"]
                                bpy.context.object.modifiers["Boolean"].double_threshold = 0.0
                                bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
                                objectdata.select = False
                                objectdata2.select = True
                                bpy.ops.object.delete(use_global=False)
                                bpy.context.scene.objects.active = None
        #
        tree = None     # どうせ終わるので無くてもよい
        #
        MergeRecName()  # 複数レコードを1つにまとめたいなら実行
#
print( '\nEnd Script' )

 内側の穴ですが、interiorの定義からtempという名前のオブジェクトを作ります。このとき、あらかじめ高い位置で図形を作成すると動作が確実になります。

 外枠の図形を、モディファイアーからブーリアンでtempオブジェクトをくり抜きます。

 くり抜いたらtempオブジェクトを削除します。千葉県鎌ケ谷市のように、穴が複数箇所開いている地区もあるので、これを繰り返します。

 出来上がった図形をポリゴン数削減で0.1にし、高さを加えると次のようになります。南西の方角に小さな穴があります。

<トップページに戻る>


Copyright© Shizuka-Toikanbetsu.
inserted by FC2 system