Mercurial > hg > nsaunier > traffic-intelligence
comparison trafficintelligence/iframework.py @ 1162:efd52c55a72b
added indicator framework file
| author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
|---|---|
| date | Sun, 07 Mar 2021 23:26:51 -0500 |
| parents | |
| children | fa9c358789ac |
comparison
equal
deleted
inserted
replaced
| 1161:b968c33f8c2f | 1162:efd52c55a72b |
|---|---|
| 1 from enum import Enum | |
| 2 from pathlib import Path | |
| 3 from datetime import datetime | |
| 4 | |
| 5 from sqlalchemy.ext.declarative import declarative_base, declared_attr | |
| 6 from sqlalchemy import Table, Column, Integer, Boolean, String, Float, DateTime, Enum as SQLEnum, ForeignKey, CheckConstraint, create_engine | |
| 7 from sqlalchemy.orm import relationship, backref, sessionmaker | |
| 8 | |
| 9 """ | |
| 10 | |
| 11 """ | |
| 12 | |
| 13 Base = declarative_base() | |
| 14 | |
| 15 GenderEnum = Enum('GenderEnum', 'male female unknown') | |
| 16 ModeEnum = Enum('ModeEnum', 'cardriver carpassenger transit bike walking scooter skating') | |
| 17 VehicleEnum = Enum('VehicleEnum', 'car SUV truck bus bike scooter skate rollers') | |
| 18 | |
| 19 # should there be a survey object for site info, observer, etc? | |
| 20 | |
| 21 class Mode(Base): | |
| 22 'personal, because in a group (family), some might have a scooter or rollers' | |
| 23 __tablename__ = 'modes' | |
| 24 idx = Column(Integer, primary_key=True) | |
| 25 personIdx = Column(Integer, ForeignKey('persons.idx')) | |
| 26 vehicleIdx = Column(Integer, ForeignKey('vehicles.idx')) | |
| 27 transport = Column(SQLEnum(ModeEnum), nullable=False) | |
| 28 startTime = Column(DateTime) # None first time if only one group | |
| 29 pointIdx = Column(Integer, ForeignKey('points.idx')) | |
| 30 | |
| 31 person = relationship('Person', backref = backref('modes')) | |
| 32 vehicle = relationship('Vehicle') | |
| 33 point = relationship('Point') | |
| 34 | |
| 35 def __init__(self, transport, person, vehicle = None, startTime = None, p = None): | |
| 36 self.person = person | |
| 37 self.transport = transport | |
| 38 self.vehicle = vehicle | |
| 39 self.startTime = startTime | |
| 40 self.point = p | |
| 41 | |
| 42 @staticmethod | |
| 43 def initGroup(transport, group, vehicle = None, startTime = None): | |
| 44 return [Mode(transport, p, startTime) for p in group.getPersons()] | |
| 45 | |
| 46 class Group(Base): | |
| 47 __tablename__ = 'groups' | |
| 48 idx = Column(Integer, primary_key=True) | |
| 49 | |
| 50 def __init__(self, persons): | |
| 51 for p in persons: | |
| 52 GroupBelonging(p, self) | |
| 53 | |
| 54 def getPersons(self): | |
| 55 return [gb.person for gb in self.groupBelongings] | |
| 56 | |
| 57 class GroupBelonging(Base): | |
| 58 __tablename__ = 'groupbelongings' | |
| 59 groupIdx = Column(Integer, ForeignKey('groups.idx'), primary_key=True) | |
| 60 personIdx = Column(Integer, ForeignKey('persons.idx'), primary_key=True) | |
| 61 pointIdx = Column(Integer, ForeignKey('points.idx')) | |
| 62 startTime = Column(DateTime) # None first time if only one group | |
| 63 | |
| 64 person = relationship('Person', backref = backref('groupBelongings')) | |
| 65 group = relationship('Group', backref = backref('groupBelongings')) | |
| 66 point = relationship('Point') | |
| 67 | |
| 68 def __init__(self, person, group, startTime = None, p = None): | |
| 69 self.person = person | |
| 70 self.group = group | |
| 71 self.startTime = startTime | |
| 72 self.point = p | |
| 73 | |
| 74 # 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 | |
| 75 class Person(Base): | |
| 76 __tablename__ = 'persons' | |
| 77 idx = Column(Integer, primary_key=True) | |
| 78 #groupIdx = Column(Integer, ForeignKey('groups.idx')) | |
| 79 age = Column(String) | |
| 80 gender = Column(SQLEnum(GenderEnum), nullable=False) | |
| 81 disability = Column(String) # could be enum | |
| 82 stroller = Column(Boolean) # the booleans could be strings or enum to have more information | |
| 83 bag = Column(Boolean) | |
| 84 animal = Column(Boolean) | |
| 85 | |
| 86 #group = relationship('Group', backref = backref('persons')) | |
| 87 | |
| 88 def __init__(self, age = 'unknown', gender = 'unknown', disability = False, stroller = False, bag = False, animal = False): | |
| 89 self.age = age | |
| 90 self.gender = gender | |
| 91 self.disability = disability | |
| 92 self.stroller = stroller | |
| 93 self.bag = bag | |
| 94 self.animal = animal | |
| 95 | |
| 96 def getAgeNum(self): | |
| 97 if str.isnumeric(self.age): | |
| 98 return int(self.age) | |
| 99 elif '.' in self.age: | |
| 100 try: | |
| 101 return float(self.age) | |
| 102 except ValueError: | |
| 103 pass | |
| 104 else: | |
| 105 return self.age | |
| 106 | |
| 107 def getGroups(self): | |
| 108 if len(self.groupBelongings) > 0: | |
| 109 return [gb.group for gb in self.groupBelongings] | |
| 110 else: | |
| 111 return None | |
| 112 | |
| 113 class Vehicle(Base): | |
| 114 __tablename__ = 'vehicles' | |
| 115 idx = Column(Integer, primary_key=True) | |
| 116 category = Column(SQLEnum(VehicleEnum), nullable=False) | |
| 117 trailer = Column(Boolean) | |
| 118 | |
| 119 def __init__(self, category, trailer = False): | |
| 120 self.category = category | |
| 121 self.trailer = trailer | |
| 122 | |
| 123 class Point(Base): | |
| 124 __tablename__ = 'points' | |
| 125 idx = Column(Integer, primary_key=True) | |
| 126 x = Column(Float) | |
| 127 y = Column(Float) | |
| 128 | |
| 129 def __init__(self, x, y): | |
| 130 self.x = x | |
| 131 self.y = y | |
| 132 | |
| 133 pointLineAssociation = Table('pointlines', Base.metadata, | |
| 134 Column('pointIdx', Integer, ForeignKey('points.idx')), | |
| 135 Column('lineIdx', Integer, ForeignKey('lines.idx'))) | |
| 136 | |
| 137 class Line(Base): | |
| 138 __tablename__ = 'lines' | |
| 139 idx = Column(Integer, primary_key=True) | |
| 140 | |
| 141 points = relationship('Point', secondary=pointLineAssociation) | |
| 142 | |
| 143 def __init__(self, x1, y1, x2, y2): | |
| 144 self.points = [Point(x1, y1), Point(x2, y2)] | |
| 145 | |
| 146 pointZoneAssociation = Table('pointzones', Base.metadata, | |
| 147 Column('pointIdx', Integer, ForeignKey('points.idx')), | |
| 148 Column('zoneIdx', Integer, ForeignKey('zones.idx'))) | |
| 149 | |
| 150 class Zone(Base): | |
| 151 __tablename__ = 'zones' | |
| 152 idx = Column(Integer, primary_key=True) | |
| 153 | |
| 154 points = relationship('Point', secondary=pointZoneAssociation) | |
| 155 | |
| 156 def __init__(self, xs = None, ys = None): | |
| 157 'xs and ys are the list of x and y coordinates' | |
| 158 if xs is not None and ys is not None: | |
| 159 for x,y in zip(xs, ys): | |
| 160 self.addPoint(x,y) | |
| 161 | |
| 162 def addPoint(self, x, y): | |
| 163 self.points.append(Point(x, y)) | |
| 164 | |
| 165 class AbstractPassing: | |
| 166 def initPersonGroupPassing(self, group, person, transport, vehicle): | |
| 167 if person is None and group is not None: # create group | |
| 168 self.group = group | |
| 169 if transport is not None: | |
| 170 Mode.initGroup(transport, group, vehicle) | |
| 171 elif person is not None and group is None: # create person | |
| 172 self.group = Group([person]) | |
| 173 if transport is not None: | |
| 174 Mode(transport, person, vehicle) | |
| 175 else: | |
| 176 print('Warning: passing person and group or both None') | |
| 177 | |
| 178 class LinePassing(AbstractPassing,Base): | |
| 179 __tablename__ = 'linepassings' | |
| 180 idx = Column(Integer, primary_key=True) | |
| 181 lineIdx = Column(Integer, ForeignKey('lines.idx')) | |
| 182 groupIdx = Column(Integer, ForeignKey('groups.idx')) | |
| 183 pointIdx = Column(Integer, ForeignKey('points.idx')) | |
| 184 instant = Column(DateTime) | |
| 185 speed = Column(Float) | |
| 186 wrongDirection = Column(Boolean) | |
| 187 | |
| 188 line = relationship('Line') | |
| 189 group = relationship('Group') | |
| 190 point = relationship('Point') | |
| 191 | |
| 192 def __init__(self, line, instant, speed = None, wrongDirection = None, p = None, group = None, person = None, transport = None, vehicle = None): | |
| 193 # makes it possible to create person and mode for just counting | |
| 194 # pass transport as string to instantiate after | |
| 195 self.line = line | |
| 196 self.instant = instant | |
| 197 self.speed = speed | |
| 198 self.wrongDirection = wrongDirection | |
| 199 self.point = p | |
| 200 self.initPersonGroupPassing(group, person, transport, vehicle) | |
| 201 | |
| 202 class ZonePassing(AbstractPassing,Base): | |
| 203 __tablename__ = 'zonepassings' | |
| 204 idx = Column(Integer, primary_key=True) | |
| 205 zoneIdx = Column(Integer, ForeignKey('zones.idx')) | |
| 206 groupIdx = Column(Integer, ForeignKey('groups.idx')) | |
| 207 pointIdx = Column(Integer, ForeignKey('points.idx')) | |
| 208 instant = Column(DateTime) | |
| 209 entering = Column(Boolean) | |
| 210 | |
| 211 zone = relationship('Zone') | |
| 212 group = relationship('Group') | |
| 213 point = relationship('Point') | |
| 214 | |
| 215 def __init__(self, zone, instant, entering, p = None, group = None, person = None, transport = None, vehicle = None): | |
| 216 self.zone = zone | |
| 217 self.instant = instant | |
| 218 self.entering = entering | |
| 219 self.point = p | |
| 220 self.initPersonGroupPassing(group, person, transport, vehicle) | |
| 221 | |
| 222 class Activity(AbstractPassing,Base): | |
| 223 __tablename__ = 'activities' | |
| 224 idx = Column(Integer, primary_key=True) | |
| 225 activity = Column(String) # could be enum | |
| 226 groupIdx = Column(Integer, ForeignKey('groups.idx')) | |
| 227 # can an activity be done in a vehicle? Is it relevant? Can it be unambiguously identified? | |
| 228 startTime = Column(DateTime) | |
| 229 endTime = Column(DateTime) | |
| 230 zoneIdx = Column(Integer, ForeignKey('zones.idx')) | |
| 231 pointIdx = Column(Integer, ForeignKey('points.idx')) | |
| 232 | |
| 233 group = relationship('Group') | |
| 234 zone = relationship('Zone') | |
| 235 point = relationship('Point') | |
| 236 | |
| 237 def __init__(self, activity, startTime, endTime, zone, p = None, group = None, person = None, transport = None, vehicle = None): | |
| 238 self.activity = activity | |
| 239 self.startTime = startTime | |
| 240 self.endTime = endTime | |
| 241 self.zone = zone | |
| 242 self.point = p | |
| 243 self.initPersonGroupPassing(group, person, transport, vehicle) | |
| 244 | |
| 245 def createDatabase(filename): | |
| 246 'creates a session to query the filename' | |
| 247 if Path(filename).is_file(): | |
| 248 print('The file '+filename+' exists') | |
| 249 return None | |
| 250 else: | |
| 251 engine = create_engine('sqlite:///'+filename) | |
| 252 Base.metadata.create_all(engine) | |
| 253 Session = sessionmaker(bind=engine) | |
| 254 return Session() | |
| 255 | |
| 256 def connectDatabase(filename): | |
| 257 'creates a session to query the filename' | |
| 258 if Path(filename).is_file(): | |
| 259 engine = create_engine('sqlite:///'+filename) | |
| 260 Session = sessionmaker(bind=engine) | |
| 261 return Session() | |
| 262 else: | |
| 263 print('The file '+filename+' does not exist') | |
| 264 return None | |
| 265 | |
| 266 if __name__ == '__main__': # demo code | |
| 267 session = createDatabase('test.sqlite') | |
| 268 if session is None: | |
| 269 session = connectDatabase('test.sqlite') | |
| 270 # count example | |
| 271 p = Person(6, 'female', bag = True) | |
| 272 veh1 = Vehicle('car') | |
| 273 modes = [Mode('cardriver', p, veh1), Mode('walking', p, startTime = datetime(2020,7,7,11,20))] | |
| 274 | |
| 275 line = Line(0.,0.,0.,10.) | |
| 276 zone = Zone([0., 0., 1., 1.], [0., 1., 1., 0.]) | |
| 277 destination = Zone([10., 10., 11., 11.], [10., 11., 11., 10.]) | |
| 278 counts = [LinePassing(line, datetime(2020,7,2,23,20+i), person = Person(20+i, 'female', disability = True), transport = 'walking') for i in range(5)] | |
| 279 group1 = Group([Person(13+i,'female', False, False, True, False) for i in range(3)]) | |
| 280 groupMode1 = Mode.initGroup('walking', group1) | |
| 281 activities = [Activity('walking', datetime(2020,7,2,23,0), datetime(2020,7,2,23,10), zone, person = Person(40, 'male', True, False, True, False)), | |
| 282 Activity('eating', datetime(2020,7,2,23,10), datetime(2020,7,2,23,12), zone, person = Person(40, 'male', True, False, True, False)), | |
| 283 Activity('playing', datetime(2020,7,2,22,0), datetime(2020,7,2,23,0), zone, group = group1)] | |
| 284 counts.append(LinePassing(line, datetime(2020,7,2,23,5), group = group1)) | |
| 285 counts.append(LinePassing(line, datetime(2020,7,2,23,7), person = Person(23, 'unknown'), transport = 'cardriver', vehicle = Vehicle('car'))) | |
| 286 counts.append(LinePassing(line, datetime(2020,7,2,23,9), person = Person('teen', 'unknown'), transport = 'scooter', vehicle = Vehicle('scooter'))) | |
| 287 counts.append(LinePassing(line, datetime(2020,7,2,23,11), person = Person(12, 'female'), transport = 'bike')) | |
| 288 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 | |
| 289 counts.append(LinePassing(line, datetime(2020,7,2,23,15), group = Group([Person(34+i) for i in range(3)]), transport = 'carpassenger')) | |
| 290 | |
| 291 | |
| 292 counts.append(ZonePassing(zone, datetime(2020,7,7,9,5), True, person = Person(33, 'male', False, False, True, False))) | |
| 293 | |
| 294 session.add_all([line, p, zone, group1, destination]+modes+groupMode1+counts+activities) | |
| 295 | |
| 296 session.commit() | |
| 297 session.close() |
