temporal_vector_algebra.py 27 KB

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