source: anuga_core/source/anuga/pmesh/mesh_interface.py @ 7711

Last change on this file since 7711 was 7711, checked in by hudson, 14 years ago

Refactored geometry classes to live in their own folder.

File size: 12.7 KB
Line 
1
2from anuga.coordinate_transforms.geo_reference import Geo_reference,DEFAULT_ZONE
3from anuga.geometry.polygon import  point_in_polygon ,populate_polygon
4from anuga.utilities.numerical_tools import ensure_numeric
5import numpy as num
6from anuga.geometry.polygon import inside_polygon
7from anuga.geometry.polygon import polylist2points_verts
8import anuga.utilities.log as log
9
10
11# This is due to pmesh being a package and a module and
12# the current dir being unknown
13try:
14    from anuga.pmesh.mesh import Mesh
15except ImportError: 
16    from mesh import Mesh
17
18import exceptions
19class PolygonError(exceptions.Exception): pass
20class SegmentError(exceptions.Exception): pass
21
22def create_mesh_from_regions(bounding_polygon,
23                             boundary_tags,
24                             maximum_triangle_area=None,
25                             filename=None,
26                             interior_regions=None,
27                             interior_holes=None,
28                             poly_geo_reference=None,
29                             mesh_geo_reference=None,
30                             minimum_triangle_angle=28.0,
31                             fail_if_polygons_outside=True,
32                             breaklines=None,
33                             use_cache=False,
34                             verbose=True):
35    """Create mesh from bounding polygons, and resolutions.
36
37    bounding_polygon is a list of points in Eastings and Northings,
38    relative to the poly_geo_reference.
39
40    Boundary tags is a dictionary of symbolic tags. For every tag there
41    is a list of indices referring to segments associated with that tag.
42    If a segment is omitted an Exception will be raised.
43
44    maximum_triangle_area is the maximal area per triangle
45    for the bounding polygon, excluding the  interior regions.
46
47    Interior_regions is a list of tuples consisting of (polygon,
48    resolution) for each region to be separately refined. Do not have
49    polygon lines cross or be on-top of each other.  Also do not have
50    polygon close to each other.
51   
52    NOTE: If a interior_region is outside the bounding_polygon it should
53    throw an error
54   
55    Interior_holes is a list of ploygons for each hole.
56
57    This function does not allow segments to share points - use underlying
58    pmesh functionality for that
59
60    poly_geo_reference is the geo_reference of the bounding polygon and
61    the interior polygons.
62    If none, assume absolute.  Please pass one though, since absolute
63    references have a zone.
64   
65    mesh_geo_reference is the geo_reference of the mesh to be created.
66    If none is given one will be automatically generated.  It was use
67    the lower left hand corner of  bounding_polygon (absolute)
68    as the x and y values for the geo_ref.
69
70    breaklines is a list of polygons. These lines will be preserved by the
71               triangulation algorithm - useful for coastlines, walls, etc.
72               The polygons are not closed.                       
73   
74    Returns the mesh instance if no filename is given
75
76    Note, interior regions should be fully nested, as overlaps may cause
77    unintended resolutions.
78
79    fail_if_polygons_outside: If True (the default) Exception in thrown
80    where interior polygons fall outside bounding polygon. If False, these
81    will be ignored and execution continued.
82       
83   
84    """
85   
86    # Build arguments and keyword arguments for use with caching or apply.
87    args = (bounding_polygon,
88            boundary_tags)
89   
90    kwargs = {'maximum_triangle_area': maximum_triangle_area,
91              'filename': filename,
92              'interior_regions': interior_regions,
93              'interior_holes': interior_holes,
94              'poly_geo_reference': poly_geo_reference,
95              'mesh_geo_reference': mesh_geo_reference,
96              'minimum_triangle_angle': minimum_triangle_angle,
97              'fail_if_polygons_outside': fail_if_polygons_outside,
98              'breaklines': breaklines,
99              'verbose': verbose}   # FIXME (Ole): Should be bypassed one day. See ticket:14
100
101    # Call underlying engine with or without caching
102    if use_cache is True:
103        try:
104            from anuga.caching import cache
105        except:
106            msg = 'Caching was requested, but caching module'+\
107                  'could not be imported'
108            raise msg
109
110
111        m = cache(_create_mesh_from_regions,
112                  args, kwargs,
113                  verbose=verbose,
114                  compression=False)
115    else:
116        m = apply(_create_mesh_from_regions,
117                  args, kwargs)
118
119    return m   
120       
121
122
123def _create_mesh_from_regions(bounding_polygon,
124                              boundary_tags,
125                              maximum_triangle_area=None,
126                              filename=None,                             
127                              interior_regions=None,
128                              interior_holes=None,
129                              poly_geo_reference=None,
130                              mesh_geo_reference=None,
131                              minimum_triangle_angle=28.0,
132                              fail_if_polygons_outside=True,
133                              breaklines=None,
134                              verbose=True):
135    """_create_mesh_from_regions - internal function.
136
137    See create_mesh_from_regions for documentation.
138    """
139
140   
141    # check the segment indexes - throw an error if they are out of bounds
142    if boundary_tags is not None:
143        max_points = len(bounding_polygon)
144        for key in boundary_tags.keys():
145            if len([x for x in boundary_tags[key] if x > max_points-1]) >= 1:
146                msg = 'Boundary tag %s has segment out of bounds. '\
147                      %(str(key))
148                msg += 'Number of points in bounding polygon = %d' % max_points
149                raise SegmentError, msg
150
151        for i in range(max_points):
152            found = False
153            for tag in boundary_tags:
154                if i in boundary_tags[tag]:
155                    found = True
156            if found is False:
157                msg = 'Segment %d was not assigned a boundary_tag.' % i
158                msg +=  'Default tag "exterior" will be assigned to missing segment'
159                #raise Exception, msg
160                # Fixme: Use proper Python warning
161                if verbose: log.critical('WARNING: %s' % msg)
162               
163
164   
165    #In addition I reckon the polygons could be of class Geospatial_data
166    #(DSG) If polygons were classes caching would break in places.
167
168    # Simple check
169    bounding_polygon = ensure_numeric(bounding_polygon, num.float)
170    msg = 'Bounding polygon must be a list of points or an Nx2 array'
171    assert len(bounding_polygon.shape) == 2, msg
172    assert bounding_polygon.shape[1] == 2, msg   
173
174    #
175    if interior_regions is not None:
176       
177        # Test that all the interior polygons are inside the
178        # bounding_poly and throw out those that aren't fully
179        # included.  #Note, Both poly's have the same geo_ref,
180        # therefore don't take into account # geo_ref
181
182
183        polygons_inside_boundary = []
184        for interior_polygon, res in interior_regions:
185            indices = inside_polygon(interior_polygon, bounding_polygon,
186                                     closed = True, verbose = False)
187   
188            if len(indices) <> len(interior_polygon): 
189                msg = 'Interior polygon %s is not fully inside'\
190                      %(str(interior_polygon))
191                msg += ' bounding polygon: %s.' %(str(bounding_polygon))
192
193                if fail_if_polygons_outside is True:
194                    raise PolygonError, msg                   
195                else:
196                    msg += ' I will ignore it.'
197                    log.critical(msg)
198
199            else:
200                polygons_inside_boundary.append([interior_polygon, res])
201               
202        # Record only those that were fully contained       
203        interior_regions = polygons_inside_boundary
204
205           
206   
207# the following segment of code could be used to Test that all the
208# interior polygons are inside the bounding_poly... however it might need
209# to be change a bit   
210#
211#count = 0
212#for i in range(len(interior_regions)):
213#    region = interior_regions[i]
214#    interior_polygon = region[0]
215#    if len(inside_polygon(interior_polygon, bounding_polygon,
216#                   closed = True, verbose = False)) <> len(interior_polygon):
217#        print 'WARNING: interior polygon %d is outside bounding polygon' %(i)
218#        count += 1
219
220#if count == 0:
221#    print 'interior regions OK'
222#else:
223#    print 'check out your interior polygons'
224#    print 'check %s in production directory' %figname
225#    import sys; sys.exit()
226
227    if interior_holes is not None:       
228        # Test that all the interior polygons are inside the bounding_poly
229        for interior_polygon in interior_holes:
230            indices = inside_polygon(interior_polygon, bounding_polygon,
231                                     closed = True, verbose = False)
232   
233            if len(indices) <> len(interior_polygon): 
234                msg = 'Interior polygon %s is outside bounding polygon: %s'\
235                      %(str(interior_polygon), str(bounding_polygon))
236                raise PolygonError, msg
237
238    # Resolve geo referencing       
239    if mesh_geo_reference is None:
240        xllcorner = min(bounding_polygon[:,0])
241        yllcorner = min(bounding_polygon[:,1])   
242        #
243        if poly_geo_reference is None:
244            zone = DEFAULT_ZONE
245        else:
246            zone = poly_geo_reference.get_zone()
247            [(xllcorner,yllcorner)] = poly_geo_reference.get_absolute( \
248            [(xllcorner,yllcorner)])
249        # create a geo_ref, based on the llc of the bounding_polygon
250        mesh_geo_reference = Geo_reference(xllcorner = xllcorner,
251                                           yllcorner = yllcorner,
252                                           zone = zone)
253
254    m = Mesh(geo_reference=mesh_geo_reference)
255
256    # build a list of discrete segments from the breakline polygons
257    if breaklines is not None:
258        points, verts = polylist2points_verts(breaklines)
259        m.add_points_and_segments(points, verts)
260
261    # Do bounding polygon
262    m.add_region_from_polygon(bounding_polygon,
263                              segment_tags=boundary_tags,
264                              geo_reference=poly_geo_reference)
265
266    # Find one point inside region automatically
267    if interior_regions is not None:
268        excluded_polygons = []       
269        for polygon, res in interior_regions:
270            excluded_polygons.append( polygon )
271    else:
272        excluded_polygons = None
273
274    # Convert bounding poly to absolute values
275    # this sort of thing can be fixed with the geo_points class
276    if poly_geo_reference is not None:
277        bounding_polygon_absolute = \
278            poly_geo_reference.get_absolute(bounding_polygon)
279    else:
280        bounding_polygon_absolute = bounding_polygon
281   
282    inner_point = point_in_polygon(bounding_polygon_absolute)
283    inner = m.add_region(inner_point[0], inner_point[1])
284    inner.setMaxArea(maximum_triangle_area)
285
286    # Do interior regions
287#    if interior_regions is not None:   
288#        for polygon, res in interior_regions:
289#            m.add_region_from_polygon(polygon,
290#                                      geo_reference=poly_geo_reference)
291#            # convert bounding poly to absolute values
292#            if poly_geo_reference is not None:
293#                polygon_absolute = \
294#                    poly_geo_reference.get_absolute(polygon)
295#            else:
296#                polygon_absolute = polygon
297#            inner_point = point_in_polygon(polygon_absolute)
298#            region = m.add_region(inner_point[0], inner_point[1])
299#            region.setMaxArea(res)
300           
301           
302    if interior_regions is not None:   
303        for polygon, res in interior_regions:
304            m.add_region_from_polygon(polygon,
305                                      max_triangle_area=res,
306                                      geo_reference=poly_geo_reference)
307   
308           
309    # Do interior holes
310    if interior_holes is not None:   
311        for polygon in interior_holes:
312            m.add_hole_from_polygon(polygon,
313                                    geo_reference=poly_geo_reference)
314       
315
316
317    # NOTE (Ole): This was moved here as it is annoying if mesh is always
318    # stored irrespective of whether the computation
319    # was cached or not. This caused Domain to
320    # recompute as it has meshfile as a dependency
321
322    # Decide whether to store this mesh or return it   
323    if filename is None:
324        return m
325    else:
326        if verbose: log.critical("Generating mesh to file '%s'" % filename)
327        m.generate_mesh(minimum_triangle_angle=minimum_triangle_angle,
328                        verbose=verbose)
329        m.export_mesh_file(filename)
330
331
Note: See TracBrowser for help on using the repository browser.