123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- """@package grass.temporal
- Temporal operator evaluation with PLY
- (C) 2013 by the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- :authors: Thomas Leppelt and Soeren Gebbert
- .. code-block:: python
- >>> p = TemporalOperatorParser()
- >>> expression = "{equal|equivalent|cover|in|meet|contain|overlap}"
- >>> p.parse(expression, optype = 'relation')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'equivalent', 'cover', 'in', 'meet', 'contain', 'overlap'], None, None)
- >>> p = TemporalOperatorParser()
- >>> expression = "{equal| during}"
- >>> p.parse(expression, optype = 'relation')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'during'], None, None)
- >>> p = TemporalOperatorParser()
- >>> expression = "{contains | starts}"
- >>> p.parse(expression)
- >>> print((p.relations, p.temporal, p.function))
- (['contains', 'starts'], None, None)
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, during}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['during'], 'l', '&&', '&')
- >>> p = TemporalOperatorParser()
- >>> expression = "{||, equal | during}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['equal', 'during'], 'l', '||', '|')
- >>> p = TemporalOperatorParser()
- >>> expression = "{||, equal | during, &}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['equal', 'during'], 'l', '||', '&')
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, during, |}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['during'], 'l', '&&', '|')
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, during, |, r}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['during'], 'r', '&&', '|')
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, during, u}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['during'], 'u', '&&', '&')
- >>> p = TemporalOperatorParser()
- >>> expression = "{:, during, r}"
- >>> p.parse(expression, optype = 'select')
- >>> print((p.relations, p.temporal, p.function))
- (['during'], 'r', ':')
- >>> p = TemporalOperatorParser()
- >>> expression = "{!:, equal | contains, d}"
- >>> p.parse(expression, optype = 'select')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'contains'], 'd', '!:')
- >>> p = TemporalOperatorParser()
- >>> expression = "{#, during, r}"
- >>> p.parse(expression, optype = 'hash')
- >>> print((p.relations, p.temporal, p.function))
- (['during'], 'r', '#')
- >>> p = TemporalOperatorParser()
- >>> expression = "{#, equal | contains}"
- >>> p.parse(expression, optype = 'hash')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'contains'], 'l', '#')
- >>> p = TemporalOperatorParser()
- >>> expression = "{+, during, r}"
- >>> p.parse(expression, optype = 'raster')
- >>> print((p.relations, p.temporal, p.function))
- (['during'], 'r', '+')
- >>> p = TemporalOperatorParser()
- >>> expression = "{/, equal | contains}"
- >>> p.parse(expression, optype = 'raster')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'contains'], 'l', '/')
- >>> p = TemporalOperatorParser()
- >>> expression = "{+, equal | contains,intersect}"
- >>> p.parse(expression, optype = 'raster')
- >>> print((p.relations, p.temporal, p.function))
- (['equal', 'contains'], 'i', '+')
- >>> p = TemporalOperatorParser()
- >>> expression = "{*, contains,disjoint}"
- >>> p.parse(expression, optype = 'raster')
- >>> print((p.relations, p.temporal, p.function))
- (['contains'], 'd', '*')
- >>> p = TemporalOperatorParser()
- >>> expression = "{~, equal,left}"
- >>> p.parse(expression, optype = 'overlay')
- >>> print((p.relations, p.temporal, p.function))
- (['equal'], 'l', '~')
- >>> p = TemporalOperatorParser()
- >>> expression = "{^, over,right}"
- >>> p.parse(expression, optype = 'overlay')
- >>> print((p.relations, p.temporal, p.function))
- (['overlaps', 'overlapped'], 'r', '^')
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, equal | during | contains | starts, &}"
- >>> p.parse(expression, optype = 'boolean')
- >>> print((p.relations, p.temporal, p.function, p.aggregate))
- (['equal', 'during', 'contains', 'starts'], 'l', '&&', '&')
- >>> p = TemporalOperatorParser()
- >>> expression = "{&&, equal | during | contains | starts, &&&&&}"
- >>> p.parse(expression, optype = 'boolean')
- Traceback (most recent call last):
- SyntaxError: Unexpected syntax error in expression "{&&, equal | during | contains | starts, &&&&&}" at position 42 near &
- >>> p = TemporalOperatorParser()
- >>> expression = "{+, starting}"
- >>> p.parse(expression)
- Traceback (most recent call last):
- SyntaxError: syntax error on line 1 position 4 near 'starting'
- >>> p = TemporalOperatorParser()
- >>> expression = "{nope, start, |, l}"
- >>> p.parse(expression)
- Traceback (most recent call last):
- SyntaxError: syntax error on line 1 position 1 near 'nope'
- >>> p = TemporalOperatorParser()
- >>> expression = "{++, start, |, l}"
- >>> p.parse(expression)
- Traceback (most recent call last):
- SyntaxError: Unexpected syntax error in expression "{++, start, |, l}" at position 2 near +
- >>> p = TemporalOperatorParser()
- >>> expression = "{^, over, right}"
- >>> p.parse(expression, optype='rter')
- Traceback (most recent call last):
- SyntaxError: Unknown optype rter, must be one of ['select', 'boolean', 'raster', 'hash', 'relation', 'overlay']
- """
- from __future__ import print_function
- try:
- import ply.lex as lex
- import ply.yacc as yacc
- except ImportError:
- pass
- class TemporalOperatorLexer(object):
- """Lexical analyzer for the GRASS GIS temporal operator"""
- # Functions that defines topological relations.
- relations = {
- # temporal relations
- "equal": "EQUAL",
- "follows": "FOLLOWS",
- "precedes": "PRECEDES",
- "overlaps": "OVERLAPS",
- "overlapped": "OVERLAPPED",
- "during": "DURING",
- "starts": "STARTS",
- "finishes": "FINISHES",
- "contains": "CONTAINS",
- "started": "STARTED",
- "finished": "FINISHED",
- "over": "OVER",
- # spatial relations
- "equivalent": "EQUIVALENT",
- "cover": "COVER",
- "overlap": "OVERLAP",
- "in": "IN",
- "contain": "CONTAIN",
- "meet": "MEET",
- }
- # This is the list of token names.
- tokens = (
- "COMMA",
- "LEFTREF",
- "RIGHTREF",
- "UNION",
- "DISJOINT",
- "INTERSECT",
- "HASH",
- "OR",
- "AND",
- "DISOR",
- "XOR",
- "NOT",
- "MOD",
- "DIV",
- "MULT",
- "ADD",
- "SUB",
- "T_SELECT",
- "T_NOT_SELECT",
- "CLPAREN",
- "CRPAREN",
- )
- # Build the token list
- tokens = tokens + tuple(relations.values())
- # Regular expression rules for simple tokens
- t_T_SELECT = r":"
- t_T_NOT_SELECT = r"!:"
- t_COMMA = r","
- t_LEFTREF = "^[l|left]"
- t_RIGHTREF = "^[r|right]"
- t_UNION = "^[u|union]"
- t_DISJOINT = "^[d|disjoint]"
- t_INTERSECT = "^[i|intersect]"
- t_HASH = r"\#"
- t_OR = r"[\|]"
- t_AND = r"[&]"
- t_DISOR = r"\+"
- t_XOR = r"\^"
- t_NOT = r"\~"
- t_MOD = r"[\%]"
- t_DIV = r"[\/]"
- t_MULT = r"[\*]"
- t_ADD = r"[\+]"
- t_SUB = r"[-]"
- t_CLPAREN = r"\{"
- t_CRPAREN = r"\}"
- # These are the things that should be ignored.
- t_ignore = " \t\n"
- # Track line numbers.
- def t_newline(self, t):
- r"\n+"
- t.lineno += len(t.value)
- def t_NAME(self, t):
- r"[a-zA-Z_][a-zA-Z_0-9]*"
- return self.temporal_symbol(t)
- # Parse symbols
- def temporal_symbol(self, t):
- # Check for reserved words
- if t.value in TemporalOperatorLexer.relations.keys():
- t.type = TemporalOperatorLexer.relations.get(t.value)
- elif t.value == "l" or t.value == "left":
- t.value = "l"
- t.type = "LEFTREF"
- elif t.value == "r" or t.value == "right":
- t.value = "r"
- t.type = "RIGHTREF"
- elif t.value == "u" or t.value == "union":
- t.value = "u"
- t.type = "UNION"
- elif t.value == "d" or t.value == "disjoint":
- t.value = "d"
- t.type = "DISJOINT"
- elif t.value == "i" or t.value == "intersect":
- t.value = "i"
- t.type = "INTERSECT"
- else:
- self.t_error(t)
- return t
- # Handle errors.
- def t_error(self, t):
- raise SyntaxError(
- "syntax error on line %d position %i near '%s'"
- % (t.lineno, t.lexpos, t.value)
- )
- # Build the lexer
- def build(self, **kwargs):
- self.lexer = lex.lex(
- module=self, optimize=False, nowarn=True, debug=0, **kwargs
- )
- # Just for testing
- def test(self, data):
- self.name_list = {}
- print(data)
- self.lexer.input(data)
- while True:
- tok = self.lexer.token()
- if not tok:
- break
- print(tok)
- ###############################################################################
- class TemporalOperatorParser(object):
- """The temporal operator class"""
- def __init__(self):
- self.lexer = TemporalOperatorLexer()
- self.lexer.build()
- self.parser = yacc.yacc(module=self, debug=0)
- self.relations = None # Temporal relations (equals, contain, during, ...)
- self.temporal = None # Temporal operation (intersect, left, right, ...)
- self.function = None # Actual operation (+, -, /, *, ... )
- self.aggregate = None # Aggregation function (|, &)
- self.optype_list = [
- "select",
- "boolean",
- "raster",
- "hash",
- "relation",
- "overlay",
- ]
- def parse(self, expression, optype="relation"):
- """Parse the expression and fill the object variables
- :param expression:
- :param optype: The parameter optype can be of type:
- - select { :, during, r}
- - boolean {&&, contains, |}
- - raster { *, equal, |}
- - overlay { |, starts, &}
- - hash { #, during, l}
- - relation {during}
- :return:
- """
- self.optype = optype
- if optype not in self.optype_list:
- raise SyntaxError(
- "Unknown optype %s, must be one of %s"
- % (self.optype, str(self.optype_list))
- )
- self.expression = expression
- self.parser.parse(expression)
- # Error rule for syntax errors.
- def p_error(self, t):
- raise SyntaxError(
- "Unexpected syntax error in expression"
- ' "%s" at position %i near %s' % (self.expression, t.lexpos, t.value)
- )
- # Get the tokens from the lexer class
- tokens = TemporalOperatorLexer.tokens
- def p_relation_operator(self, t):
- # {during}
- # {during | equal | starts}
- """
- operator : CLPAREN relation CRPAREN
- | CLPAREN relationlist CRPAREN
- """
- # Check for correct type.
- if not self.optype == "relation":
- raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype)
- else:
- # Set three operator components.
- if isinstance(t[2], list):
- self.relations = t[2]
- else:
- self.relations = [t[2]]
- self.temporal = None
- self.function = None
- t[0] = t[2]
- def p_relation_bool_operator(self, t):
- # {||, during}
- # {&&, during | equal | starts}
- """
- operator : CLPAREN OR OR COMMA relation CRPAREN
- | CLPAREN AND AND COMMA relation CRPAREN
- | CLPAREN OR OR COMMA relationlist CRPAREN
- | CLPAREN AND AND COMMA relationlist CRPAREN
- """
- if not self.optype == "boolean":
- raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype)
- else:
- # Set three operator components.
- if isinstance(t[5], list):
- self.relations = t[5]
- else:
- self.relations = [t[5]]
- self.temporal = "l"
- self.function = t[2] + t[3]
- self.aggregate = t[2]
- t[0] = t[2]
- def p_relation_bool_combi_operator(self, t):
- # {||, during, &}
- # {&&, during | equal | starts, |}
- """
- operator : CLPAREN OR OR COMMA relation COMMA OR CRPAREN
- | CLPAREN OR OR COMMA relation COMMA AND CRPAREN
- | CLPAREN AND AND COMMA relation COMMA OR CRPAREN
- | CLPAREN AND AND COMMA relation COMMA AND CRPAREN
- | CLPAREN OR OR COMMA relationlist COMMA OR CRPAREN
- | CLPAREN OR OR COMMA relationlist COMMA AND CRPAREN
- | CLPAREN AND AND COMMA relationlist COMMA OR CRPAREN
- | CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN
- """
- if not self.optype == "boolean":
- raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype)
- else:
- # Set three operator components.
- if isinstance(t[5], list):
- self.relations = t[5]
- else:
- self.relations = [t[5]]
- self.temporal = "l"
- self.function = t[2] + t[3]
- self.aggregate = t[7]
- t[0] = t[2]
- def p_relation_bool_combi_operator2(self, t):
- # {||, during, left}
- # {&&, during | equal | starts, union}
- """
- operator : CLPAREN OR OR COMMA relation COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relation COMMA temporal CRPAREN
- | CLPAREN OR OR COMMA relationlist COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN
- """
- if not self.optype == "boolean":
- raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype)
- else:
- # Set three operator components.
- if isinstance(t[5], list):
- self.relations = t[5]
- else:
- self.relations = [t[5]]
- self.temporal = t[7]
- self.function = t[2] + t[3]
- self.aggregate = t[2]
- t[0] = t[2]
- def p_relation_bool_combi_operator3(self, t):
- # {||, during, |, left}
- # {&&, during | equal | starts, &, union}
- """
- operator : CLPAREN OR OR COMMA relation COMMA OR COMMA temporal CRPAREN
- | CLPAREN OR OR COMMA relation COMMA AND COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relation COMMA OR COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relation COMMA AND COMMA temporal CRPAREN
- | CLPAREN OR OR COMMA relationlist COMMA OR COMMA temporal CRPAREN
- | CLPAREN OR OR COMMA relationlist COMMA AND COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relationlist COMMA OR COMMA temporal CRPAREN
- | CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN
- """
- if not self.optype == "boolean":
- raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype)
- else:
- # Set three operator components.
- if isinstance(t[5], list):
- self.relations = t[5]
- else:
- self.relations = [t[5]]
- self.temporal = t[9]
- self.function = t[2] + t[3]
- self.aggregate = t[7]
- t[0] = t[2]
- def p_select_relation_operator(self, t):
- # {!:}
- # { :, during}
- # {!:, during | equal | starts}
- # { :, during | equal | starts, l}
- """
- operator : CLPAREN select CRPAREN
- | CLPAREN select COMMA relation CRPAREN
- | CLPAREN select COMMA relationlist CRPAREN
- | CLPAREN select COMMA relation COMMA temporal CRPAREN
- | CLPAREN select COMMA relationlist COMMA temporal CRPAREN
- """
- if not self.optype == "select":
- raise SyntaxError('Wrong optype "%s" must be "select"' % self.optype)
- else:
- if len(t) == 4:
- # Set three operator components.
- self.relations = ["equal", "equivalent"]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 6:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 8:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = t[6]
- self.function = t[2]
- t[0] = t[2]
- def p_hash_relation_operator(self, t):
- # {#}
- # {#, during}
- # {#, during | equal | starts}
- # {#, during | equal | starts, l}
- """
- operator : CLPAREN HASH CRPAREN
- | CLPAREN HASH COMMA relation CRPAREN
- | CLPAREN HASH COMMA relationlist CRPAREN
- | CLPAREN HASH COMMA relation COMMA temporal CRPAREN
- | CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN
- """
- if not self.optype == "hash":
- raise SyntaxError('Wrong optype "%s" must be "hash"' % self.optype)
- else:
- if len(t) == 4:
- # Set three operator components.
- self.relations = ["equal"]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 6:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 8:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = t[6]
- self.function = t[2]
- t[0] = t[2]
- def p_raster_relation_operator(self, t):
- # {+}
- # {-, during}
- # {*, during | equal | starts}
- # {/, during | equal | starts, l}
- """
- operator : CLPAREN arithmetic CRPAREN
- | CLPAREN arithmetic COMMA relation CRPAREN
- | CLPAREN arithmetic COMMA relationlist CRPAREN
- | CLPAREN arithmetic COMMA relation COMMA temporal CRPAREN
- | CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN
- """
- if not self.optype == "raster":
- raise SyntaxError('Wrong optype "%s" must be "raster"' % self.optype)
- else:
- if len(t) == 4:
- # Set three operator components.
- self.relations = ["equal"]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 6:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 8:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = t[6]
- self.function = t[2]
- t[0] = t[2]
- def p_overlay_relation_operator(self, t):
- # {+}
- # {-, during}
- # {~, during | equal | starts}
- # {^, during | equal | starts, l}
- """
- operator : CLPAREN overlay CRPAREN
- | CLPAREN overlay COMMA relation CRPAREN
- | CLPAREN overlay COMMA relationlist CRPAREN
- | CLPAREN overlay COMMA relation COMMA temporal CRPAREN
- | CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN
- """
- if not self.optype == "overlay":
- raise SyntaxError('Wrong optype "%s" must be "overlay"' % self.optype)
- else:
- if len(t) == 4:
- # Set three operator components.
- self.relations = ["equal"]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 6:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = "l"
- self.function = t[2]
- elif len(t) == 8:
- if isinstance(t[4], list):
- self.relations = t[4]
- else:
- self.relations = [t[4]]
- self.temporal = t[6]
- self.function = t[2]
- t[0] = t[2]
- def p_relation(self, t):
- # The list of relations. Temporal and spatial relations are supported
- """
- relation : EQUAL
- | FOLLOWS
- | PRECEDES
- | OVERLAPS
- | OVERLAPPED
- | DURING
- | STARTS
- | FINISHES
- | CONTAINS
- | STARTED
- | FINISHED
- | EQUIVALENT
- | COVER
- | OVERLAP
- | IN
- | CONTAIN
- | MEET
- """
- t[0] = t[1]
- def p_over(self, t):
- # The the over keyword
- """
- relation : OVER
- """
- over_list = ["overlaps", "overlapped"]
- t[0] = over_list
- def p_relationlist(self, t):
- # The list of relations.
- """
- relationlist : relation OR relation
- | relation OR relationlist
- """
- rel_list = []
- rel_list.append(t[1])
- if isinstance(t[3], list):
- rel_list = rel_list + t[3]
- else:
- rel_list.append(t[3])
- t[0] = rel_list
- def p_temporal_operator(self, t):
- # The list of relations.
- """
- temporal : LEFTREF
- | RIGHTREF
- | UNION
- | DISJOINT
- | INTERSECT
- """
- t[0] = t[1]
- def p_select_operator(self, t):
- # The list of relations.
- """
- select : T_SELECT
- | T_NOT_SELECT
- """
- t[0] = t[1]
- def p_arithmetic_operator(self, t):
- # The list of relations.
- """
- arithmetic : MOD
- | DIV
- | MULT
- | ADD
- | SUB
- """
- t[0] = t[1]
- def p_overlay_operator(self, t):
- # The list of relations.
- """
- overlay : AND
- | OR
- | XOR
- | DISOR
- | NOT
- """
- t[0] = t[1]
- ###############################################################################
- if __name__ == "__main__":
- import doctest
- doctest.testmod()
|