diff --git a/operators/io_get_dem.py b/operators/io_get_dem.py index ebc07a22..dc53bc95 100755 --- a/operators/io_get_dem.py +++ b/operators/io_get_dem.py @@ -113,9 +113,9 @@ def execute(self, context): # Download the file from url and save it locally # opentopo return a geotiff object in wgs84 if bpy.data.is_saved: - filePath = os.path.join(os.path.dirname(bpy.data.filepath), 'srtm.tif') + filePath = os.path.join(os.path.dirname(bpy.data.filepath), aObj.name + 'srtm.tif') else: - filePath = os.path.join(bpy.app.tempdir, 'srtm.tif') + filePath = os.path.join(bpy.app.tempdir, aObj.name + 'srtm.tif') #we can directly init NpImg from blob but if gdal is not used as image engine then georef will not be extracted #Alternatively, we can save on disk, open with GeoRaster class (will use tyf if gdal not available) diff --git a/operators/io_import_georaster.py b/operators/io_import_georaster.py index d74de1d1..41861cb7 100755 --- a/operators/io_import_georaster.py +++ b/operators/io_import_georaster.py @@ -46,8 +46,8 @@ from ..core.proj import Reproj from bpy_extras.io_utils import ImportHelper #helper class defines filename and invoke() function which calls the file selector -from bpy.props import StringProperty, BoolProperty, EnumProperty, IntProperty -from bpy.types import Operator +from bpy.props import StringProperty, BoolProperty, EnumProperty, IntProperty, CollectionProperty +from bpy.types import Operator, OperatorFileListElement PKG, SUBPKG = __package__.split('.', maxsplit=1) @@ -59,6 +59,13 @@ class IMPORTGIS_OT_georaster(Operator, ImportHelper): bl_label = "Import georaster" bl_options = {"UNDO"} + files: CollectionProperty( + name="Files", + type=OperatorFileListElement + ) + + directory: StringProperty(subtype='DIR_PATH') + def listObjects(self, context): #Function used to update the objects list (obj_list) used by the dropdown box. objs = [] #list containing tuples of each object @@ -97,8 +104,7 @@ def listPredefCRS(self, context): ('DEM', 'DEM as displacement texture', "Use DEM raster as height texture to wrap a base mesh"), ('DEM_RAW', 'DEM raw data build [slow]', "Import a DEM as pixels points cloud with building faces. Do not use with huge dataset.")] ) - # - objectsLst: EnumProperty(attr="obj_list", name="Objects", description="Choose object to edit", items=listObjects) + # #Subdivise (as DEM option) def listSubdivisionModes(self, context): @@ -157,19 +163,16 @@ def draw(self, context): pass # if self.importMode == 'MESH': - if geoscn.isGeoref and len(self.objectsLst) > 0: - layout.prop(self, 'objectsLst') - else: - layout.label(text="There isn't georef mesh to UVmap on") + if not (geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH'): + layout.label(text="The active object is not a georef mesh") # if self.importMode == 'DEM': layout.prop(self, 'demOnMesh') if self.demOnMesh: - if geoscn.isGeoref and len(self.objectsLst) > 0: - layout.prop(self, 'objectsLst') + if geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH': layout.prop(self, 'clip') else: - layout.label(text="There isn't georef mesh to apply on") + layout.label(text="The active object is not a georef mesh") layout.prop(self, 'subdivision') layout.prop(self, 'demInterpolation') if self.subdivision == 'mesh': @@ -181,10 +184,8 @@ def draw(self, context): layout.prop(self, 'step') layout.prop(self, 'clip') if self.clip: - if geoscn.isGeoref and len(self.objectsLst) > 0: - layout.prop(self, 'objectsLst') - else: - layout.label(text="There isn't georef mesh to refer") + if not (geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH'): + layout.label(text="The active object is not a georef mesh") # if geoscn.isPartiallyGeoref: layout.prop(self, 'reprojection') @@ -246,227 +247,221 @@ def execute(self, context): rprjToRaster = None rprjToScene = None - #Path - filePath = self.filepath - name = os.path.basename(filePath)[:-4] + newObjCreated = False - ###################################### - if self.importMode == 'PLANE':#on plane - #Load raster - try: - rast = bpyGeoRaster(filePath) - except IOError as e: - log.error("Unable to open raster", exc_info=True) - self.report({'ERROR'}, "Unable to open raster, check logs for more infos") - return {'CANCELLED'} - #Get or set georef dx, dy - if not geoscn.isGeoref: - dx, dy = rast.center.x, rast.center.y - if rprj: - dx, dy = rprjToScene.pt(dx, dy) - geoscn.setOriginPrj(dx, dy) - #create a new mesh from raster extent - mesh = rasterExtentToMesh(name, rast, dx, dy, reproj=rprjToScene) - #place obj - obj = placeObj(mesh, name) - #UV mapping - uvTxtLayer = mesh.uv_layers.new(name='rastUVmap')# Add UV map texture layer - geoRastUVmap(obj, uvTxtLayer, rast, dx, dy, reproj=rprjToRaster) - # Create material - mat = bpy.data.materials.new('rastMat') - # Add material to current object - obj.data.materials.append(mat) - # Add texture to material - addTexture(mat, rast.bpyImg, uvTxtLayer, name='rastText') - - ###################################### - if self.importMode == 'BKG':#background - if rprj: - #TODO, do gdal true reproj - self.report({'ERROR'}, "Raster reprojection is not possible in background mode") - return {'CANCELLED'} - #Load raster - try: - rast = bpyGeoRaster(filePath) - except IOError as e: - log.error("Unable to open raster", exc_info=True) - self.report({'ERROR'}, "Unable to open raster, check logs for more infos") - return {'CANCELLED'} - #Check pixel size and rotation - if rast.rotation.xy != [0,0]: - self.report({'ERROR'}, "Cannot apply a rotation in background image mode") - return {'CANCELLED'} - if abs(round(rast.pxSize.x, 3)) != abs(round(rast.pxSize.y, 3)): - self.report({'ERROR'}, "Background image needs equal pixel size in map units in both x ans y axis") - return {'CANCELLED'} - # - trueSizeX = rast.geoSize.x - trueSizeY = rast.geoSize.y - ratio = rast.size.x / rast.size.y - if geoscn.isGeoref: - offx, offy = rast.center.x - dx, rast.center.y - dy - else: - dx, dy = rast.center.x, rast.center.y - geoscn.setOriginPrj(dx, dy) - offx, offy = 0, 0 + for file in self.files: + filePath = os.path.join(self.directory, file.name) + name = os.path.basename(filePath)[:-4] - bkg = bpy.data.objects.new(self.name, None) #None will create an empty - bkg.empty_display_type = 'IMAGE' - bkg.empty_image_depth = 'BACK' - bkg.data = rast.bpyImg - scn.collection.objects.link(bkg) + ###################################### + if self.importMode == 'PLANE':#on plane + #Load raster + try: + rast = bpyGeoRaster(filePath) + except IOError as e: + log.error("Unable to open raster", exc_info=True) + self.report({'ERROR'}, "Unable to open raster, check logs for more infos") + return {'CANCELLED'} + #Get or set georef dx, dy + if not geoscn.isGeoref: + dx, dy = rast.center.x, rast.center.y + if rprj: + dx, dy = rprjToScene.pt(dx, dy) + geoscn.setOriginPrj(dx, dy) + #create a new mesh from raster extent + mesh = rasterExtentToMesh(name, rast, dx, dy, reproj=rprjToScene) + #place obj + obj = placeObj(mesh, name) + #UV mapping + uvTxtLayer = mesh.uv_layers.new(name='rastUVmap')# Add UV map texture layer + geoRastUVmap(obj, uvTxtLayer, rast, dx, dy, reproj=rprjToRaster) + # Create material + mat = bpy.data.materials.new('rastMat') + # Add material to current object + obj.data.materials.append(mat) + # Add texture to material + addTexture(mat, rast.bpyImg, uvTxtLayer, name='rastText') + + ###################################### + if self.importMode == 'BKG':#background + if rprj: + #TODO, do gdal true reproj + self.report({'ERROR'}, "Raster reprojection is not possible in background mode") + return {'CANCELLED'} + #Load raster + try: + rast = bpyGeoRaster(filePath) + except IOError as e: + log.error("Unable to open raster", exc_info=True) + self.report({'ERROR'}, "Unable to open raster, check logs for more infos") + return {'CANCELLED'} + #Check pixel size and rotation + if rast.rotation.xy != [0,0]: + self.report({'ERROR'}, "Cannot apply a rotation in background image mode") + return {'CANCELLED'} + if abs(round(rast.pxSize.x, 3)) != abs(round(rast.pxSize.y, 3)): + self.report({'ERROR'}, "Background image needs equal pixel size in map units in both x ans y axis") + return {'CANCELLED'} + # + trueSizeX = rast.geoSize.x + trueSizeY = rast.geoSize.y + ratio = rast.size.x / rast.size.y + if geoscn.isGeoref: + offx, offy = rast.center.x - dx, rast.center.y - dy + else: + dx, dy = rast.center.x, rast.center.y + geoscn.setOriginPrj(dx, dy) + offx, offy = 0, 0 - bkg.empty_display_size = 1 #a size of 1 means image width=1bu - bkg.scale = (trueSizeX, trueSizeY*ratio, 1) - bkg.location = (offx, offy, 0) + bkg = bpy.data.objects.new(self.name, None) #None will create an empty + bkg.empty_display_type = 'IMAGE' + bkg.empty_image_depth = 'BACK' + bkg.data = rast.bpyImg + scn.collection.objects.link(bkg) - bpy.context.view_layer.objects.active = bkg - bkg.select_set(True) + bkg.empty_display_size = 1 #a size of 1 means image width=1bu + bkg.scale = (trueSizeX, trueSizeY*ratio, 1) + bkg.location = (offx, offy, 0) - if prefs.adjust3Dview: - adjust3Dview(context, rast.bbox) + bpy.context.view_layer.objects.active = bkg + bkg.select_set(True) - ###################################### - if self.importMode == 'MESH': - if not geoscn.isGeoref or len(self.objectsLst) == 0: - self.report({'ERROR'}, "There isn't georef mesh to apply on") - return {'CANCELLED'} - # Get choosen object - obj = scn.objects[int(self.objectsLst)] - # Select and active this obj - obj.select_set(True) - context.view_layer.objects.active = obj - # Compute projeted bbox (in geographic coordinates system) - subBox = getBBOX.fromObj(obj).toGeo(geoscn) - if rprj: - subBox = rprjToRaster.bbox(subBox) - #Load raster - try: - rast = bpyGeoRaster(filePath, subBoxGeo=subBox) - except IOError as e: - log.error("Unable to open raster", exc_info=True) - self.report({'ERROR'}, "Unable to open raster, check logs for more infos") - return {'CANCELLED'} - except OverlapError: - self.report({'ERROR'}, "Non overlap data") - return {'CANCELLED'} - # Add UV map texture layer - mesh = obj.data - uvTxtLayer = mesh.uv_layers.new(name='rastUVmap') - uvTxtLayer.active = True - # UV mapping - geoRastUVmap(obj, uvTxtLayer, rast, dx, dy, reproj=rprjToRaster) - # Add material and texture - mat = bpy.data.materials.new('rastMat') - obj.data.materials.append(mat) - addTexture(mat, rast.bpyImg, uvTxtLayer, name='rastText') - - ###################################### - if self.importMode == 'DEM': + if prefs.adjust3Dview: + adjust3Dview(context, rast.bbox) - # Get reference plane - if self.demOnMesh: - if not geoscn.isGeoref or len(self.objectsLst) == 0: - self.report({'ERROR'}, "There isn't georef mesh to apply on") + ###################################### + if self.importMode == 'MESH': + if not (geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH'): + self.report({'ERROR'}, "The active object is not a georef mesh") return {'CANCELLED'} # Get choosen object - obj = scn.objects[int(self.objectsLst)] - mesh = obj.data - # Select and active this obj - obj.select_set(True) - context.view_layer.objects.active = obj + obj = context.view_layer.objects.active # Compute projeted bbox (in geographic coordinates system) subBox = getBBOX.fromObj(obj).toGeo(geoscn) if rprj: subBox = rprjToRaster.bbox(subBox) - else: - subBox = None + #Load raster + try: + rast = bpyGeoRaster(filePath, subBoxGeo=subBox) + except IOError as e: + log.error("Unable to open raster", exc_info=True) + self.report({'ERROR'}, "Unable to open raster, check logs for more infos") + return {'CANCELLED'} + except OverlapError: + self.report({'ERROR'}, "Non overlap data") + return {'CANCELLED'} + # Add UV map texture layer + mesh = obj.data + uvTxtLayer = mesh.uv_layers.new(name='rastUVmap') + uvTxtLayer.active = True + # UV mapping + geoRastUVmap(obj, uvTxtLayer, rast, dx, dy, reproj=rprjToRaster) + # Add material and texture + mat = bpy.data.materials.new('rastMat') + obj.data.materials.append(mat) + addTexture(mat, rast.bpyImg, uvTxtLayer, name='rastText') + + ###################################### + if self.importMode == 'DEM': + + # Get reference plane + if self.demOnMesh: + if not (geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH'): + self.report({'ERROR'}, "The active object is not a georef mesh") + return {'CANCELLED'} + # Get choosen object + obj = context.view_layer.objects.active + mesh = obj.data + # Compute projeted bbox (in geographic coordinates system) + subBox = getBBOX.fromObj(obj).toGeo(geoscn) + if rprj: + subBox = rprjToRaster.bbox(subBox) + else: + subBox = None + + # Load raster + try: + grid = bpyGeoRaster(filePath, subBoxGeo=subBox, clip=self.clip, fillNodata=self.fillNodata, useGDAL=HAS_GDAL, raw=True) + except IOError as e: + log.error("Unable to open raster", exc_info=True) + self.report({'ERROR'}, "Unable to open raster, check logs for more infos") + return {'CANCELLED'} + except OverlapError: + self.report({'ERROR'}, "Non overlap data") + return {'CANCELLED'} - # Load raster - try: - grid = bpyGeoRaster(filePath, subBoxGeo=subBox, clip=self.clip, fillNodata=self.fillNodata, useGDAL=HAS_GDAL, raw=True) - except IOError as e: - log.error("Unable to open raster", exc_info=True) - self.report({'ERROR'}, "Unable to open raster, check logs for more infos") - return {'CANCELLED'} - except OverlapError: - self.report({'ERROR'}, "Non overlap data") - return {'CANCELLED'} + # If needed, create a new plane object from raster extent + if not self.demOnMesh: + if not geoscn.isGeoref: + dx, dy = grid.center.x, grid.center.y + if rprj: + dx, dy = rprjToScene.pt(dx, dy) + geoscn.setOriginPrj(dx, dy) + if self.subdivision == 'mesh':#Mesh cut + mesh = exportAsMesh(grid, dx, dy, self.step, reproj=rprjToScene, flat=True) + else: + mesh = rasterExtentToMesh(name, grid, dx, dy, pxLoc='CENTER', reproj=rprjToScene) #use pixel center to avoid displacement glitch + obj = placeObj(mesh, name) + subBox = getBBOX.fromObj(obj).toGeo(geoscn) + + # Add UV map texture layer + previousUVmapIdx = mesh.uv_layers.active_index + uvTxtLayer = mesh.uv_layers.new(name='demUVmap') + #UV mapping + geoRastUVmap(obj, uvTxtLayer, grid, dx, dy, reproj=rprjToRaster) + #Restore previous uv map + if previousUVmapIdx != -1: + mesh.uv_layers.active_index = previousUVmapIdx + #Make subdivision + if self.subdivision == 'subsurf':#Add subsurf modifier + if not 'SUBSURF' in [mod.type for mod in obj.modifiers]: + subsurf = obj.modifiers.new('DEM', type='SUBSURF') + subsurf.subdivision_type = 'SIMPLE' + subsurf.levels = 6 + subsurf.render_levels = 6 + #Set displacer + dsp = setDisplacer(obj, grid, uvTxtLayer, interpolation=self.demInterpolation) + + ###################################### + if self.importMode == 'DEM_RAW': + + # Get reference plane + subBox = None + if self.clip: + if not (geoscn.isGeoref and context.view_layer.objects.active and context.view_layer.objects.active.type == 'MESH'): + self.report({'ERROR'}, "The active object is not a georef mesh") + return {'CANCELLED'} + # Get choosen object + obj = context.view_layer.objects.active + subBox = getBBOX.fromObj(obj).toGeo(geoscn) + if rprj: + subBox = rprjToRaster.bbox(subBox) + + # Load raster + try: + grid = GeoRaster(filePath, subBoxGeo=subBox, useGDAL=HAS_GDAL) + except IOError as e: + log.error("Unable to open raster", exc_info=True) + self.report({'ERROR'}, "Unable to open raster, check logs for more infos") + return {'CANCELLED'} + except OverlapError: + self.report({'ERROR'}, "Non overlap data") + return {'CANCELLED'} - # If needed, create a new plane object from raster extent - if not self.demOnMesh: if not geoscn.isGeoref: dx, dy = grid.center.x, grid.center.y if rprj: dx, dy = rprjToScene.pt(dx, dy) geoscn.setOriginPrj(dx, dy) - if self.subdivision == 'mesh':#Mesh cut - mesh = exportAsMesh(grid, dx, dy, self.step, reproj=rprjToScene, flat=True) - else: - mesh = rasterExtentToMesh(name, grid, dx, dy, pxLoc='CENTER', reproj=rprjToScene) #use pixel center to avoid displacement glitch + mesh = exportAsMesh(grid, dx, dy, self.step, reproj=rprjToScene, subset=self.clip, flat=False, buildFaces=self.buildFaces) obj = placeObj(mesh, name) - subBox = getBBOX.fromObj(obj).toGeo(geoscn) - - # Add UV map texture layer - previousUVmapIdx = mesh.uv_layers.active_index - uvTxtLayer = mesh.uv_layers.new(name='demUVmap') - #UV mapping - geoRastUVmap(obj, uvTxtLayer, grid, dx, dy, reproj=rprjToRaster) - #Restore previous uv map - if previousUVmapIdx != -1: - mesh.uv_layers.active_index = previousUVmapIdx - #Make subdivision - if self.subdivision == 'subsurf':#Add subsurf modifier - if not 'SUBSURF' in [mod.type for mod in obj.modifiers]: - subsurf = obj.modifiers.new('DEM', type='SUBSURF') - subsurf.subdivision_type = 'SIMPLE' - subsurf.levels = 6 - subsurf.render_levels = 6 - #Set displacer - dsp = setDisplacer(obj, grid, uvTxtLayer, interpolation=self.demInterpolation) - - ###################################### - if self.importMode == 'DEM_RAW': + #grid.unload() - # Get reference plane - subBox = None - if self.clip: - if not geoscn.isGeoref or len(self.objectsLst) == 0: - self.report({'ERROR'}, "No working extent") - return {'CANCELLED'} - # Get choosen object - obj = scn.objects[int(self.objectsLst)] - subBox = getBBOX.fromObj(obj).toGeo(geoscn) - if rprj: - subBox = rprjToRaster.bbox(subBox) + ###################################### - # Load raster - try: - grid = GeoRaster(filePath, subBoxGeo=subBox, useGDAL=HAS_GDAL) - except IOError as e: - log.error("Unable to open raster", exc_info=True) - self.report({'ERROR'}, "Unable to open raster, check logs for more infos") - return {'CANCELLED'} - except OverlapError: - self.report({'ERROR'}, "Non overlap data") - return {'CANCELLED'} - - if not geoscn.isGeoref: - dx, dy = grid.center.x, grid.center.y - if rprj: - dx, dy = rprjToScene.pt(dx, dy) - geoscn.setOriginPrj(dx, dy) - mesh = exportAsMesh(grid, dx, dy, self.step, reproj=rprjToScene, subset=self.clip, flat=False, buildFaces=self.buildFaces) - obj = placeObj(mesh, name) - #grid.unload() - - ###################################### - - #Flag if a new object as been created... - if self.importMode == 'PLANE' or (self.importMode == 'DEM' and not self.demOnMesh) or self.importMode == 'DEM_RAW': - newObjCreated = True - else: - newObjCreated = False + #Flag if a new object as been created... + if self.importMode == 'PLANE' or (self.importMode == 'DEM' and not self.demOnMesh) or self.importMode == 'DEM_RAW': + newObjCreated = True #...if so, maybee we need to adjust 3d view settings to it if newObjCreated and prefs.adjust3Dview: