temporal_vector_algebra.py 29 KB

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