6 from salome.smesh import smeshBuilder
10 import medcoupling as mc
17 def freeBordersGroup(meshFileIn, meshFileOut=""):
19 In a mesh, create a group of Edges for the borders: domain, internal holes (isles)
21 meshFileIn: full path of the input mesh file, format MED
22 meshFileOut: full path of the output mesh file, format MED (default="" : when "", output file is suffixed with "_brd.med"
23 return full path of the output mesh file
25 smesh = smeshBuilder.New()
26 if not salome.sg.hasDesktop():
27 smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed
28 ([MESH], status) = smesh.CreateMeshesFromMED(meshFileIn)
30 nbAdded, MESH, addedBnd = MESH.MakeBoundaryElements( SMESH.BND_1DFROM2D, '', '', 0, [])
33 aCriterion = smesh.GetCriterion(SMESH.EDGE,SMESH.FT_FreeBorders,SMESH.FT_Undefined,0)
34 aCriteria.append(aCriterion)
35 aFilter = smesh.GetFilterFromCriteria(aCriteria)
36 aFilter.SetMesh(MESH.GetMesh())
37 FreeBorders = MESH.GroupOnFilter( SMESH.EDGE, 'FreeBorders', aFilter )
39 a = os.path.splitext(meshFileIn)
40 smesh.SetName(MESH, os.path.basename(a[0]))
43 a = os.path.splitext(meshFileIn)
44 smesh.SetName(MESH, os.path.basename(a[0]))
45 meshFileOut = a[0] + '_brd' + a[1]
48 MESH.ExportMED(meshFileOut,auto_groups=0,minor=40,overwrite=1,meshPart=None,autoDimension=1)
51 print('ExportMED() failed. Invalid file name?')
55 def explodeGroup(grp, grpName):
57 from a group of edges loaded with MEDCoupling, create ordered lists of nodes, one list for each set of connected edges.
59 grp: MEDCoupling object for the group of edges
60 grpName: name of the group
62 List of descriptors [(ordered list of nodeIds, name of the list, closed status)]
64 print(" === explodeGroup", grpName)
65 nbCells=grp.getNumberOfCells()
67 dicReverse = {} # id node --> id edges
68 for i in range(nbCells):
69 nodcell = grp.getNodeIdsOfCell(i)
70 for j in range(len(nodcell)):
71 if nodcell[j] in dicReverse:
72 dicReverse[nodcell[j]].append(i)
74 dicReverse[nodcell[j]] = [i]
77 usedCells = [False] * nbCells
78 while False in usedCells:
79 icell = usedCells.index(False)
80 usedCells[icell] = True
81 nodcell = grp.getNodeIdsOfCell(icell)
83 chain = [nodcell[0], nodcell[1]]
86 while nextnode in dicReverse:
87 nextcells = dicReverse[nextnode]
88 if len(nextcells) != 2: # end of chain(1) or "edges connector"(>2): stop
93 for i in range(len(nextcells)):
95 if not usedCells[ncell]: # the chain of nodes grows
96 usedCells[ncell] = True
98 nodcell = grp.getNodeIdsOfCell(ncell)
99 if nodcell[0] == nextnode:
100 nextnode = nodcell[1] # forward edge
102 nextnode = nodcell[0] # reversed edge ?
103 chain.append(nextnode)
104 if not newcell: # end of chain, closed
107 while prevnode in dicReverse:
108 prevcells = dicReverse[prevnode]
109 if len(prevcells) != 2: # end of chain(1) or "edges connector"(>2): stop
114 for i in range(len(prevcells)):
116 if not usedCells[ncell]: # the chain of nodes grows
117 usedCells[ncell] = True
119 nodcell = grp.getNodeIdsOfCell(ncell)
120 if nodcell[1] == prevnode:
121 prevnode = nodcell[0] # forward edge
123 prevnode = nodcell[1] # reversed edge ?
124 chain.insert(0, prevnode)
125 if not newcell: # end of chain, closed
129 chainDesc = (chain, grpName +"_%s" % len(nodeChains), closed)
130 nodeChains.append(chainDesc)
135 def writeShapeLines(mcMesh, grpName, nodeChains, outputDirectory, offsetX=0., offsetY=0.):
137 from a mesh loaded in memory with MEDLoader, and a list of list of connected nodes associated to a group of edges, write a shapefile of type line, with one record per list of connected nodes.
139 mcMesh: mesh loaded in memory with MEDLoader
140 grpName: name associated to the group of edges
141 nodeChains: List of descriptors corresponding to the group [(ordered list of nodeIds, name of the list, closed status)]
142 outputDirectory: directory for writing the shapefile
143 offsetX : offset of origin X in the mesh (default 0). The shapefile is always written without local origin to be ready for a direct load in Qgis.
144 offsetY : offset of origin Y in the mesh (default 0). The shapefile is always written without local origin to be ready for a direct load in Qgis.
146 print(" === writeShapeLines", grpName)
147 coords = mcMesh.getCoords()
148 shapeFileName = os.path.join(outputDirectory, grpName)
149 w = shapefile.Writer(shapeFileName)
152 for (chain, chainName, closed) in nodeChains:
153 print(" --- ", chainName)
156 coord = coords[node].getValues()
157 coordLb93=[coord[0] + offsetX, coord[1] + offsetY]
158 chaincoords.append(coordLb93)
159 w.line([chaincoords])
164 def writeShapePoints(mcMesh, grpName, nodeChains, outputDirectory, offsetX=0., offsetY=0.):
166 from a mesh loaded in memory with MEDLoader, and a list of list of connected nodes associated to a group of edges, write a shapefile of type multi points, with one record per list of connected nodes.
168 mcMesh: mesh loaded in memory with MEDLoader
169 grpName: name associated to the group of edges
170 nodeChains: List of descriptors corresponding to the group [(ordered list of nodeIds, name of the list, closed status)]
171 outputDirectory: directory for writing the shapefile
172 offsetX : offset of origin X in the mesh (default 0). The shapefile is always written without local origin to be ready for a direct load in Qgis.
173 offsetY : offset of origin Y in the mesh (default 0). The shapefile is always written without local origin to be ready for a direct load in Qgis.
175 print(" === writeShapePoints", grpName)
176 coords = mcMesh.getCoords()
177 shapeFileName = os.path.join(outputDirectory, grpName)
178 w = shapefile.Writer(shapeFileName + '_pts')
181 for (chain, chainName, closed) in nodeChains:
182 print(" --- ", chainName)
185 coord = coords[node].getValues()
186 coordLb93=[coord[0] + offsetX, coord[1] + offsetY]
187 chaincoords.append(coordLb93)
188 w.multipoint(chaincoords)
193 def exploreEdgeGroups(meshFile, outputDirectory="", offsetX=0., offsetY=0.):
195 Find all the groups of edges in a mesh and, for each group, create one shapefile of lines and one of points. The shapefiles are created in the same system of coordinates as the mesh (For instance Lambert 93), but without origin offset (to be ready for a direct load in Qgis)
197 meshFile: full path of the input mesh file, format MED
198 outputDirectory: directory in which the shapefiles are written (default "", if "" use the directory containing the mesh
199 offsetX: local X origin of the mesh
200 offsetY: local Y origin of the mesh
202 print(" === exploreEdgeGroups", meshFile)
203 if outputDirectory == "":
204 outputDirectory = os.path.dirname(meshFile)
206 a = os.path.splitext(meshFile)
207 prefix = os.path.basename(a[0]) # prefix = file name without extension
209 mcMesh = ml.MEDFileMesh.New(meshFile)
210 dim = mcMesh.getMeshDimension()
211 d1=-1 # when dimension 2, edges are dim -1
212 if dim == 3: # when dimension 3, edges are dim -2
215 grp_names = mcMesh.getGroupsOnSpecifiedLev(d1) #names of edges groups
217 groups = [mcMesh.getGroup(d1, name) for name in grp_names] # list of groups in their name order
219 for (grp, grpName) in zip(groups, grp_names):
220 fullGrpName = prefix + '_' + grpName
221 nodeChains = explodeGroup(grp, fullGrpName)
222 writeShapeLines(mcMesh, fullGrpName, nodeChains, outputDirectory, offsetX, offsetY)
223 writeShapePoints(mcMesh, fullGrpName, nodeChains, outputDirectory, offsetX, offsetY)
226 def fitShapePointsToMesh(freeBorderShapefile, shapefileToAdjust, splitFreeBorder=True, splitShapeAdjusted=True):
228 shapeFileToAdjust must be a closed line or polygon crossing freeBorderShapefile in 2 points.
229 Find in shapeFileToAdjust and in freeBorderShapefile the two closest corresponding points and move the points in shapeFileToAdjust to correspond to the points found in freeBorderShapefile. Split shapeFileToAdjust in two parts (inside or outside freeBorder). If requested, split freeBorderShapefile in two parts. Same for shapeFileToAdjust.
231 freeBorderShapefile: a set of free border lines, as generated by the functions freeBordersGroup and exploreEdgeGroups.
232 shapefileToAdjust: a closed line or polygon, supposed to be drawn in qgis to pass as close as possible to the points to be connected, on the free border.
233 splitFreeBorder: boolean default True
234 splitShapeAdjusted: boolean default True
236 print(" === fitShapePointsToMesh", freeBorderShapefile, shapefileToAdjust)
238 # --- find domain freeBorder: bounding box englobing all others
239 # TODO: This may not be always the case, when there is not a single domain with holes, but several non connected domains.
241 fb = shapefile.Reader(freeBorderShapefile)
242 fbShapes = fb.shapes()
243 maxbbox=[1.e30, 1.e30, -1.e30, -1.e30]
245 for i,s in enumerate(fbShapes):
247 if (bb[0] < maxbbox[0] and bb[1] < maxbbox[1] and bb[2] > maxbbox[2] and bb[3] > maxbbox[3]):
250 fbs = fbShapes[outerBboxIndex] # the domain free border shape
252 # --- find the intersections of the shapefile to adjust and the domain free border:
253 # the closests points (two pairs of points)
255 sf = shapefile.Reader(shapefileToAdjust)
261 for i,p in enumerate(sfta.points):
262 d = math.sqrt((x0-p[0])*(x0-p[0]) + (y0-p[1])*(y0-p[1]))
266 continue # do not take into account superposed points in shape to adjust
269 for j,pfb in enumerate(fbs.points):
270 d = math.sqrt((pfb[0]-p[0])*(pfb[0]-p[0]) + (pfb[1]-p[1])*(pfb[1]-p[1]))
274 pdist.append((dmin, jmin, i)) # distance, index in freeBorder, index in shapeToAdjust
275 pdist.sort() # the 2 closest points must be set on the mesh freeBorder
277 i1 = min(pdist[0][2], pdist[1][2]) # index of first adjusted point in shapeToAdjust
278 i2 = max(pdist[0][2], pdist[1][2]) # index of second adjusted point in shapeToAdjust
279 if i1 == pdist[0][2]:
280 ifb1 = pdist[0][1] # index of first adjusted point in freeBorder
281 ifb2 = pdist[1][1] # index of second adjusted point in freeBorder
285 print("i1, i2, len(sfta.points)", i1, i2, len(sfta.points))
286 print("ifb1, ifb2, len(fbs.points)", ifb1, ifb2, len(fbs.points))
288 # --- write the adusted shapefile: free border closest points replaced with corresponding points
289 # on the free border. two polylines, one inside the domain, one outside
291 a = os.path.splitext(shapefileToAdjust)
292 shapefileAdjusted = a[0] + '_adj' + a[1]
293 chainName = os.path.basename(a[0])
295 w = shapefile.Writer(shapefileAdjusted)
299 if splitShapeAdjusted:
301 chaincoords.append(fbs.points[ifb1])
302 for i in range(i1+1, i2):
303 chaincoords.append(sfta.points[i])
304 chaincoords.append(fbs.points[ifb2])
305 w.line([chaincoords])
306 w.record(chainName + '_0')
309 chaincoords.append(fbs.points[ifb2])
310 if i2+1 < len(sfta.points):
311 for i in range(i2+1, len(sfta.points)):
312 chaincoords.append(sfta.points[i])
314 chaincoords.append(sfta.points[i])
315 chaincoords.append(fbs.points[ifb1])
316 w.line([chaincoords])
317 w.record(chainName + '_1')
320 chaincoords.append(fbs.points[ifb1])
321 for i in range(i1+1, i2):
322 chaincoords.append(sfta.points[i])
323 chaincoords.append(fbs.points[ifb2])
324 if i2+1 < len(sfta.points):
325 for i in range(i2+1, len(sfta.points)):
326 chaincoords.append(sfta.points[i])
328 chaincoords.append(sfta.points[i])
329 w.line([chaincoords])
335 # write the free border split in two polylines (cut by the adjusted shapefile)
337 a = os.path.splitext(freeBorderShapefile)
338 freeBorderSplit = a[0] + '_split' + a[1]
339 chainName = os.path.basename(a[0])
341 w = shapefile.Writer(freeBorderSplit)
346 i = ifb1; ifb1 = ifb2; ifb2 = i
349 for i in range(ifb1, ifb2+1):
350 chaincoords.append(fbs.points[i])
351 w.line([chaincoords])
352 w.record(chainName + '_0')
355 for i in range(ifb2, len(fbs.points)):
356 chaincoords.append(fbs.points[i])
357 for i in range(ifb1+1):
358 chaincoords.append(fbs.points[i])
359 w.line([chaincoords])
360 w.record(chainName + '_1')