1"""@package grass.temporal
2
3Temporal vector algebra
4
5(C) 2014 by the GRASS Development Team
6This program is free software under the GNU General Public
7License (>=v2). Read the file COPYING that comes with GRASS
8for details.
9
10:authors: Thomas Leppelt and Soeren Gebbert
11
12.. code-block:: python
13
14    >>> import grass.temporal as tgis
15    >>> tgis.init(True)
16    >>> p = tgis.TemporalVectorAlgebraLexer()
17    >>> p.build()
18    >>> p.debug = True
19    >>> expression =  'E = A : B ^ C : D'
20    >>> p.test(expression)
21    E = A : B ^ C : D
22    LexToken(NAME,'E',1,0)
23    LexToken(EQUALS,'=',1,2)
24    LexToken(NAME,'A',1,4)
25    LexToken(T_SELECT,':',1,6)
26    LexToken(NAME,'B',1,8)
27    LexToken(XOR,'^',1,10)
28    LexToken(NAME,'C',1,12)
29    LexToken(T_SELECT,':',1,14)
30    LexToken(NAME,'D',1,16)
31    >>> expression =  'E = buff_a(A, 10)'
32    >>> p.test(expression)
33    E = buff_a(A, 10)
34    LexToken(NAME,'E',1,0)
35    LexToken(EQUALS,'=',1,2)
36    LexToken(BUFF_AREA,'buff_a',1,4)
37    LexToken(LPAREN,'(',1,10)
38    LexToken(NAME,'A',1,11)
39    LexToken(COMMA,',',1,12)
40    LexToken(INT,10,1,14)
41    LexToken(RPAREN,')',1,16)
42
43"""
44from __future__ import print_function
45
46try:
47    import ply.lex as lex
48    import ply.yacc as yacc
49except:
50    pass
51
52import grass.pygrass.modules as pygrass
53
54import copy
55from .temporal_algebra import TemporalAlgebraLexer, TemporalAlgebraParser, GlobalTemporalVar
56from .core import init_dbif, get_current_mapset
57from .abstract_dataset import AbstractDatasetComparisonKeyStartTime
58from .open_stds import open_new_stds
59from .spatio_temporal_relationships import SpatioTemporalTopologyBuilder
60from .space_time_datasets import VectorDataset
61
62
63##############################################################################
64
65class TemporalVectorAlgebraLexer(TemporalAlgebraLexer):
66    """Lexical analyzer for the GRASS GIS temporal vector algebra"""
67
68    def __init__(self):
69        TemporalAlgebraLexer.__init__(self)
70
71    # Buffer functions from v.buffer
72    vector_buff_functions = {
73       'buff_p'  : 'BUFF_POINT',
74       'buff_l'   : 'BUFF_LINE',
75       'buff_a'   : 'BUFF_AREA',
76       }
77
78    # This is the list of token names.
79    vector_tokens = (
80        'DISOR',
81        'XOR',
82        'NOT',
83        'T_OVERLAY_OPERATOR',
84    )
85
86    # Build the token list
87    tokens = TemporalAlgebraLexer.tokens \
88                    + vector_tokens \
89                    + tuple(vector_buff_functions.values())
90
91    # Regular expression rules for simple tokens
92    t_DISOR              = r'\+'
93    t_XOR                = r'\^'
94    t_NOT                = r'\~'
95    #t_T_OVERLAY_OPERATOR = r'\{([a-zA-Z\|]+[,])?([\|&+=]?[\|&+=\^\~])\}'
96    t_T_OVERLAY_OPERATOR = r'\{[\|&+\^\~][,]?[a-zA-Z\| ]*([,])?([lrudi]|left|right|union|disjoint|intersect)?\}'
97
98    # Parse symbols
99    def temporal_symbol(self, t):
100        # Check for reserved words
101        if t.value in TemporalVectorAlgebraLexer.time_functions.keys():
102            t.type = TemporalVectorAlgebraLexer.time_functions.get(t.value)
103        elif t.value in TemporalVectorAlgebraLexer.datetime_functions.keys():
104            t.type = TemporalVectorAlgebraLexer.datetime_functions.get(t.value)
105        elif t.value in TemporalVectorAlgebraLexer.conditional_functions.keys():
106            t.type = TemporalVectorAlgebraLexer.conditional_functions.get(t.value)
107        elif t.value in TemporalVectorAlgebraLexer.vector_buff_functions.keys():
108            t.type = TemporalVectorAlgebraLexer.vector_buff_functions.get(t.value)
109        else:
110            t.type = 'NAME'
111        return t
112
113##############################################################################
114
115class TemporalVectorAlgebraParser(TemporalAlgebraParser):
116    """The temporal algebra class"""
117
118    # Get the tokens from the lexer class
119    tokens = TemporalVectorAlgebraLexer.tokens
120
121    # Setting equal precedence level for select and hash operations.
122    precedence = (
123        ('left', 'T_SELECT_OPERATOR', 'T_SELECT', 'T_NOT_SELECT',  'T_HASH_OPERATOR',  'HASH'), # 1
124        ('left', 'AND', 'OR', 'T_COMP_OPERATOR', 'T_OVERLAY_OPERATOR', 'DISOR', \
125          'NOT', 'XOR'), #2
126        )
127
128    def __init__(self, pid=None, run=False, debug=True, spatial = False):
129        TemporalAlgebraParser.__init__(self, pid, run, debug, spatial)
130
131        self.m_overlay = pygrass.Module('v.overlay', quiet=True, run_=False)
132        self.m_rename = pygrass.Module('g.rename', quiet=True, run_=False)
133        self.m_patch = pygrass.Module('v.patch', quiet=True, run_=False)
134        self.m_mremove = pygrass.Module('g.remove', quiet=True, run_=False)
135        self.m_buffer = pygrass.Module('v.buffer', quiet=True, run_=False)
136
137    def parse(self, expression, basename = None, overwrite = False):
138        # Check for space time dataset type definitions from temporal algebra
139        l = TemporalVectorAlgebraLexer()
140        l.build()
141        l.lexer.input(expression)
142
143        while True:
144            tok = l.lexer.token()
145            if not tok: break
146
147            if tok.type == "STVDS" or tok.type == "STRDS" or tok.type == "STR3DS":
148                raise SyntaxError("Syntax error near '%s'" %(tok.type))
149
150        self.lexer = TemporalVectorAlgebraLexer()
151        self.lexer.build()
152        self.parser = yacc.yacc(module=self, debug=self.debug, write_tables=False)
153
154        self.overwrite = overwrite
155        self.count = 0
156        self.stdstype = "stvds"
157        self.maptype = "vector"
158        self.mapclass = VectorDataset
159        self.basename = basename
160        self.expression = expression
161        self.parser.parse(expression)
162
163    ######################### Temporal functions ##############################
164
165    def build_spatio_temporal_topology_list(self, maplistA, maplistB = None, topolist = ["EQUAL"],
166                                            assign_val = False, count_map = False, compare_bool = False,
167                                            compare_cmd = False, compop = None, aggregate = None,
168                                            new = False, convert = False, overlay_cmd = False):
169        """Build temporal topology for two space time data sets, copy map objects
170          for given relation into map list.
171
172          :param maplistA: List of maps.
173          :param maplistB: List of maps.
174          :param topolist: List of strings of temporal relations.
175          :param assign_val: Boolean for assigning a boolean map value based on
176                            the map_values from the compared map list by
177                            topological relationships.
178          :param count_map: Boolean if the number of topological related maps
179                           should be returned.
180          :param compare_bool: Boolean for comparing boolean map values based on
181                            related map list and compariosn operator.
182          :param compare_cmd: Boolean for comparing command list values based on
183                            related map list and compariosn operator.
184          :param compop: Comparison operator, && or ||.
185          :param aggregate: Aggregation operator for relation map list, & or |.
186          :param new: Boolean if new temporary maps should be created.
187          :param convert: Boolean if conditional values should be converted to
188                        r.mapcalc command strings.
189          :param overlay_cmd: Boolean for aggregate overlay operators implicitly
190                        in command list values based on related map lists.
191
192          :return: List of maps from maplistA that fulfil the topological relationships
193                  to maplistB specified in topolist.
194        """
195        topologylist = ["EQUAL", "FOLLOWS", "PRECEDES", "OVERLAPS", "OVERLAPPED", \
196                        "DURING", "STARTS", "FINISHES", "CONTAINS", "STARTED", \
197                        "FINISHED"]
198        complementdict = {"EQUAL": "EQUAL", "FOLLOWS" : "PRECEDES",
199                          "PRECEDES" : "FOLLOWS", "OVERLAPS" : "OVERLAPPED",
200                          "OVERLAPPED" : "OVERLAPS", "DURING" : "CONTAINS",
201                          "CONTAINS" : "DURING", "STARTS" : "STARTED",
202                          "STARTED" : "STARTS", "FINISHES" : "FINISHED",
203                          "FINISHED" : "FINISHES"}
204        resultdict = {}
205        # Check if given temporal relation are valid.
206        for topo in topolist:
207          if topo.upper() not in topologylist:
208              raise SyntaxError("Unpermitted temporal relation name '" + topo + "'")
209
210        # Create temporal topology for maplistA to maplistB.
211        tb = SpatioTemporalTopologyBuilder()
212        # Dictionary with different spatial variables used for topology builder.
213        spatialdict = {'strds' : '2D', 'stvds' : '2D', 'str3ds' : '3D'}
214        # Build spatial temporal topology
215        if self.spatial:
216            tb.build(maplistA, maplistB, spatial = spatialdict[self.stdstype])
217        else:
218            tb.build(maplistA, maplistB)
219        # Iterate through maps in maplistA and search for relationships given
220        # in topolist.
221        for map_i in maplistA:
222            tbrelations = map_i.get_temporal_relations()
223            # Check for boolean parameters for further calculations.
224            if assign_val:
225                self.assign_bool_value(map_i,  tbrelations,  topolist)
226            elif compare_bool:
227                self.compare_bool_value(map_i,  tbrelations, compop, aggregate, topolist)
228            elif compare_cmd:
229                self.compare_cmd_value(map_i,  tbrelations, compop, aggregate, topolist, convert)
230            elif overlay_cmd:
231                self.overlay_cmd_value(map_i,  tbrelations, compop, topolist)
232
233            for topo in topolist:
234                if topo.upper() in tbrelations.keys():
235                    if count_map:
236                        relationmaplist = tbrelations[topo.upper()]
237                        gvar = GlobalTemporalVar()
238                        gvar.td = len(relationmaplist)
239                        if "map_value" in dir(map_i):
240                            map_i.map_value.append(gvar)
241                        else:
242                            map_i.map_value = gvar
243                    # Use unique identifier, since map names may be equal
244                    resultdict[map_i.uid] = map_i
245        resultlist = resultdict.values()
246
247        # Sort list of maps chronological.
248        resultlist = sorted(resultlist, key = AbstractDatasetComparisonKeyStartTime)
249
250        return(resultlist)
251
252    def overlay_cmd_value(self,  map_i, tbrelations, function, topolist = ["EQUAL"]):
253        """ Function to evaluate two map lists by given overlay operator.
254
255          :param map_i: Map object with temporal extent.
256          :param tbrelations: List of temporal relation to map_i.
257          :param topolist: List of strings for given temporal relations.
258          :param function: Overlay operator, &|+^~.
259
260          :return: Map object with command list with  operators that has been
261                        evaluated by implicit aggregration.
262        """
263        # Build comandlist list with elements from related maps and given relation operator.
264        resultlist = []
265        # Define overlay operation dictionary.
266        overlaydict = {"&":"and",  "|":"or",  "^":"xor",  "~":"not", "+":"disor"}
267        operator = overlaydict[function]
268        # Set first input for overlay module.
269        mapainput = map_i.get_id()
270        # Append command list of given map to result command list.
271        if "cmd_list" in dir(map_i):
272            resultlist = resultlist + map_i.cmd_list
273        for topo in topolist:
274            if topo.upper() in tbrelations.keys():
275                relationmaplist = tbrelations[topo.upper()]
276                for relationmap in relationmaplist:
277                    # Append command list of given map to result command list.
278                    if "cmd_list" in dir(relationmap):
279                        resultlist = resultlist + relationmap.cmd_list
280                    # Generate an intermediate name
281                    name = self.generate_map_name()
282                    # Put it into the removalbe map list
283                    self.removable_maps[name] = VectorDataset(name + "@%s"%(self.mapset))
284                    map_i.set_id(name + "@" + self.mapset)
285                    # Set second input for overlay module.
286                    mapbinput = relationmap.get_id()
287                    # Create module command in PyGRASS for v.overlay and v.patch.
288                    if operator != "disor":
289                        m = copy.deepcopy(self.m_overlay)
290                        m.run_ = False
291                        m.inputs["operator"].value = operator
292                        m.inputs["ainput"].value = str(mapainput)
293                        m.inputs["binput"].value = str(mapbinput)
294                        m.outputs["output"].value = name
295                        m.flags["overwrite"].value = self.overwrite
296                    else:
297                        patchinput = str(mapainput) + ',' + str(mapbinput)
298                        m = copy.deepcopy(self.m_patch)
299                        m.run_ = False
300                        m.inputs["input"].value = patchinput
301                        m.outputs["output"].value = name
302                        m.flags["overwrite"].value = self.overwrite
303                    # Conditional append of module command.
304                    resultlist.append(m)
305                    # Set new map name to temporary map name.
306                    mapainput = name
307        # Add command list to result map.
308        map_i.cmd_list = resultlist
309
310        return(resultlist)
311
312    def set_temporal_extent_list(self, maplist, topolist = ["EQUAL"], temporal = 'l' ):
313        """ Change temporal extent of map list based on temporal relations to
314                other map list and given temporal operator.
315
316            :param maplist: List of map objects for which relations has been build
317                                        correctely.
318            :param topolist: List of strings of temporal relations.
319            :param temporal: The temporal operator specifying the temporal
320                                            extent operation (intersection, union, disjoint
321                                            union, right reference, left reference).
322
323            :return: Map list with specified temporal extent.
324        """
325        resultdict = {}
326
327        for map_i in maplist:
328            # Loop over temporal related maps and create overlay modules.
329            tbrelations = map_i.get_temporal_relations()
330            # Generate an intermediate map for the result map list.
331            map_new = self.generate_new_map(base_map=map_i, bool_op = 'and',
332                                                                        copy = True,  rename = False,
333                                                                        remove = True)
334            # Combine temporal and spatial extents of intermediate map with related maps.
335            for topo in topolist:
336                if topo in tbrelations.keys():
337                    for map_j in (tbrelations[topo]):
338                        if temporal == 'r':
339                            # Generate an intermediate map for the result map list.
340                            map_new = self.generate_new_map(base_map=map_i, bool_op = 'and',
341                                                                                        copy = True,  rename = False,
342                                                                                        remove = True)
343                        # Create overlaid map extent.
344                        returncode = self.overlay_map_extent(map_new, map_j, 'and', \
345                                                                temp_op = temporal)
346                        # Stop the loop if no temporal or spatial relationship exist.
347                        if returncode == 0:
348                            break
349                        # Append map to result map list.
350                        elif returncode == 1:
351                            # resultlist.append(map_new)
352                            resultdict[map_new.get_id()] = map_new
353                    if returncode == 0:
354                        break
355            # Append map to result map list.
356            #if returncode == 1:
357            #    resultlist.append(map_new)
358        # Get sorted map objects as values from result dictionoary.
359        resultlist = resultdict.values()
360        resultlist = sorted(resultlist, key = AbstractDatasetComparisonKeyStartTime)
361
362        return(resultlist)
363
364    ###########################################################################
365
366    def p_statement_assign(self, t):
367        # The expression should always return a list of maps.
368        """
369        statement : stds EQUALS expr
370
371        """
372        # Execute the command lists
373        if self.run:
374            # Open connection to temporal database.
375            dbif, connected = init_dbif(dbif=self.dbif)
376            if isinstance(t[3], list):
377                num = len(t[3])
378                count = 0
379                returncode = 0
380                register_list = []
381                leadzero = len(str(num))
382                for i in range(num):
383                    # Check if resultmap names exist in GRASS database.
384                    vectorname = self.basename + "_" + str(i).zfill(leadzero)
385                    vectormap = VectorDataset(vectorname + "@" + get_current_mapset())
386                    if vectormap.map_exists() and self.overwrite == False:
387                        self.msgr.fatal(_("Error vector maps with basename %s exist. "
388                                          "Use --o flag to overwrite existing file") \
389                                          %(vectorname))
390                for map_i in t[3]:
391                    if "cmd_list" in dir(map_i):
392                        # Execute command list.
393                        for cmd in map_i.cmd_list:
394                            try:
395                                # We need to check if the input maps have areas in case of v.overlay
396                                # otherwise v.overlay will break
397                                if cmd.name == "v.overlay":
398                                    for name in (cmd.inputs["ainput"].value,
399                                                 cmd.inputs["binput"].value):
400                                        #self.msgr.message("Check if map <" + name + "> exists")
401                                        if name.find("@") < 0:
402                                            name = name + "@" + get_current_mapset()
403                                        tmp_map = map_i.get_new_instance(name)
404                                        if not tmp_map.map_exists():
405                                            raise Exception
406                                        #self.msgr.message("Check if map <" + name + "> has areas")
407                                        tmp_map.load()
408                                        if tmp_map.metadata.get_number_of_areas() == 0:
409                                            raise Exception
410                            except Exception:
411                                returncode = 1
412                                break
413
414                            # run the command
415                            # print the command that will be executed
416                            self.msgr.message("Run command:\n" + cmd.get_bash())
417                            cmd.run()
418
419                            if cmd.returncode != 0:
420                                self.msgr.fatal(
421                                    _("Error starting %s : \n%s")
422                                    % (cmd.get_bash(), cmd.outputs.stderr)
423                                )
424                            mapname = cmd.outputs["output"].value
425                            if mapname.find("@") >= 0:
426                                map_test = map_i.get_new_instance(mapname)
427                            else:
428                                map_test = map_i.get_new_instance(mapname + "@" + self.mapset)
429                            if not map_test.map_exists():
430                                returncode = 1
431                                break
432                        if returncode == 0:
433                            # We remove the invalid vector name from the remove list.
434                            if map_i.get_name() in self.removable_maps:
435                                self.removable_maps.pop(map_i.get_name())
436                            mapset = map_i.get_mapset()
437                            # Change map name to given basename.
438                            newident = self.basename + "_" + str(count).zfill(leadzero)
439                            m = copy.deepcopy(self.m_rename)
440                            m.inputs["vector"].value = (map_i.get_name(),newident)
441                            m.flags["overwrite"].value = self.overwrite
442                            m.run()
443                            map_i.set_id(newident + "@" + mapset)
444                            count += 1
445                            register_list.append(map_i)
446                    else:
447                        # Test if temporal extents have been changed by temporal
448                        # relation operators (i|r). This is a code copy from temporal_algebra.py
449                        map_i_extent = map_i.get_temporal_extent_as_tuple()
450                        map_test = map_i.get_new_instance(map_i.get_id())
451                        map_test.select(dbif)
452                        map_test_extent = map_test.get_temporal_extent_as_tuple()
453                        if map_test_extent != map_i_extent:
454                            # Create new map with basename
455                            newident = self.basename + "_" + str(count).zfill(leadzero)
456                            map_result = map_i.get_new_instance(newident + "@" + self.mapset)
457
458                            if map_test.map_exists() and self.overwrite is False:
459                                self.msgr.fatal("Error raster maps with basename %s exist. "
460                                                "Use --o flag to overwrite existing file"
461                                                %(mapname))
462
463                            map_result.set_temporal_extent(map_i.get_temporal_extent())
464                            map_result.set_spatial_extent(map_i.get_spatial_extent())
465                            # Attention we attach a new attribute
466                            map_result.is_new = True
467                            count += 1
468                            register_list.append(map_result)
469
470                            # Copy the map
471                            m = copy.deepcopy(self.m_copy)
472                            m.inputs["vector"].value = map_i.get_id(),  newident
473                            m.flags["overwrite"].value = self.overwrite
474                            m.run()
475                        else:
476                            register_list.append(map_i)
477
478                if len(register_list) > 0:
479                    # Create result space time dataset.
480                    resultstds = open_new_stds(t[1], self.stdstype,
481                                               'absolute', t[1], t[1],
482                                               "temporal vector algebra", self.dbif,
483                                               overwrite=self.overwrite)
484                    for map_i in register_list:
485                        # Check if modules should be executed from command list.
486                        if hasattr(map_i, "cmd_list") or hasattr(map_i, "is_new"):
487                            # Get meta data from grass database.
488                            map_i.load()
489                            if map_i.is_in_db(dbif=dbif) and self.overwrite:
490                                # Update map in temporal database.
491                                map_i.update_all(dbif=dbif)
492                            elif map_i.is_in_db(dbif=dbif) and self.overwrite == False:
493                                # Raise error if map exists and no overwrite flag is given.
494                                self.msgr.fatal(
495                                    _(
496                                        "Error vector map %s exist in temporal database. "
497                                        "Use overwrite flag.  : \n%s"
498                                    )
499                                    % (map_i.get_map_id(), cmd.outputs.stderr)
500                                )
501                            else:
502                                # Insert map into temporal database.
503                                map_i.insert(dbif=dbif)
504                        else:
505                            # Map is original from an input STVDS
506                            map_i.load()
507                        # Register map in result space time dataset.
508                        print(map_i.get_temporal_extent_as_tuple())
509                        success = resultstds.register_map(map_i, dbif=dbif)
510                    resultstds.update_from_registered_maps(dbif)
511
512            # Remove intermediate maps
513            self.remove_maps()
514            if connected:
515                dbif.close()
516            t[0] = t[3]
517
518    def p_overlay_operation(self, t):
519        """
520        expr : stds AND stds
521             | expr AND stds
522             | stds AND expr
523             | expr AND expr
524             | stds OR stds
525             | expr OR stds
526             | stds OR expr
527             | expr OR expr
528             | stds XOR stds
529             | expr XOR stds
530             | stds XOR expr
531             | expr XOR expr
532             | stds NOT stds
533             | expr NOT stds
534             | stds NOT expr
535             | expr NOT expr
536             | stds DISOR stds
537             | expr DISOR stds
538             | stds DISOR expr
539             | expr DISOR expr
540        """
541        if self.run:
542            # Check input stds and operator.
543            maplistA = self.check_stds(t[1])
544            maplistB = self.check_stds(t[3])
545            relations = ["EQUAL"]
546            temporal = 'l'
547            function = t[2]
548            # Build command list for related maps.
549            complist = self.build_spatio_temporal_topology_list(maplistA, maplistB, topolist = relations,
550                                                                compop = function, overlay_cmd = True)
551            # Set temporal extent based on topological relationships.
552            resultlist = self.set_temporal_extent_list(complist, topolist = relations,
553                                temporal = temporal)
554
555            t[0] = resultlist
556        if self.debug:
557            str(t[1]) + t[2] + str(t[3])
558
559    def p_overlay_operation_relation(self, t):
560        """
561        expr : stds T_OVERLAY_OPERATOR stds
562             | expr T_OVERLAY_OPERATOR stds
563             | stds T_OVERLAY_OPERATOR expr
564             | expr T_OVERLAY_OPERATOR expr
565        """
566        if self.run:
567            # Check input stds and operator.
568            maplistA = self.check_stds(t[1])
569            maplistB = self.check_stds(t[3])
570            relations, temporal, function,  aggregate = self.eval_toperator(t[2],  optype = 'overlay')
571            # Build command list for related maps.
572            complist = self.build_spatio_temporal_topology_list(maplistA, maplistB, topolist = relations,
573                                                                compop = function, overlay_cmd = True)
574            # Set temporal extent based on topological relationships.
575            resultlist = self.set_temporal_extent_list(complist, topolist = relations,
576                                temporal = temporal)
577
578            t[0] = resultlist
579        if self.debug:
580            str(t[1]) + t[2] + str(t[3])
581
582    def p_buffer_operation(self,t):
583        """
584        expr : buff_function LPAREN stds COMMA number RPAREN
585             | buff_function LPAREN expr COMMA number RPAREN
586        """
587
588        if self.run:
589            # Check input stds.
590            bufflist = self.check_stds(t[3])
591            # Create empty result list.
592            resultlist = []
593
594            for map_i in bufflist:
595                # Generate an intermediate name for the result map list.
596                map_new = self.generate_new_map(base_map=map_i, bool_op=None,
597                                                copy=True,  remove = True)
598                # Change spatial extent based on buffer size.
599                map_new.spatial_buffer(float(t[5]))
600                # Check buff type.
601                if t[1] == "buff_p":
602                    buff_type = "point"
603                elif t[1] == "buff_l":
604                    buff_type = "line"
605                elif t[1] == "buff_a":
606                    buff_type = "area"
607                m = copy.deepcopy(self.m_buffer)
608                m.run_ = False
609                m.inputs["type"].value = buff_type
610                m.inputs["input"].value = str(map_i.get_id())
611                m.inputs["distance"].value = float(t[5])
612                m.outputs["output"].value = map_new.get_name()
613                m.flags["overwrite"].value = self.overwrite
614
615                # Conditional append of module command.
616                if "cmd_list" in dir(map_new):
617                    map_new.cmd_list.append(m)
618                else:
619                    map_new.cmd_list = [m]
620                resultlist.append(map_new)
621
622            t[0] = resultlist
623
624    def p_buff_function(self, t):
625        """buff_function    : BUFF_POINT
626                            | BUFF_LINE
627                            | BUFF_AREA
628                            """
629        t[0] = t[1]
630
631    # Handle errors.
632    def p_error(self, t):
633        raise SyntaxError("syntax error on line %d near '%s' expression '%s'" %
634            (t.lineno, t.value, self.expression))
635
636###############################################################################
637
638if __name__ == "__main__":
639    import doctest
640    doctest.testmod()
641