├── README.md ├── LICENSE ├── .gitignore ├── __init__.py ├── SetMesh.py └── LoadGML.py /README.md: -------------------------------------------------------------------------------- 1 | # Platearu-Blender-Importer 2 | 3 | 国土交通省のPLATEAUで提供されるのCityGMLファイルをBlenderにインポートするツール 4 | CityGMLであれば読み込めるかもしれませんがとくに確認を取っていないので保障外です 5 | 6 | 入力した地域メッシュコードあるいは緯度経度を中心に3Dオブジェクトを出力します 7 | スケールの値を変更すると出力するオブジェクトのサイズを調整できます 8 | 9 | 地域メッシュコードを指定しない場合や誤った形式の場合中心は自動的にデフォルト値の"53394611"が採用されます 10 | また、地域メッシュコード入力欄に緯度経度をカンマ区切りで入力すると緯度経度中心にすることができます。 11 | 12 | 13 | [このリンクから](https://github.com/nneri-hin/Plateau-Blender-Importer/archive/master.zip)Zipファイルを入手してください。 14 | 15 | BlenderのEidt(編集)->Preference(プリファレンス)->addon(アドオン)->Install(インストール) 16 | でダウンロードしたzipファイルを読み込んでください。 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nneri-hin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | bl_info = { 2 | "name": "Import Plateau CityGML", 3 | "author": "Hin(@thamurian)", 4 | "version": (0, 9, 5), 5 | "blender": (3, 0, 0), 6 | #"support":"TESTING", 7 | "location":"File > Import-Export", 8 | "category": "Import-Export", 9 | "description": "Import geometry from CityGML file(s).", 10 | "wiki_url": "https://github.com/nneri-hin/Plateau-Blender-Importer", 11 | } 12 | 13 | import os 14 | import numpy as np 15 | #from xml.etree import ElementTree as et 16 | import bpy 17 | from bpy_extras.io_utils import ImportHelper 18 | import re 19 | from bpy.props import (BoolProperty, 20 | FloatProperty, 21 | IntProperty, 22 | StringProperty, 23 | EnumProperty, 24 | CollectionProperty, 25 | FloatVectorProperty) 26 | from . import LoadGML 27 | from . import SetMesh 28 | #if "bpy" in locals(): 29 | # print("imp") 30 | # import imp 31 | # imp.reload(LoadGML) 32 | # 33 | 34 | def get_shader_enum(scene,context): 35 | items = [ 36 | ("PrincipledBSDF" ,"PrincipledBSDF" ,"PrincipledBSDF" ,"PrincipledBSDF",0), 37 | ("TextureEmission","TextureEmission","Emission(TextureOnly)","TextureEmission",1), 38 | ("AllEmission" ,"AllEmission" ,"Emission(AllMaterial)" ,"AllEmission",2) 39 | ] 40 | return items 41 | 42 | class PlateauImporter(bpy.types.Operator, ImportHelper): 43 | bl_idname = "hin.plateau_importer" 44 | bl_label = "pick an gml file(s)" 45 | filename_ext = ".gml" 46 | use_filter_folder = True 47 | files: CollectionProperty(type=bpy.types.PropertyGroup) 48 | origin_setting_jmc: StringProperty( 49 | name="Origin Japan Mesh Code Or Latlon ", 50 | description="Origin Japan Mesh Code Or Latlon(Ex.35.680, 139.766)", 51 | default="53394611" 52 | ) 53 | scale: FloatProperty( 54 | name="Scale", 55 | description="Scale object size", 56 | default=1.0 57 | ) 58 | range: FloatProperty( 59 | name="Range", 60 | description="Effective distance(km). If a negative value is entered, it is assumed to be infinite.", 61 | default=-1 62 | ) 63 | limit_type: BoolProperty( 64 | name="Range limit vertex units", 65 | description="Delete out-of-range as vertex units", 66 | default=False 67 | ) 68 | import_texture:BoolProperty( 69 | name="Import Texture", 70 | description="Import Textuure", 71 | default=True 72 | ) 73 | shader_type:EnumProperty( 74 | name = "Shader Type", 75 | description = "select shader type. princpled bsdf of emission", 76 | #default="PrincipledBSDF", 77 | items = get_shader_enum 78 | ) 79 | 80 | def execute(self,context): 81 | pass 82 | directory= (os.path.dirname(self.filepath)) 83 | loader = LoadGML.LoadGML() 84 | setmesh = SetMesh.SetMesh() 85 | meshcode = "53394611" 86 | clat,clon = 0,0 87 | isMeshCode = True 88 | if re.search(',',self.origin_setting_jmc) : 89 | #Center is Latlon 90 | slat,slon = self.origin_setting_jmc.split(",") 91 | isMeshCode = False 92 | try: 93 | clat = float(slat) 94 | clon = float(slon) 95 | except: 96 | print("ParseError") 97 | isMeshCode = True 98 | pass 99 | if isMeshCode: 100 | if len(self.origin_setting_jmc) != 8 or re.search("\D",self.origin_setting_jmc): 101 | print("Wrong meshcode") 102 | else: 103 | meshcode = self.origin_setting_jmc 104 | print(meshcode) 105 | jmTool = LoadGML.JapanMeshTool() 106 | clat,clon = jmTool.getCenter(meshcode) 107 | for i in self.files: 108 | path_to_file = (os.path.join(directory, i.name)) 109 | result = loader.load(path_to_file) 110 | poly = loader.positionSet(result,clat,clon,0,self.scale,self.range * 500,self.limit_type) 111 | print(self.shader_type) 112 | setmesh.mesh(context,poly,directory,i.name,self.import_texture,self.shader_type) 113 | 114 | return {'FINISHED'} 115 | def menu_import(self, context): 116 | self.layout.operator(PlateauImporter.bl_idname, text="Plateau cityGML (.gml)") 117 | 118 | def register(): 119 | bpy.utils.register_class(PlateauImporter) 120 | bpy.types.TOPBAR_MT_file_import.append(menu_import) 121 | 122 | 123 | def unregister(): 124 | bpy.types.TOPBAR_MT_file_import.remove(menu_import) 125 | bpy.utils.unregister_class(PlateauImporter) 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /SetMesh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #import requests 4 | import bpy 5 | import bmesh 6 | import math 7 | from bpy_extras import object_utils 8 | import numpy as np 9 | import os 10 | 11 | class Textures: 12 | def __init__(self): 13 | self.textures = [] 14 | self.cnt = 0 15 | pass 16 | def add(self,texture): 17 | if texture not in self.textures: 18 | self.textures.append(texture) 19 | return self.textures.index(texture) 20 | 21 | 22 | 23 | class SetMesh: 24 | def __init__(self): 25 | pass 26 | def create_material(self,directory,texture,emission): 27 | path_to_file = (os.path.join(directory, texture)) 28 | image = bpy.data.images.load(path_to_file) 29 | mat = bpy.data.materials.new(texture) 30 | mat.use_nodes = True 31 | nodes = mat.node_tree.nodes 32 | tex = nodes.new(type='ShaderNodeTexImage') 33 | tex.location = (-300,300) 34 | tex.image = image 35 | bsdf = None 36 | #ShaderNodeBsdfPrinciple 37 | #ShaderNodeEmission 38 | output = None 39 | em = None 40 | if emission : 41 | em = nodes.new("ShaderNodeEmission") 42 | for node in nodes: 43 | if node.type == "BSDF_PRINCIPLED": 44 | bsdf = node 45 | if node.type == "OUTPUT_MATERIAL": 46 | output = node 47 | #bsdf = nodes['Principled BSDF'] 48 | if bsdf is not None: 49 | if emission: 50 | mat.node_tree.links.new(tex.outputs[0],em.inputs[0]) 51 | mat.node_tree.links.new(em.outputs[0],output.inputs[0]) 52 | nodes.remove(bsdf) 53 | else: 54 | mat.node_tree.links.new(tex.outputs[0],bsdf.inputs[0]) 55 | return mat 56 | def create_blank_material(selfa,emission): 57 | mat = bpy.data.materials.new("Default") 58 | mat.use_nodes = True 59 | nodes = mat.node_tree.nodes 60 | if emission : 61 | em = nodes.new("ShaderNodeEmission") 62 | for node in nodes: 63 | if node.type == "BSDF_PRINCIPLED": 64 | bsdf = node 65 | if node.type == "OUTPUT_MATERIAL": 66 | output = node 67 | if bsdf is not None: 68 | if emission: 69 | mat.node_tree.links.new(em.outputs[0],output.inputs[0]) 70 | nodes.remove(bsdf) 71 | mat.use_nodes = True 72 | return mat 73 | pass 74 | 75 | def set_uvmap(self,n_mesh,uvmap): 76 | #UVMapがあった場合面に設定する 77 | bm = bmesh.new() 78 | bm.from_mesh(n_mesh) 79 | uv = bm.loops.layers.uv.new("UVMap") 80 | cnt = 0 81 | texture = "" 82 | textures = Textures() 83 | for face in bm.faces: 84 | #for loop in face.loops: 85 | for i in range(len(face.loops)): 86 | if len(uvmap[cnt]["uv"]) > i* 2: 87 | face.loops[i][uv].uv = [uvmap[cnt]["uv"][i*2],uvmap[cnt]["uv"][i*2+1]] 88 | if uvmap[cnt]["texture"] != "": 89 | texture = uvmap[cnt]["texture"] 90 | face.material_index = textures.add(uvmap[cnt]["texture"]) 91 | cnt += 1 92 | bm.to_mesh(n_mesh) 93 | n_mesh.update() 94 | return textures.textures 95 | def mesh(self,context,poly,directory,name,import_texture,shader_type): 96 | materials = {} 97 | collection = bpy.data.collections.new(name) 98 | bpy.context.scene.collection.children.link(collection) 99 | blank = self.create_blank_material(shader_type == "AllEmission") 100 | for texture in poly["textures"]: 101 | materials[texture] = self.create_material(directory,texture,shader_type != "PrincipledBSDF") 102 | for data in poly["datas"]: 103 | obj = data["obj"] 104 | verts = data["verts"] 105 | faces = data["faces"] 106 | uvmap = data["uvmap"] 107 | #textures = data["textures"] 108 | n_mesh = bpy.data.meshes.new(obj.id) 109 | n_mesh.from_pydata(verts,[],faces) 110 | n_mesh.update() 111 | #bObject = object_utils.object_data_add(context, n_mesh, operator=None) 112 | bObject = bpy.data.objects.new(obj.id,n_mesh) 113 | if obj.enableTexture and import_texture : 114 | textures = self.set_uvmap(n_mesh,uvmap) 115 | #この辺多分そのうちかえる 116 | #if texture != "": 117 | if len(textures) != 0: 118 | for i in range(len(textures)): 119 | texture = textures[i] 120 | bObject.data.materials.append(materials[texture]) 121 | #bObject.active_material = materials[texture] 122 | pass 123 | else : 124 | bObject.active_material = blank 125 | else : 126 | bObject.active_material = blank 127 | collection.objects.link(bObject) 128 | -------------------------------------------------------------------------------- /LoadGML.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #import requests 4 | import xml.etree.ElementTree as ET 5 | import json 6 | #import bpy 7 | #import bmesh 8 | import math 9 | #from bpy_extras import object_utils 10 | import numpy as np 11 | import os 12 | 13 | lodTypes = { 14 | "LOD0FP":0, 15 | "LOD0RE":0, 16 | "LOD1":1, 17 | "LOD2":2, 18 | "LODX":3 19 | } 20 | class Verts: 21 | def __init__(self,_id,vertex,lodType): 22 | self.vertex = vertex 23 | self.id = _id 24 | self.lod = lodTypes[lodType] 25 | self.lodType = lodType 26 | class Object: 27 | def __init__(self,_id,posList,enableTexture,uri): 28 | self.id = _id 29 | self.posList = posList 30 | self.minLod = 10 31 | self.maxLod = 0 32 | for verts in posList: 33 | #if verts.lodType == "LOD0RE": 34 | # #RoofEdgeだけの場合はLOD0は無視する。FootPrintがあればそっちを使う 35 | # continue 36 | if self.maxLod < verts.lod : 37 | self.maxLod = verts.lod 38 | if self.minLod > verts.lod : 39 | self.minLod = verts.lod 40 | pass 41 | self.enableTexture = enableTexture 42 | self.uri = uri 43 | self.boundingBox = np.array([90,180,-90,-180,0,0],dtype=np.float64) 44 | class ParseResult: 45 | def __init__(self): 46 | self.objects = [] 47 | self.uvmap = {} 48 | self.textures = set() 49 | 50 | 51 | 52 | class DistanceCalc: 53 | def __init__(self): 54 | #WGS84 55 | self.a = 6378137 #長半径 56 | self.b = 6356752.314245 #短半径 57 | self.f = 1 / 298.257223563 #扁平率 58 | self.E = 0.081819191042815790 59 | self.E2 = 0.006694380022900788 60 | #Φ1 = 緯度 L1 経度 (出発) 61 | pass 62 | def calc(self,lat1,lon1,lat2,lon2): 63 | #ヒュベニの公式を使用している 64 | radlat1 = np.radians(lat1) 65 | radlon1 = np.radians(lon1) 66 | radlat2 = np.radians(lat2) 67 | radlon2 = np.radians(lon2) 68 | avglat = (radlat1 + radlat2)/2 69 | dy = radlat1 - radlat2 70 | dx = radlon1 - radlon2 71 | #W = np.sqrt(1-(self.E2*np.arcsin(avglat))) 72 | W = np.sqrt(1-(self.E2 * np.power(np.sin(avglat),2) )) 73 | M = (self.a*(1-self.E2))/np.power(W,3) 74 | N = self.a / W 75 | x = dx * N * np.cos(avglat) 76 | y = dy * M 77 | #本来は下の公式で距離を出すが、今回はx,yが必要なのでそのまま返す 78 | #return (x,y,np.sqrt(np.power(x,2)+np.power(y,2))) 79 | return x,y 80 | 81 | 82 | class LoadGML: 83 | def __init__(self): 84 | pass 85 | self.POSLIST = "{http://www.opengis.net/gml}posList" 86 | self.GMLID = "{http://www.opengis.net/gml}id" 87 | self.IMAGEURI = "{http://www.opengis.net/citygml/appearance/2.0}imageURI" 88 | self.STRINGATRIB = "{http://www.opengis.net/citygml/generics/2.0}stringAttribute" 89 | self.CITYOBJECT = "{http://www.opengis.net/citygml/2.0}cityObjectMember" 90 | self.TEXCOORD = "{http://www.opengis.net/citygml/appearance/2.0}TexCoordList" 91 | self.BOUNDED = "{http://www.opengis.net/citygml/building/2.0}boundedBy" 92 | self.LOD0FP = "{http://www.opengis.net/citygml/building/2.0}lod0FootPrint" 93 | self.LOD0RE = "{http://www.opengis.net/citygml/building/2.0}lod0RoofEdge" 94 | self.LOD1 = "{http://www.opengis.net/citygml/building/2.0}lod1Solid" 95 | self.LOD2 = "{http://www.opengis.net/citygml/building/2.0}lod2Solid" 96 | self.LOD2MS = "{http://www.opengis.net/citygml/building/2.0}lod2MultiSurface" 97 | self.dc = DistanceCalc() 98 | def searchPosList(self,data,posList,enableTexture,uri,lod): 99 | for child in data: 100 | if child.tag == self.LOD0FP: 101 | lod = "LOD0FP" 102 | if child.tag == self.LOD0RE: 103 | lod = "LOD0RE" 104 | if child.tag == self.LOD1: 105 | lod = "LOD1" 106 | if child.tag == self.LOD2: 107 | lod = "LOD2" 108 | if child.tag == self.LOD2MS: 109 | lod = "LOD2" 110 | (posList,enableTexture,uri,lod) = self.searchPosList(child,posList,enableTexture,uri,lod) 111 | if child.tag == self.POSLIST: 112 | gmlid = "0" 113 | if self.GMLID in data.attrib: 114 | gmlid = data.attrib[self.GMLID] 115 | enableTexture = True 116 | #posList.append({"id":gmlid,"vertex":np.asfarray(child.text.split(" "),dtype=float)}) 117 | posList.append(Verts(gmlid, np.asfarray(child.text.split(" "),dtype=np.float64),lod ) ) 118 | return (posList,enableTexture,uri,lod) 119 | def CreateDict(self,data): 120 | gmlid = data.attrib[self.GMLID] 121 | posList = [] 122 | (posList,enableTexture,uri,lod)= self.searchPosList(data,posList,False,"","LODX") 123 | #for i in posList: 124 | # print(gmlid,i.lod) 125 | #return {"posList":posList,"id":gmlid,"enableTexture":enableTexture,"uri":uri} 126 | return Object(gmlid,posList,enableTexture,uri) 127 | 128 | def CityObjectParse(self,data): 129 | maxPos = [-90,-180] 130 | minPos = [90,180] 131 | for child in data: 132 | city = self.CreateDict(child) 133 | for p in city.posList: 134 | city.boundingBox[0] = np.min(np.append(p.vertex[::3] , city.boundingBox[0]) ) 135 | city.boundingBox[1] = np.min(np.append(p.vertex[1::3] , city.boundingBox[1]) ) 136 | city.boundingBox[2] = np.max(np.append(p.vertex[::3] , city.boundingBox[2]) ) 137 | city.boundingBox[3] = np.max(np.append(p.vertex[1::3] , city.boundingBox[3]) ) 138 | city.boundingBox[4] = (city.boundingBox[0]+city.boundingBox[2])/2 139 | city.boundingBox[5] = (city.boundingBox[1]+city.boundingBox[3])/2 140 | return city 141 | 142 | def UVParse(self,data,result,texture): 143 | coords = [] 144 | for child in data: 145 | result.uvmap[child.attrib["ring"]] ={"texture":texture,"uv": np.asfarray(child.text.split(" "),dtype=float)} 146 | pass 147 | return result 148 | 149 | def load(self,filename): 150 | tree = ET.parse(filename) 151 | root = tree.getroot() 152 | return self.parse(root) 153 | 154 | def parse(self,obj): 155 | temp = ParseResult() 156 | return self._parse(obj,temp,"",0) 157 | 158 | def _parse(self,obj,result,texture,depth): 159 | children = [] 160 | for child in obj: 161 | if child.tag == self.CITYOBJECT: 162 | o = self.CityObjectParse(child) 163 | result.objects.append(o) 164 | elif child.tag == self.TEXCOORD: 165 | result = self.UVParse(child,result,texture) 166 | #print(str(depth)+":TEXCOORD") 167 | elif child.tag == self.IMAGEURI: 168 | texture = child.text 169 | result.textures.add(child.text) 170 | else : 171 | children.append(child) 172 | #下に降りるときは一度その層を全部見てからにする 173 | for child in children: 174 | result = self._parse(child,result,texture,depth+1) 175 | return result 176 | 177 | def positionSet(self,result,clat,clon,celev,scale,viewRange,limitType): 178 | #tets用53393641 179 | verts = [] 180 | datas = [] 181 | for obj in result.objects: 182 | verts = [] 183 | vertsMerge={} 184 | faces = [] 185 | faces_tex = [] 186 | vindex = 0 187 | uvmap = [] 188 | dist = self.dc.calc(clat,clon,obj.boundingBox[4],obj.boundingBox[5]) 189 | if not limitType and viewRange > 0 and ( np.abs(dist[0]) > viewRange or np.abs(dist[1]) > viewRange ) : 190 | continue 191 | #print(obj["id"],obj["enableTexture"]) 192 | for o2 in obj.posList:#osはposList 193 | lid = "-" 194 | if obj.enableTexture and o2.id == "0": 195 | #テクスチャ有効時、idが0のものはスキップする 196 | continue 197 | if obj.enableTexture: 198 | lid = "#"+o2.id 199 | if lid in result.uvmap: 200 | uvmap.append(result.uvmap[lid]) 201 | else: 202 | uvmap.append({"uv":[0.4,0.4],"texture":""}) 203 | #else: 204 | # uvmap.append([]) 205 | indexes = [] 206 | lodSplit = [[],[],[]]#lod0~2 207 | for i in range(0,len(o2.vertex),3): 208 | lat = o2.vertex[i] 209 | lon = o2.vertex[i+1] 210 | hig = o2.vertex[i+2] 211 | key = str(lat)+","+str(lon)+","+str(hig) 212 | if o2.lod != obj.maxLod: 213 | continue 214 | if key in vertsMerge: 215 | indexes.append(vertsMerge[key]) 216 | else: 217 | (x,y) = self.dc.calc(lat,lon,clat,clon) 218 | if limitType and viewRange > 0 and ( np.abs(x) > viewRange or np.abs(y) > viewRange ) : 219 | #リミットタイプがTrueの時頂点単位で削除 220 | continue 221 | vertsMerge[key] = vindex 222 | indexes.append(vindex) 223 | hig = hig - celev 224 | verts.append([x*scale,y*scale,hig*scale]) 225 | vindex += 1 226 | if len(indexes) > 2 : #面を貼る場合最低3頂点必要 227 | faces.append(indexes) 228 | if len(faces) > 0: #最低1面必要 229 | datas.append({ "obj":obj,"verts":verts,"faces":faces,"uvmap":uvmap }) 230 | return {"datas":datas,"textures":result.textures} 231 | def get_image_path(self,filename,texture): 232 | dir = os.path.dirname(filename) 233 | path = dir + texture 234 | print(path) 235 | 236 | ##こっからC#のコピペ改造 237 | class JapanMeshTool: 238 | #三次メッシュしか対応してません 239 | def getNeighbor(self,meshcode,lat ,lon): 240 | #隣接メッシュを取得します 241 | lat1 = meshcode[0:2] 242 | lon1 = meshcode[2:4] 243 | lat2 = meshcode[4:5] 244 | lon2 = meshcode[5:6] 245 | lat3 = meshcode[6:7] 246 | lon3 = meshcode[7:8] 247 | tlat = str(int(lat1 + lat2 + lat3) + lat).zfill(4) 248 | tlon = str(int(lon1 + lon2 + lon3) + lon).zfill(4) 249 | return tlat[0:2]+ tlon[0:2] + tlat[2:3] + tlon[2:3] + tlat[3:4] + tlon[3:4] 250 | def toMeshCode(self,lat ,lon): 251 | #メッシュコードは3次メッシュ固定です 252 | #緯度の計算 253 | p = np.floor(lat * 1.5) 254 | a = (lat * 60) % 40 255 | q = np.floor(a / 5) 256 | b = a % 5 257 | r = np.floor(b * 60 / 30) 258 | u = np.floor(lon - 100) 259 | f = lon - u - 100 260 | v = np.floor(f * 60 / 7.5) 261 | g = f * 60 % 7.5 262 | w = np.floor(g * 60 / 45) 263 | return str(p)+str(u)+str(q)+str(v)+ str(r) + str(w) 264 | def toLatLon(self,meshcode): 265 | lat1 = float(meshcode[0: 2]) / 60 * 40 266 | lon1 = float(meshcode[2: 4]) + 100 267 | lat2 = (float(meshcode[4: 5]) * 2/3)/8 268 | lon2 = float(meshcode[5: 6]) /8 269 | lat3 = float(meshcode[6: 7]) * (2/3) / 80 270 | lon3 = float(meshcode[7: 8]) / 80 271 | latlon = [ lat1 + lat2 + lat3, lon1 + lon2 + lon3 ] 272 | return latlon 273 | def getCenter(self,meshcode): 274 | latlon1 = self.toLatLon(meshcode) 275 | latlon2 = self.toLatLon(self.getNeighbor(meshcode, 1, 1)) 276 | #return new float[2] { (latlon1[0] + latlon2[0]) / 2, (latlon1[1] + latlon2[1]) / 2 } 277 | return [ (latlon1[0] + latlon2[0]) / 2 ,(latlon1[1] + latlon2[1]) /2] 278 | --------------------------------------------------------------------------------