temporal_operator.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. """@package grass.temporal
  2. Temporal operator evaluation with PLY
  3. (C) 2013 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. >>> p = TemporalOperatorParser()
  10. >>> expression = "{equal| during}"
  11. >>> p.parse(expression, optype = 'relation')
  12. >>> print((p.relations, p.temporal, p.function))
  13. (['equal', 'during'], None, None)
  14. >>> p = TemporalOperatorParser()
  15. >>> expression = "{contains | starts}"
  16. >>> p.parse(expression)
  17. >>> print((p.relations, p.temporal, p.function))
  18. (['contains', 'starts'], None, None)
  19. >>> p = TemporalOperatorParser()
  20. >>> expression = "{&&, during}"
  21. >>> p.parse(expression, optype = 'boolean')
  22. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  23. (['during'], 'l', '&&', '&')
  24. >>> p = TemporalOperatorParser()
  25. >>> expression = "{||, equal | during}"
  26. >>> p.parse(expression, optype = 'boolean')
  27. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  28. (['equal', 'during'], 'l', '||', '|')
  29. >>> p = TemporalOperatorParser()
  30. >>> expression = "{||, equal | during, &}"
  31. >>> p.parse(expression, optype = 'boolean')
  32. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  33. (['equal', 'during'], 'l', '||', '&')
  34. >>> p = TemporalOperatorParser()
  35. >>> expression = "{&&, during, |}"
  36. >>> p.parse(expression, optype = 'boolean')
  37. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  38. (['during'], 'l', '&&', '|')
  39. >>> p = TemporalOperatorParser()
  40. >>> expression = "{&&, during, |, r}"
  41. >>> p.parse(expression, optype = 'boolean')
  42. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  43. (['during'], 'r', '&&', '|')
  44. >>> p = TemporalOperatorParser()
  45. >>> expression = "{&&, during, u}"
  46. >>> p.parse(expression, optype = 'boolean')
  47. >>> print((p.relations, p.temporal, p.function,p.aggregate))
  48. (['during'], 'u', '&&', '&')
  49. >>> p = TemporalOperatorParser()
  50. >>> expression = "{:, during, r}"
  51. >>> p.parse(expression, optype = 'select')
  52. >>> print((p.relations, p.temporal, p.function))
  53. (['during'], 'r', ':')
  54. >>> p = TemporalOperatorParser()
  55. >>> expression = "{!:, equal | contains, d}"
  56. >>> p.parse(expression, optype = 'select')
  57. >>> print((p.relations, p.temporal, p.function))
  58. (['equal', 'contains'], 'd', '!:')
  59. >>> p = TemporalOperatorParser()
  60. >>> expression = "{#, during, r}"
  61. >>> p.parse(expression, optype = 'hash')
  62. >>> print((p.relations, p.temporal, p.function))
  63. (['during'], 'r', '#')
  64. >>> p = TemporalOperatorParser()
  65. >>> expression = "{#, equal | contains}"
  66. >>> p.parse(expression, optype = 'hash')
  67. >>> print((p.relations, p.temporal, p.function))
  68. (['equal', 'contains'], 'l', '#')
  69. >>> p = TemporalOperatorParser()
  70. >>> expression = "{+, during, r}"
  71. >>> p.parse(expression, optype = 'raster')
  72. >>> print((p.relations, p.temporal, p.function))
  73. (['during'], 'r', '+')
  74. >>> p = TemporalOperatorParser()
  75. >>> expression = "{/, equal | contains}"
  76. >>> p.parse(expression, optype = 'raster')
  77. >>> print((p.relations, p.temporal, p.function))
  78. (['equal', 'contains'], 'l', '/')
  79. >>> p = TemporalOperatorParser()
  80. >>> expression = "{+, equal | contains,intersect}"
  81. >>> p.parse(expression, optype = 'raster')
  82. >>> print((p.relations, p.temporal, p.function))
  83. (['equal', 'contains'], 'i', '+')
  84. >>> p = TemporalOperatorParser()
  85. >>> expression = "{*, contains,disjoint}"
  86. >>> p.parse(expression, optype = 'raster')
  87. >>> print((p.relations, p.temporal, p.function))
  88. (['contains'], 'd', '*')
  89. >>> p = TemporalOperatorParser()
  90. >>> expression = "{~, equal,left}"
  91. >>> p.parse(expression, optype = 'overlay')
  92. >>> print((p.relations, p.temporal, p.function))
  93. (['equal'], 'l', '~')
  94. >>> p = TemporalOperatorParser()
  95. >>> expression = "{^, over,right}"
  96. >>> p.parse(expression, optype = 'overlay')
  97. >>> print((p.relations, p.temporal, p.function))
  98. (['overlaps', 'overlapped'], 'r', '^')
  99. """
  100. from __future__ import print_function
  101. try:
  102. import ply.lex as lex
  103. import ply.yacc as yacc
  104. except:
  105. pass
  106. class TemporalOperatorLexer(object):
  107. """Lexical analyzer for the GRASS GIS temporal operator"""
  108. # Functions that defines topological relations.
  109. relations = {
  110. 'equal' : "EQUAL",
  111. 'follows' : "FOLLOWS",
  112. 'precedes' : "PRECEDES",
  113. 'overlaps' : "OVERLAPS",
  114. 'overlapped' : "OVERLAPPED",
  115. 'during' : "DURING",
  116. 'starts' : "STARTS",
  117. 'finishes' : "FINISHES",
  118. 'contains' : "CONTAINS",
  119. 'started' : "STARTED",
  120. 'finished' : "FINISHED",
  121. 'over' : "OVER"
  122. }
  123. # This is the list of token names.
  124. tokens = (
  125. 'COMMA',
  126. 'LEFTREF',
  127. 'RIGHTREF',
  128. 'UNION',
  129. 'DISJOINT',
  130. 'INTERSECT',
  131. 'HASH',
  132. 'OR',
  133. 'AND',
  134. 'DISOR',
  135. 'XOR',
  136. 'NOT',
  137. 'MOD',
  138. 'DIV',
  139. 'MULT',
  140. 'ADD',
  141. 'SUB',
  142. 'T_SELECT',
  143. 'T_NOT_SELECT',
  144. 'CLPAREN',
  145. 'CRPAREN',
  146. )
  147. # Build the token list
  148. tokens = tokens + tuple(relations.values())
  149. # Regular expression rules for simple tokens
  150. t_T_SELECT = r':'
  151. t_T_NOT_SELECT = r'!:'
  152. t_COMMA = r','
  153. t_LEFTREF = '^[l|left]'
  154. t_RIGHTREF ='^[r|right]'
  155. t_UNION = '^[u|union]'
  156. t_DISJOINT = '^[d|disjoint]'
  157. t_INTERSECT = '^[i|intersect]'
  158. t_HASH = r'\#'
  159. t_OR = r'[\|]'
  160. t_AND = r'[&]'
  161. t_DISOR = r'\+'
  162. t_XOR = r'\^'
  163. t_NOT = r'\~'
  164. t_MOD = r'[\%]'
  165. t_DIV = r'[\/]'
  166. t_MULT = r'[\*]'
  167. t_ADD = r'[\+]'
  168. t_SUB = r'[-]'
  169. t_CLPAREN = r'\{'
  170. t_CRPAREN = r'\}'
  171. # These are the things that should be ignored.
  172. t_ignore = ' \t'
  173. # Track line numbers.
  174. def t_newline(self, t):
  175. r'\n+'
  176. t.lineno += len(t.value)
  177. def t_NAME(self, t):
  178. r'[a-zA-Z_][a-zA-Z_0-9]*'
  179. self.temporal_symbol(t)
  180. return t
  181. # Parse symbols
  182. def temporal_symbol(self, t):
  183. # Check for reserved words
  184. if t.value in TemporalOperatorLexer.relations.keys():
  185. t.type = TemporalOperatorLexer.relations.get(t.value)
  186. elif t.value == 'l' or t.value == 'left':
  187. t.value = 'l'
  188. t.type = 'LEFTREF'
  189. elif t.value == 'r' or t.value == 'right':
  190. t.value = 'r'
  191. t.type = 'RIGHTREF'
  192. elif t.value == 'u' or t.value == 'union':
  193. t.value = 'u'
  194. t.type = 'UNION'
  195. elif t.value == 'd' or t.value == 'disjoint':
  196. t.value = 'd'
  197. t.type = 'DISJOINT'
  198. elif t.value == 'i' or t.value == 'intersect':
  199. t.value = 'i'
  200. t.type = 'INTERSECT'
  201. #else:
  202. # t.type = 'NAME'
  203. return(t)
  204. # Handle errors.
  205. def t_error(self, t):
  206. raise SyntaxError("syntax error on line %d near '%s'" %
  207. (t.lineno, t.value))
  208. # Build the lexer
  209. def build(self,**kwargs):
  210. self.lexer = lex.lex(module=self, **kwargs)
  211. # Just for testing
  212. def test(self,data):
  213. self.name_list = {}
  214. print(data)
  215. self.lexer.input(data)
  216. while True:
  217. tok = self.lexer.token()
  218. if not tok: break
  219. print(tok)
  220. ###############################################################################
  221. class TemporalOperatorParser(object):
  222. """The temporal operator class"""
  223. def __init__(self):
  224. self.lexer = TemporalOperatorLexer()
  225. self.lexer.build()
  226. self.parser = yacc.yacc(module=self)
  227. self.relations = None # Temporal relations equals, contain, during, ...
  228. self.temporal = None # Temporal operation like intersect, left, right, ...
  229. self.function = None # Actual operation
  230. self.aggregate = None # Aggregation function
  231. def parse(self, expression, optype='relation'):
  232. """Parse the expression and fill the object variables
  233. :param expression:
  234. :param optype: The parameter optype can be of type:
  235. - select { :, during, r}
  236. - boolean {&&, contains, |}
  237. - raster { *, equal, |}
  238. - vector { |, starts, &}
  239. - hash { #, during, l}
  240. - relation {during}
  241. :return:
  242. """
  243. self.optype = optype
  244. self.parser.parse(expression)
  245. #
  246. # Error rule for syntax errors.
  247. def p_error(self, t):
  248. raise SyntaxError("Unexpected syntax error")
  249. # Get the tokens from the lexer class
  250. tokens = TemporalOperatorLexer.tokens
  251. def p_relation_operator(self, t):
  252. """
  253. operator : CLPAREN relation CRPAREN
  254. | CLPAREN relationlist CRPAREN
  255. """
  256. # Check for correct type.
  257. if not self.optype == 'relation':
  258. raise SyntaxError("invalid syntax")
  259. else:
  260. # Set three operator components.
  261. if isinstance(t[2], list):
  262. self.relations = t[2]
  263. else:
  264. self.relations = [t[2]]
  265. self.temporal = None
  266. self.function = None
  267. t[0] = t[2]
  268. def p_relation_bool_operator(self, t):
  269. """
  270. operator : CLPAREN OR OR COMMA relation CRPAREN
  271. | CLPAREN AND AND COMMA relation CRPAREN
  272. | CLPAREN OR OR COMMA relationlist CRPAREN
  273. | CLPAREN AND AND COMMA relationlist CRPAREN
  274. """
  275. if not self.optype == 'boolean':
  276. raise SyntaxError("invalid syntax")
  277. else:
  278. # Set three operator components.
  279. if isinstance(t[5], list):
  280. self.relations = t[5]
  281. else:
  282. self.relations = [t[5]]
  283. self.temporal = "l"
  284. self.function = t[2] + t[3]
  285. self.aggregate = t[2]
  286. t[0] = t[2]
  287. def p_relation_bool_combi_operator(self, t):
  288. """
  289. operator : CLPAREN OR OR COMMA relation COMMA OR CRPAREN
  290. | CLPAREN OR OR COMMA relation COMMA AND CRPAREN
  291. | CLPAREN AND AND COMMA relation COMMA OR CRPAREN
  292. | CLPAREN AND AND COMMA relation COMMA AND CRPAREN
  293. | CLPAREN OR OR COMMA relationlist COMMA OR CRPAREN
  294. | CLPAREN OR OR COMMA relationlist COMMA AND CRPAREN
  295. | CLPAREN AND AND COMMA relationlist COMMA OR CRPAREN
  296. | CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN
  297. """
  298. if not self.optype == 'boolean':
  299. raise SyntaxError("invalid syntax")
  300. else:
  301. # Set three operator components.
  302. if isinstance(t[5], list):
  303. self.relations = t[5]
  304. else:
  305. self.relations = [t[5]]
  306. self.temporal = "l"
  307. self.function = t[2] + t[3]
  308. self.aggregate = t[7]
  309. t[0] = t[2]
  310. def p_relation_bool_combi_operator2(self, t):
  311. """
  312. operator : CLPAREN OR OR COMMA relation COMMA temporal CRPAREN
  313. | CLPAREN AND AND COMMA relation COMMA temporal CRPAREN
  314. | CLPAREN OR OR COMMA relationlist COMMA temporal CRPAREN
  315. | CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN
  316. """
  317. if not self.optype == 'boolean':
  318. raise SyntaxError("invalid syntax")
  319. else:
  320. # Set three operator components.
  321. if isinstance(t[5], list):
  322. self.relations = t[5]
  323. else:
  324. self.relations = [t[5]]
  325. self.temporal = t[7]
  326. self.function = t[2] + t[3]
  327. self.aggregate = t[2]
  328. t[0] = t[2]
  329. def p_relation_bool_combi_operator3(self, t):
  330. """
  331. operator : CLPAREN OR OR COMMA relation COMMA OR COMMA temporal CRPAREN
  332. | CLPAREN OR OR COMMA relation COMMA AND COMMA temporal CRPAREN
  333. | CLPAREN AND AND COMMA relation COMMA OR COMMA temporal CRPAREN
  334. | CLPAREN AND AND COMMA relation COMMA AND COMMA temporal CRPAREN
  335. | CLPAREN OR OR COMMA relationlist COMMA OR COMMA temporal CRPAREN
  336. | CLPAREN OR OR COMMA relationlist COMMA AND COMMA temporal CRPAREN
  337. | CLPAREN AND AND COMMA relationlist COMMA OR COMMA temporal CRPAREN
  338. | CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN
  339. """
  340. if not self.optype == 'boolean':
  341. raise SyntaxError("invalid syntax")
  342. else:
  343. # Set three operator components.
  344. if isinstance(t[5], list):
  345. self.relations = t[5]
  346. else:
  347. self.relations = [t[5]]
  348. self.temporal = t[9]
  349. self.function = t[2] + t[3]
  350. self.aggregate = t[7]
  351. t[0] = t[2]
  352. def p_select_relation_operator(self, t):
  353. """
  354. operator : CLPAREN select CRPAREN
  355. | CLPAREN select COMMA relation CRPAREN
  356. | CLPAREN select COMMA relationlist CRPAREN
  357. | CLPAREN select COMMA relation COMMA temporal CRPAREN
  358. | CLPAREN select COMMA relationlist COMMA temporal CRPAREN
  359. """
  360. if not self.optype == 'select':
  361. raise SyntaxError("invalid syntax")
  362. else:
  363. if len(t) == 4:
  364. # Set three operator components.
  365. self.relations = ['equal']
  366. self.temporal = "l"
  367. self.function = t[2]
  368. elif len(t) == 6:
  369. if isinstance(t[4], list):
  370. self.relations = t[4]
  371. else:
  372. self.relations = [t[4]]
  373. self.temporal = "l"
  374. self.function = t[2]
  375. elif len(t) == 8:
  376. if isinstance(t[4], list):
  377. self.relations = t[4]
  378. else:
  379. self.relations = [t[4]]
  380. self.temporal = t[6]
  381. self.function = t[2]
  382. t[0] = t[2]
  383. def p_hash_relation_operator(self, t):
  384. """
  385. operator : CLPAREN HASH CRPAREN
  386. | CLPAREN HASH COMMA relation CRPAREN
  387. | CLPAREN HASH COMMA relationlist CRPAREN
  388. | CLPAREN HASH COMMA relation COMMA temporal CRPAREN
  389. | CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN
  390. """
  391. if not self.optype == 'hash':
  392. raise SyntaxError("invalid syntax")
  393. else:
  394. if len(t) == 4:
  395. # Set three operator components.
  396. self.relations = ['equal']
  397. self.temporal = "l"
  398. self.function = t[2]
  399. elif len(t) == 6:
  400. if isinstance(t[4], list):
  401. self.relations = t[4]
  402. else:
  403. self.relations = [t[4]]
  404. self.temporal = "l"
  405. self.function = t[2]
  406. elif len(t) == 8:
  407. if isinstance(t[4], list):
  408. self.relations = t[4]
  409. else:
  410. self.relations = [t[4]]
  411. self.temporal = t[6]
  412. self.function = t[2]
  413. t[0] = t[2]
  414. def p_raster_relation_operator(self, t):
  415. # The expression should always return a list of maps.
  416. """
  417. operator : CLPAREN arithmetic CRPAREN
  418. | CLPAREN arithmetic COMMA relation CRPAREN
  419. | CLPAREN arithmetic COMMA relationlist CRPAREN
  420. | CLPAREN arithmetic COMMA relation COMMA temporal CRPAREN
  421. | CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN
  422. """
  423. if not self.optype == 'raster':
  424. raise SyntaxError("invalid syntax")
  425. else:
  426. if len(t) == 4:
  427. # Set three operator components.
  428. self.relations = ['equal']
  429. self.temporal = "l"
  430. self.function = t[2]
  431. elif len(t) == 6:
  432. if isinstance(t[4], list):
  433. self.relations = t[4]
  434. else:
  435. self.relations = [t[4]]
  436. self.temporal = "l"
  437. self.function = t[2]
  438. elif len(t) == 8:
  439. if isinstance(t[4], list):
  440. self.relations = t[4]
  441. else:
  442. self.relations = [t[4]]
  443. self.temporal = t[6]
  444. self.function = t[2]
  445. t[0] = t[2]
  446. def p_overlay_relation_operator(self, t):
  447. # The expression should always return a list of maps.
  448. """
  449. operator : CLPAREN overlay CRPAREN
  450. | CLPAREN overlay COMMA relation CRPAREN
  451. | CLPAREN overlay COMMA relationlist CRPAREN
  452. | CLPAREN overlay COMMA relation COMMA temporal CRPAREN
  453. | CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN
  454. """
  455. if not self.optype == 'overlay':
  456. raise SyntaxError("invalid syntax")
  457. else:
  458. if len(t) == 4:
  459. # Set three operator components.
  460. self.relations = ['equal']
  461. self.temporal = "l"
  462. self.function = t[2]
  463. elif len(t) == 6:
  464. if isinstance(t[4], list):
  465. self.relations = t[4]
  466. else:
  467. self.relations = [t[4]]
  468. self.temporal = "l"
  469. self.function = t[2]
  470. elif len(t) == 8:
  471. if isinstance(t[4], list):
  472. self.relations = t[4]
  473. else:
  474. self.relations = [t[4]]
  475. self.temporal = t[6]
  476. self.function = t[2]
  477. t[0] = t[2]
  478. def p_relation(self, t):
  479. # The list of relations.
  480. """
  481. relation : EQUAL
  482. | FOLLOWS
  483. | PRECEDES
  484. | OVERLAPS
  485. | OVERLAPPED
  486. | DURING
  487. | STARTS
  488. | FINISHES
  489. | CONTAINS
  490. | STARTED
  491. | FINISHED
  492. """
  493. t[0] = t[1]
  494. def p_over(self, t):
  495. # The list of relations.
  496. """
  497. relation : OVER
  498. """
  499. over_list = ["overlaps", "overlapped"]
  500. t[0] = over_list
  501. def p_relationlist(self, t):
  502. # The list of relations.
  503. """
  504. relationlist : relation OR relation
  505. | relation OR relationlist
  506. """
  507. rel_list = []
  508. rel_list.append(t[1])
  509. if isinstance(t[3], list):
  510. rel_list = rel_list + t[3]
  511. else:
  512. rel_list.append(t[3])
  513. t[0] = rel_list
  514. def p_temporal_operator(self, t):
  515. # The list of relations.
  516. """
  517. temporal : LEFTREF
  518. | RIGHTREF
  519. | UNION
  520. | DISJOINT
  521. | INTERSECT
  522. """
  523. t[0] = t[1]
  524. def p_select_operator(self, t):
  525. # The list of relations.
  526. """
  527. select : T_SELECT
  528. | T_NOT_SELECT
  529. """
  530. t[0] = t[1]
  531. def p_arithmetic_operator(self, t):
  532. # The list of relations.
  533. """
  534. arithmetic : MOD
  535. | DIV
  536. | MULT
  537. | ADD
  538. | SUB
  539. """
  540. t[0] = t[1]
  541. def p_overlay_operator(self, t):
  542. # The list of relations.
  543. """
  544. overlay : AND
  545. | OR
  546. | XOR
  547. | DISOR
  548. | NOT
  549. """
  550. t[0] = t[1]
  551. ###############################################################################
  552. if __name__ == "__main__":
  553. import doctest
  554. doctest.testmod()