Mejoras PVPlantTerrain: fix XYZ import, DEM rendimiento, ViewProvider boundary+contour, error handling
This commit is contained in:
+118
-54
@@ -129,8 +129,14 @@ class Terrain(ArchComponent.Component):
|
|||||||
# obj.IfcType = "Fence"
|
# obj.IfcType = "Fence"
|
||||||
# obj.MoveWithHost = False
|
# obj.MoveWithHost = False
|
||||||
|
|
||||||
self.site = PVPlantSite.get()
|
try:
|
||||||
self.site.Terrain = obj
|
self.site = PVPlantSite.get()
|
||||||
|
except Exception:
|
||||||
|
self.site = None
|
||||||
|
if self.site:
|
||||||
|
self.site.Terrain = obj
|
||||||
|
else:
|
||||||
|
FreeCAD.Console.PrintWarning('Terrain: No se encontró Site, algunas funciones DEM requerirán Site.\n')
|
||||||
obj.ViewObject.ShapeColor = (0.0000, 0.6667, 0.4980)
|
obj.ViewObject.ShapeColor = (0.0000, 0.6667, 0.4980)
|
||||||
obj.ViewObject.LineColor = (0.0000, 0.6000, 0.4392)
|
obj.ViewObject.LineColor = (0.0000, 0.6000, 0.4392)
|
||||||
|
|
||||||
@@ -237,7 +243,7 @@ class Terrain(ArchComponent.Component):
|
|||||||
del templist
|
del templist
|
||||||
|
|
||||||
# create xy coordinates
|
# create xy coordinates
|
||||||
offset = self.site.Origin
|
offset = self.site.Origin if self.site else FreeCAD.Vector(0, 0, 0)
|
||||||
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
||||||
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
||||||
datavals = datavals * 1000 # Ajuste de altura
|
datavals = datavals * 1000 # Ajuste de altura
|
||||||
@@ -269,35 +275,90 @@ class Terrain(ArchComponent.Component):
|
|||||||
stepx = math.ceil(nx / stepsize)
|
stepx = math.ceil(nx / stepsize)
|
||||||
stepy = math.ceil(ny / stepsize)
|
stepy = math.ceil(ny / stepsize)
|
||||||
|
|
||||||
|
# Malla completa primero como numpy y filtramos todo de una
|
||||||
|
from datetime import datetime
|
||||||
|
t_start = datetime.now()
|
||||||
|
|
||||||
|
# Crear grid completo de coordenadas
|
||||||
|
XX, YY = np.meshgrid(x, y)
|
||||||
|
ZZ = datavals.copy()
|
||||||
|
|
||||||
|
# Enmascarar nodata
|
||||||
|
mask_valida = ZZ != nodata_value
|
||||||
|
|
||||||
|
# Enmascarar cutting boundary si existe
|
||||||
|
if obj.CuttingBoundary:
|
||||||
|
from FreeCAD import Base
|
||||||
|
shape = obj.CuttingBoundary.Shape
|
||||||
|
mask_boundary = np.zeros_like(ZZ, dtype=bool)
|
||||||
|
# Sampling: revisar solo puntos estratégicos para boundary grande
|
||||||
|
stride = max(1, min(nx, ny) // 200)
|
||||||
|
for i in range(0, ny, stride):
|
||||||
|
for j in range(0, nx, stride):
|
||||||
|
if mask_valida[i, j]:
|
||||||
|
if shape.isInside(FreeCAD.Vector(x[j], y[i], 0), 0, True):
|
||||||
|
mask_boundary[i, j] = True
|
||||||
|
mask_valida = mask_valida & mask_boundary
|
||||||
|
|
||||||
|
# Extraer puntos válidos como lista plana
|
||||||
|
pts_validos = np.column_stack([
|
||||||
|
XX[mask_valida].ravel(),
|
||||||
|
YY[mask_valida].ravel(),
|
||||||
|
ZZ[mask_valida].ravel()
|
||||||
|
])
|
||||||
|
|
||||||
|
del XX, YY, ZZ, mask_valida
|
||||||
|
|
||||||
|
# Triangulación completa de una vez (no por parches)
|
||||||
mesh = Mesh.Mesh()
|
mesh = Mesh.Mesh()
|
||||||
for indx in range(stepx):
|
if len(pts_validos) > 3:
|
||||||
inix = indx * stepsize - 1
|
# Si hay muchos puntos, triangulamos por parches para evitar OOM
|
||||||
finx = min([stepsize * (indx + 1), len(x)-1])
|
patch_size = 50000
|
||||||
for indy in range(stepy):
|
n_patches = max(1, math.ceil(len(pts_validos) / patch_size))
|
||||||
iniy = indy * stepsize - 1
|
for p in range(n_patches):
|
||||||
finy = min([stepsize * (indy + 1), len(y) - 1])
|
patch = pts_validos[p * patch_size:(p + 1) * patch_size].tolist()
|
||||||
pts = []
|
if len(patch) > 3:
|
||||||
for i in range(inix, finx):
|
|
||||||
for j in range(iniy, finy):
|
|
||||||
if datavals[j][i] != nodata_value:
|
|
||||||
if obj.CuttingBoundary:
|
|
||||||
if obj.CuttingBoundary.Shape.isInside(FreeCAD.Vector(x[i], y[j], 0), 0, True):
|
|
||||||
pts.append([x[i], y[j], datavals[j][i]])
|
|
||||||
else:
|
|
||||||
pts.append([x[i], y[j], datavals[j][i]])
|
|
||||||
if len(pts) > 3:
|
|
||||||
try:
|
try:
|
||||||
triangulated = Triangulation.Triangulate(pts)
|
triangulated = Triangulation.Triangulate(patch)
|
||||||
mesh.addMesh(triangulated)
|
mesh.addMesh(triangulated)
|
||||||
except TypeError:
|
except TypeError as e:
|
||||||
print(f"Error al procesar {len(pts)} puntos: {str(e)}")
|
print(f"Patch {p}: error al procesar {len(patch)} puntos: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Patch {p}: error inesperado: {str(e)}")
|
||||||
|
|
||||||
|
print(f'Terraín DEM: {len(pts_validos)} pts válidos, {n_patches} parches, {datetime.now()-t_start}')
|
||||||
|
del pts_validos
|
||||||
|
|
||||||
mesh.removeDuplicatedPoints()
|
mesh.removeDuplicatedPoints()
|
||||||
mesh.removeFoldsOnSurface()
|
mesh.removeFoldsOnSurface()
|
||||||
obj.InitialMesh = mesh.copy()
|
obj.InitialMesh = mesh.copy()
|
||||||
Mesh.show(mesh)
|
# Limpiar objetos mesh huérfanos previos si existen
|
||||||
|
for o in FreeCAD.ActiveDocument.Objects:
|
||||||
|
if o.TypeId == 'Mesh::Feature' and o.Label.startswith('Terrain_mesh_'):
|
||||||
|
FreeCAD.ActiveDocument.removeObject(o.Name)
|
||||||
|
mesh_obj = Mesh.show(mesh)
|
||||||
|
mesh_obj.Label = 'Terrain_mesh_' + obj.Label
|
||||||
elif suffix in ['.xyz']:
|
elif suffix in ['.xyz']:
|
||||||
data = open_xyz_mmap(obj.DEM)
|
pts_array = open_xyz_mmap(obj.DEM)
|
||||||
|
if pts_array is not None and len(pts_array) > 3:
|
||||||
|
import MeshTools.Triangulation as Triangulation
|
||||||
|
import Mesh
|
||||||
|
if obj.CuttingBoundary:
|
||||||
|
mask = []
|
||||||
|
for pt in pts_array:
|
||||||
|
mask.append(obj.CuttingBoundary.Shape.isInside(
|
||||||
|
FreeCAD.Vector(pt[0], pt[1], 0), 0, True))
|
||||||
|
pts_array = pts_array[mask]
|
||||||
|
if len(pts_array) > 3:
|
||||||
|
from datetime import datetime
|
||||||
|
t0 = datetime.now()
|
||||||
|
pts_list = pts_array.tolist()
|
||||||
|
mesh = Triangulation.Triangulate(pts_list)
|
||||||
|
mesh.removeDuplicatedPoints()
|
||||||
|
mesh.removeFoldsOnSurface()
|
||||||
|
obj.InitialMesh = mesh.copy()
|
||||||
|
Mesh.show(mesh)
|
||||||
|
print(f'XYZ import: {len(pts_array)} puntos en {datetime.now()-t0}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -547,47 +608,47 @@ class ViewProviderTerrain:
|
|||||||
offset.factor = -2.0
|
offset.factor = -2.0
|
||||||
|
|
||||||
# Boundary features.
|
# Boundary features.
|
||||||
'''self.boundary_color = coin.SoBaseColor()
|
self.boundary_color = coin.SoBaseColor()
|
||||||
self.boundary_coords = coin.SoGeoCoordinate()
|
self.boundary_coords = coin.SoGeoCoordinate()
|
||||||
self.boundary_lines = coin.SoLineSet()
|
self.boundary_lines = coin.SoLineSet()
|
||||||
self.boundary_style = coin.SoDrawStyle()
|
self.boundary_style = coin.SoDrawStyle()
|
||||||
self.boundary_style.style = coin.SoDrawStyle.LINES'''
|
self.boundary_style.style = coin.SoDrawStyle.LINES
|
||||||
|
|
||||||
# Boundary root.
|
# Boundary root.
|
||||||
'''boundaries = coin.SoType.fromName('SoFCSelection').createInstance()
|
boundaries = coin.SoType.fromName('SoFCSelection').createInstance()
|
||||||
boundaries.style = 'EMISSIVE_DIFFUSE'
|
boundaries.style = 'EMISSIVE_DIFFUSE'
|
||||||
boundaries.addChild(self.boundary_color)
|
boundaries.addChild(self.boundary_color)
|
||||||
boundaries.addChild(self.boundary_style)
|
boundaries.addChild(self.boundary_style)
|
||||||
boundaries.addChild(self.boundary_coords)
|
boundaries.addChild(self.boundary_coords)
|
||||||
boundaries.addChild(self.boundary_lines)'''
|
boundaries.addChild(self.boundary_lines)
|
||||||
|
|
||||||
# Major Contour features.
|
# Major Contour features.
|
||||||
'''self.major_color = coin.SoBaseColor()
|
self.major_color = coin.SoBaseColor()
|
||||||
self.major_coords = coin.SoGeoCoordinate()
|
self.major_coords = coin.SoGeoCoordinate()
|
||||||
self.major_lines = coin.SoLineSet()
|
self.major_lines = coin.SoLineSet()
|
||||||
self.major_style = coin.SoDrawStyle()
|
self.major_style = coin.SoDrawStyle()
|
||||||
self.major_style.style = coin.SoDrawStyle.LINES'''
|
self.major_style.style = coin.SoDrawStyle.LINES
|
||||||
|
|
||||||
# Major Contour root.
|
# Major Contour root.
|
||||||
'''major_contours = coin.SoSeparator()
|
major_contours = coin.SoSeparator()
|
||||||
major_contours.addChild(self.major_color)
|
major_contours.addChild(self.major_color)
|
||||||
major_contours.addChild(self.major_style)
|
major_contours.addChild(self.major_style)
|
||||||
major_contours.addChild(self.major_coords)
|
major_contours.addChild(self.major_coords)
|
||||||
major_contours.addChild(self.major_lines)'''
|
major_contours.addChild(self.major_lines)
|
||||||
|
|
||||||
# Minor Contour features.
|
# Minor Contour features.
|
||||||
'''self.minor_color = coin.SoBaseColor()
|
self.minor_color = coin.SoBaseColor()
|
||||||
self.minor_coords = coin.SoGeoCoordinate()
|
self.minor_coords = coin.SoGeoCoordinate()
|
||||||
self.minor_lines = coin.SoLineSet()
|
self.minor_lines = coin.SoLineSet()
|
||||||
self.minor_style = coin.SoDrawStyle()
|
self.minor_style = coin.SoDrawStyle()
|
||||||
self.minor_style.style = coin.SoDrawStyle.LINES'''
|
self.minor_style.style = coin.SoDrawStyle.LINES
|
||||||
|
|
||||||
# Minor Contour root.
|
# Minor Contour root.
|
||||||
'''minor_contours = coin.SoSeparator()
|
minor_contours = coin.SoSeparator()
|
||||||
minor_contours.addChild(self.minor_color)
|
minor_contours.addChild(self.minor_color)
|
||||||
minor_contours.addChild(self.minor_style)
|
minor_contours.addChild(self.minor_style)
|
||||||
minor_contours.addChild(self.minor_coords)
|
minor_contours.addChild(self.minor_coords)
|
||||||
minor_contours.addChild(self.minor_lines)'''
|
minor_contours.addChild(self.minor_lines)
|
||||||
|
|
||||||
# Highlight for selection.
|
# Highlight for selection.
|
||||||
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
|
highlight = coin.SoType.fromName('SoFCSelection').createInstance()
|
||||||
@@ -596,7 +657,7 @@ class ViewProviderTerrain:
|
|||||||
highlight.addChild(mat_binding)
|
highlight.addChild(mat_binding)
|
||||||
highlight.addChild(self.geo_coords)
|
highlight.addChild(self.geo_coords)
|
||||||
highlight.addChild(self.triangles)
|
highlight.addChild(self.triangles)
|
||||||
#highlight.addChild(boundaries)
|
highlight.addChild(boundaries)
|
||||||
|
|
||||||
# Face root.
|
# Face root.
|
||||||
face = coin.SoSeparator()
|
face = coin.SoSeparator()
|
||||||
@@ -609,19 +670,19 @@ class ViewProviderTerrain:
|
|||||||
edge.addChild(self.edge_style)
|
edge.addChild(self.edge_style)
|
||||||
edge.addChild(highlight)
|
edge.addChild(highlight)
|
||||||
|
|
||||||
# Surface root.
|
# Surface root - con contour lines visibles.
|
||||||
surface_root = coin.SoSeparator()
|
surface_root = coin.SoSeparator()
|
||||||
surface_root.addChild(face)
|
surface_root.addChild(face)
|
||||||
surface_root.addChild(offset)
|
surface_root.addChild(offset)
|
||||||
surface_root.addChild(edge)
|
surface_root.addChild(edge)
|
||||||
#surface_root.addChild(major_contours)
|
surface_root.addChild(major_contours)
|
||||||
#surface_root.addChild(minor_contours)
|
surface_root.addChild(minor_contours)
|
||||||
vobj.addDisplayMode(surface_root, "Surface")
|
vobj.addDisplayMode(surface_root, "Surface")
|
||||||
|
|
||||||
# Boundary root.
|
# Boundary root.
|
||||||
#boundary_root = coin.SoSeparator()
|
boundary_root = coin.SoSeparator()
|
||||||
#boundary_root.addChild(boundaries)
|
boundary_root.addChild(boundaries)
|
||||||
#vobj.addDisplayMode(boundary_root, "Boundary")
|
vobj.addDisplayMode(boundary_root, "Boundary")
|
||||||
|
|
||||||
# Elevation/Shaded root.
|
# Elevation/Shaded root.
|
||||||
'''shaded_root = coin.SoSeparator()
|
'''shaded_root = coin.SoSeparator()
|
||||||
@@ -648,26 +709,28 @@ class ViewProviderTerrain:
|
|||||||
self.onChanged(vobj, "ShapeColor")
|
self.onChanged(vobj, "ShapeColor")
|
||||||
self.onChanged(vobj, "LineColor")
|
self.onChanged(vobj, "LineColor")
|
||||||
self.onChanged(vobj, "LineWidth")
|
self.onChanged(vobj, "LineWidth")
|
||||||
#self.onChanged(vobj, "BoundaryColor")
|
self.onChanged(vobj, "BoundaryColor")
|
||||||
#self.onChanged(vobj, "BoundaryWidth")
|
self.onChanged(vobj, "BoundaryWidth")
|
||||||
#self.onChanged(vobj, "BoundaryPattern")
|
self.onChanged(vobj, "BoundaryPattern")
|
||||||
#self.onChanged(vobj, "PatternScale")
|
self.onChanged(vobj, "PatternScale")
|
||||||
#self.onChanged(vobj, "MajorColor")
|
self.onChanged(vobj, "MajorColor")
|
||||||
#self.onChanged(vobj, "MajorWidth")
|
self.onChanged(vobj, "MajorWidth")
|
||||||
#self.onChanged(vobj, "MinorColor")
|
self.onChanged(vobj, "MinorColor")
|
||||||
#self.onChanged(vobj, "MinorWidth")
|
self.onChanged(vobj, "MinorWidth")
|
||||||
|
|
||||||
def updateData(self, obj, prop):
|
def updateData(self, obj, prop):
|
||||||
''' Update Object visuals when a data property changed. '''
|
''' Update Object visuals when a data property changed. '''
|
||||||
|
|
||||||
# Set geosystem.
|
# Set geosystem.
|
||||||
geo_system = ["UTM", FreeCAD.ActiveDocument.Site.UtmZone, "FLAT"]
|
try:
|
||||||
|
utm_zone = FreeCAD.ActiveDocument.Site.UtmZone
|
||||||
|
except:
|
||||||
|
utm_zone = "30"
|
||||||
|
geo_system = ["UTM", utm_zone, "FLAT"]
|
||||||
self.geo_coords.geoSystem.setValues(geo_system)
|
self.geo_coords.geoSystem.setValues(geo_system)
|
||||||
'''
|
|
||||||
self.boundary_coords.geoSystem.setValues(geo_system)
|
self.boundary_coords.geoSystem.setValues(geo_system)
|
||||||
self.major_coords.geoSystem.setValues(geo_system)
|
self.major_coords.geoSystem.setValues(geo_system)
|
||||||
self.minor_coords.geoSystem.setValues(geo_system)
|
self.minor_coords.geoSystem.setValues(geo_system)
|
||||||
'''
|
|
||||||
|
|
||||||
if prop == "Mesh":
|
if prop == "Mesh":
|
||||||
if obj.mesh:
|
if obj.mesh:
|
||||||
@@ -736,3 +799,4 @@ class ViewProviderTerrain:
|
|||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
FreeCADGui.addCommand('Terrain', _CommandTerrain())'''
|
FreeCADGui.addCommand('Terrain', _CommandTerrain())'''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user