Mercurial Hosting > traffic-intelligence
changeset 1162:efd52c55a72b
added indicator framework file
author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
---|---|
date | Sun, 07 Mar 2021 23:26:51 -0500 |
parents | b968c33f8c2f |
children | fa9c358789ac |
files | trafficintelligence/iframework.py |
diffstat | 1 files changed, 297 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trafficintelligence/iframework.py Sun Mar 07 23:26:51 2021 -0500 @@ -0,0 +1,297 @@ +from enum import Enum +from pathlib import Path +from datetime import datetime + +from sqlalchemy.ext.declarative import declarative_base, declared_attr +from sqlalchemy import Table, Column, Integer, Boolean, String, Float, DateTime, Enum as SQLEnum, ForeignKey, CheckConstraint, create_engine +from sqlalchemy.orm import relationship, backref, sessionmaker + +""" + +""" + +Base = declarative_base() + +GenderEnum = Enum('GenderEnum', 'male female unknown') +ModeEnum = Enum('ModeEnum', 'cardriver carpassenger transit bike walking scooter skating') +VehicleEnum = Enum('VehicleEnum', 'car SUV truck bus bike scooter skate rollers') + +# should there be a survey object for site info, observer, etc? + +class Mode(Base): + 'personal, because in a group (family), some might have a scooter or rollers' + __tablename__ = 'modes' + idx = Column(Integer, primary_key=True) + personIdx = Column(Integer, ForeignKey('persons.idx')) + vehicleIdx = Column(Integer, ForeignKey('vehicles.idx')) + transport = Column(SQLEnum(ModeEnum), nullable=False) + startTime = Column(DateTime) # None first time if only one group + pointIdx = Column(Integer, ForeignKey('points.idx')) + + person = relationship('Person', backref = backref('modes')) + vehicle = relationship('Vehicle') + point = relationship('Point') + + def __init__(self, transport, person, vehicle = None, startTime = None, p = None): + self.person = person + self.transport = transport + self.vehicle = vehicle + self.startTime = startTime + self.point = p + + @staticmethod + def initGroup(transport, group, vehicle = None, startTime = None): + return [Mode(transport, p, startTime) for p in group.getPersons()] + +class Group(Base): + __tablename__ = 'groups' + idx = Column(Integer, primary_key=True) + + def __init__(self, persons): + for p in persons: + GroupBelonging(p, self) + + def getPersons(self): + return [gb.person for gb in self.groupBelongings] + +class GroupBelonging(Base): + __tablename__ = 'groupbelongings' + groupIdx = Column(Integer, ForeignKey('groups.idx'), primary_key=True) + personIdx = Column(Integer, ForeignKey('persons.idx'), primary_key=True) + pointIdx = Column(Integer, ForeignKey('points.idx')) + startTime = Column(DateTime) # None first time if only one group + + person = relationship('Person', backref = backref('groupBelongings')) + group = relationship('Group', backref = backref('groupBelongings')) + point = relationship('Point') + + def __init__(self, person, group, startTime = None, p = None): + self.person = person + self.group = group + self.startTime = startTime + self.point = p + +# in aggregated form, there is a total number of observations for a given time interval, a number for each binary variable and k-1 variables for a categorical variable with k categories +class Person(Base): + __tablename__ = 'persons' + idx = Column(Integer, primary_key=True) + #groupIdx = Column(Integer, ForeignKey('groups.idx')) + age = Column(String) + gender = Column(SQLEnum(GenderEnum), nullable=False) + disability = Column(String) # could be enum + stroller = Column(Boolean) # the booleans could be strings or enum to have more information + bag = Column(Boolean) + animal = Column(Boolean) + + #group = relationship('Group', backref = backref('persons')) + + def __init__(self, age = 'unknown', gender = 'unknown', disability = False, stroller = False, bag = False, animal = False): + self.age = age + self.gender = gender + self.disability = disability + self.stroller = stroller + self.bag = bag + self.animal = animal + + def getAgeNum(self): + if str.isnumeric(self.age): + return int(self.age) + elif '.' in self.age: + try: + return float(self.age) + except ValueError: + pass + else: + return self.age + + def getGroups(self): + if len(self.groupBelongings) > 0: + return [gb.group for gb in self.groupBelongings] + else: + return None + +class Vehicle(Base): + __tablename__ = 'vehicles' + idx = Column(Integer, primary_key=True) + category = Column(SQLEnum(VehicleEnum), nullable=False) + trailer = Column(Boolean) + + def __init__(self, category, trailer = False): + self.category = category + self.trailer = trailer + +class Point(Base): + __tablename__ = 'points' + idx = Column(Integer, primary_key=True) + x = Column(Float) + y = Column(Float) + + def __init__(self, x, y): + self.x = x + self.y = y + +pointLineAssociation = Table('pointlines', Base.metadata, + Column('pointIdx', Integer, ForeignKey('points.idx')), + Column('lineIdx', Integer, ForeignKey('lines.idx'))) + +class Line(Base): + __tablename__ = 'lines' + idx = Column(Integer, primary_key=True) + + points = relationship('Point', secondary=pointLineAssociation) + + def __init__(self, x1, y1, x2, y2): + self.points = [Point(x1, y1), Point(x2, y2)] + +pointZoneAssociation = Table('pointzones', Base.metadata, + Column('pointIdx', Integer, ForeignKey('points.idx')), + Column('zoneIdx', Integer, ForeignKey('zones.idx'))) + +class Zone(Base): + __tablename__ = 'zones' + idx = Column(Integer, primary_key=True) + + points = relationship('Point', secondary=pointZoneAssociation) + + def __init__(self, xs = None, ys = None): + 'xs and ys are the list of x and y coordinates' + if xs is not None and ys is not None: + for x,y in zip(xs, ys): + self.addPoint(x,y) + + def addPoint(self, x, y): + self.points.append(Point(x, y)) + +class AbstractPassing: + def initPersonGroupPassing(self, group, person, transport, vehicle): + if person is None and group is not None: # create group + self.group = group + if transport is not None: + Mode.initGroup(transport, group, vehicle) + elif person is not None and group is None: # create person + self.group = Group([person]) + if transport is not None: + Mode(transport, person, vehicle) + else: + print('Warning: passing person and group or both None') + +class LinePassing(AbstractPassing,Base): + __tablename__ = 'linepassings' + idx = Column(Integer, primary_key=True) + lineIdx = Column(Integer, ForeignKey('lines.idx')) + groupIdx = Column(Integer, ForeignKey('groups.idx')) + pointIdx = Column(Integer, ForeignKey('points.idx')) + instant = Column(DateTime) + speed = Column(Float) + wrongDirection = Column(Boolean) + + line = relationship('Line') + group = relationship('Group') + point = relationship('Point') + + def __init__(self, line, instant, speed = None, wrongDirection = None, p = None, group = None, person = None, transport = None, vehicle = None): + # makes it possible to create person and mode for just counting + # pass transport as string to instantiate after + self.line = line + self.instant = instant + self.speed = speed + self.wrongDirection = wrongDirection + self.point = p + self.initPersonGroupPassing(group, person, transport, vehicle) + +class ZonePassing(AbstractPassing,Base): + __tablename__ = 'zonepassings' + idx = Column(Integer, primary_key=True) + zoneIdx = Column(Integer, ForeignKey('zones.idx')) + groupIdx = Column(Integer, ForeignKey('groups.idx')) + pointIdx = Column(Integer, ForeignKey('points.idx')) + instant = Column(DateTime) + entering = Column(Boolean) + + zone = relationship('Zone') + group = relationship('Group') + point = relationship('Point') + + def __init__(self, zone, instant, entering, p = None, group = None, person = None, transport = None, vehicle = None): + self.zone = zone + self.instant = instant + self.entering = entering + self.point = p + self.initPersonGroupPassing(group, person, transport, vehicle) + +class Activity(AbstractPassing,Base): + __tablename__ = 'activities' + idx = Column(Integer, primary_key=True) + activity = Column(String) # could be enum + groupIdx = Column(Integer, ForeignKey('groups.idx')) + # can an activity be done in a vehicle? Is it relevant? Can it be unambiguously identified? + startTime = Column(DateTime) + endTime = Column(DateTime) + zoneIdx = Column(Integer, ForeignKey('zones.idx')) + pointIdx = Column(Integer, ForeignKey('points.idx')) + + group = relationship('Group') + zone = relationship('Zone') + point = relationship('Point') + + def __init__(self, activity, startTime, endTime, zone, p = None, group = None, person = None, transport = None, vehicle = None): + self.activity = activity + self.startTime = startTime + self.endTime = endTime + self.zone = zone + self.point = p + self.initPersonGroupPassing(group, person, transport, vehicle) + +def createDatabase(filename): + 'creates a session to query the filename' + if Path(filename).is_file(): + print('The file '+filename+' exists') + return None + else: + engine = create_engine('sqlite:///'+filename) + Base.metadata.create_all(engine) + Session = sessionmaker(bind=engine) + return Session() + +def connectDatabase(filename): + 'creates a session to query the filename' + if Path(filename).is_file(): + engine = create_engine('sqlite:///'+filename) + Session = sessionmaker(bind=engine) + return Session() + else: + print('The file '+filename+' does not exist') + return None + +if __name__ == '__main__': # demo code + session = createDatabase('test.sqlite') + if session is None: + session = connectDatabase('test.sqlite') + # count example + p = Person(6, 'female', bag = True) + veh1 = Vehicle('car') + modes = [Mode('cardriver', p, veh1), Mode('walking', p, startTime = datetime(2020,7,7,11,20))] + + line = Line(0.,0.,0.,10.) + zone = Zone([0., 0., 1., 1.], [0., 1., 1., 0.]) + destination = Zone([10., 10., 11., 11.], [10., 11., 11., 10.]) + counts = [LinePassing(line, datetime(2020,7,2,23,20+i), person = Person(20+i, 'female', disability = True), transport = 'walking') for i in range(5)] + group1 = Group([Person(13+i,'female', False, False, True, False) for i in range(3)]) + groupMode1 = Mode.initGroup('walking', group1) + activities = [Activity('walking', datetime(2020,7,2,23,0), datetime(2020,7,2,23,10), zone, person = Person(40, 'male', True, False, True, False)), + Activity('eating', datetime(2020,7,2,23,10), datetime(2020,7,2,23,12), zone, person = Person(40, 'male', True, False, True, False)), + Activity('playing', datetime(2020,7,2,22,0), datetime(2020,7,2,23,0), zone, group = group1)] + counts.append(LinePassing(line, datetime(2020,7,2,23,5), group = group1)) + counts.append(LinePassing(line, datetime(2020,7,2,23,7), person = Person(23, 'unknown'), transport = 'cardriver', vehicle = Vehicle('car'))) + counts.append(LinePassing(line, datetime(2020,7,2,23,9), person = Person('teen', 'unknown'), transport = 'scooter', vehicle = Vehicle('scooter'))) + counts.append(LinePassing(line, datetime(2020,7,2,23,11), person = Person(12, 'female'), transport = 'bike')) + counts.append(LinePassing(line, datetime(2020,7,2,23,13), person = Person(), transport = 'car')) # example of counting cars without knowing the driver and passenger's attributes + counts.append(LinePassing(line, datetime(2020,7,2,23,15), group = Group([Person(34+i) for i in range(3)]), transport = 'carpassenger')) + + + counts.append(ZonePassing(zone, datetime(2020,7,7,9,5), True, person = Person(33, 'male', False, False, True, False))) + + session.add_all([line, p, zone, group1, destination]+modes+groupMode1+counts+activities) + + session.commit() + session.close()