├── TransportSim.gif
├── .gitignore
├── .travis.yml
├── src
├── templates
│ ├── index.tpl
│ └── simulation.tpl
├── routeModel.py
├── view.py
├── simulation.py
├── peopleModel.py
└── models.py
├── LICENSE
└── README.md
/TransportSim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidbailey/TransportSim/HEAD/TransportSim.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | var/los-angeles_california.osm
3 | var/los-angeles_california.osm.bz2
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | scala:
3 | - 2.11.7
4 | notifications:
5 | slack:
6 | secure: vrIQgoWk6vxxlXh42Y8CpMT+I7l0zvqiE08sHWO6aRTDF6imxzbjfUxfXvw1Ge0w8lH6YGywz6vTNRT9IA0eznCCt8pIJvhu4hCbFevdWKzIG5b+rf/lNCbxzugfTbY54ddAaG9aFo1R4ebJD70amJvFYp0yLr3VSYKuMTuxOX70yWPLQBNtxSIP/WIGJJY6vMMpCsygjRQLndrzQgA8MGrW6pKUdg8Y5C4p+V5grDuPjTWL6XLPzHJQZeE/xDsGWT302xZnyqMgC3xKoEiOPoGN2FGem6zcaT9SowyczmNjGxGQO/2LQPAxvhVtE+dsE4CQwCqxJM1bJXPrMTCaeiTi6MPNsR8zPXr8pyXjbn5qyYEvdZ0kUQrX4lUhYv0kJH5LfzUWSIzpQyxuJkt6PYTq+dX5MNeLVFyiDf1vZmsdGVFI/LJfKxI/6LtF4U9C8/C5G4wQZK2fs3QifkL9rqTDzrF1c+7x53Mqzohx9FNe8cU5PWZzpmNpZMwOwmA4mKBzCkxhwiyPGrY2ofHRTw4cRt7uIkdq09BUOHAeKGCUkWwLgvqz+A+vTc+9fr9MV9NYU1xpmMqq+1FFwnEu7+vm1XCrIMa5av9LyV2WkdI7sas2TmYyLFdSOFePUVCcLrt3zm2xkXSh2bzdZR85T71Rm/x3VVjBSBsYgIb/hgQ=
7 |
--------------------------------------------------------------------------------
/src/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TransportSim
5 |
6 |
7 |
8 | Welcome to TransportSim.
9 |
10 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/routeModel.py:
--------------------------------------------------------------------------------
1 | # install http://wiki.openstreetmap.org/wiki/Open_Source_Routing_Machine
2 | import pandas
3 | import os
4 | import requests
5 | from tqdm import trange
6 |
7 | trips = pandas.read_csv(os.path.expanduser('~/TransportSim/var/trips.csv'))
8 | routes = []
9 |
10 | #for i in trange(len(trips) - 1):
11 | for i in trange((len(trips) - 1)/10000):
12 | #r = requests.get('http://localhost:5000/viaroute?loc=' + str(trips.iloc[i].hY) + ',' + str(trips.iloc[i].hX) + '&loc=' + str(trips.iloc[i].wY) + ',' + str(trips.iloc[i].wX))
13 | r = requests.get('http://localhost:5000/viaroute?loc=' + str(trips.iloc[10000*i].hY) + ',' + str(trips.iloc[10000*i].hX) + '&loc=' + str(trips.iloc[10000*i].wY) + ',' + str(trips.iloc[10000*i].wX))
14 | try: routes.append(r.json()['route_geometry'])
15 | except: print "AppendError"
16 |
17 | f = open(os.path.expanduser('~/TransportSim/var/routes.polystrings'), 'w')
18 | for route in routes:
19 | f.write(route + '\n')
20 |
21 | f.close()
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/src/view.py:
--------------------------------------------------------------------------------
1 | from os.path import expanduser
2 | from subprocess import Popen
3 | from bottle import route, run, template
4 | #from kafka import KafkaConsumer
5 | import json
6 | import redis
7 |
8 | r = redis.StrictRedis(host='localhost', port=6379, db=0)
9 | #carsConsumer = KafkaConsumer('cars', group_id='kafka-python-default-group', bootstrap_servers=['localhost:9092'])
10 | #peopleConsumer = KafkaConsumer('people', group_id='kafka-python-default-group', bootstrap_servers=['localhost:9092'])
11 | previousCarView = {}
12 | previousPeopleView = {}
13 |
14 | @route('/')
15 | def index():
16 | f = open(expanduser('~/TransportSim/src/main/python/templates/index.tpl'), 'r')
17 | return f.read()
18 | f.close()
19 |
20 | @route('/startsimulation')
21 | def startsimulation():
22 | #Popen(["sbt", "run"])
23 | return {'started': 'true'}
24 |
25 | @route('/simulation')
26 | def simulation():
27 | f = open(expanduser('~/TransportSim/src/main/python/templates/simulation.tpl'), 'r')
28 | return f.read()
29 | f.close()
30 |
31 | @route('/api/getCars')
32 | def getCars():
33 | global previousCarView
34 | # try:
35 | # carView = list(carsConsumer.poll())[-1].value
36 | # previousCarView = carView
37 | # except:
38 | # carView = previousCarView
39 | #print carView
40 | return json.loads(carView)
41 |
42 | @route('/api/getPeople')
43 | def getPeople():
44 | global previousPeopleView
45 | # try:
46 | # peopleView = list(peopleConsumer.poll())[-1].value
47 | # previousPeopleView = peopleView
48 | # except:
49 | # peopleView = previousPeopleView
50 | #print peopleView
51 | try:
52 | peopleView = r.get('people')
53 | except:
54 | peopleView = previousPeopleView
55 | return json.loads(peopleView)
56 |
57 | run(host='localhost', port=8080)
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TransportSim: A transportation simulation. #
2 |
3 | [](https://travis-ci.org/davidbailey/TransportSim)
4 | [](https://coveralls.io/github/davidbailey/TransportSim?branch=master)
5 |
6 | 
7 |
8 | ## Architecture ##
9 |
10 | 1. python/peopleModel - generates people (origins and destinations)
11 | 2. python/routeModel - generates a route for each person (polylines)
12 | 3. python/simulation - simulates people (polylines) moving; sends views to redis
13 | 4. python/view - view the models and simulations from redis (openlayers + bottle REST API)
14 |
15 | ## Documentation ##
16 | Time is always in seconds.
17 | Distances have their own class and can be feet or meters.
18 |
19 | ## Install ##
20 |
21 | ```
22 | git clone https://github.com/davidbailey/TransportSim.git
23 |
24 | pip install pandas geopandas shapely requests bottle redis
25 |
26 | # osrm-backend
27 | brew install osrm-backend
28 | wget http://download.geofabrik.de/north-america/us/california-latest.osm.pbf
29 | ./osrm-extract california-latest.osm.pbf
30 | ./osrm-prepare california-latest.osrm
31 | ./osrm-routed california-latest.osrm
32 |
33 | # download osm
34 | cd var
35 | wget https://s3.amazonaws.com/metro-extracts.mapzen.com/los-angeles_california.osm.bz2
36 | bunzip2 los-angeles_california.osm.bz2
37 |
38 | # redis
39 | brew install redis
40 | redis-server
41 |
42 | # TransportSim
43 | cd TransportSim
44 | python src/view.py
45 | python src/simulation.py
46 | Launch a browser and open http://localhost:8080/
47 | ```
48 |
--------------------------------------------------------------------------------
/src/simulation.py:
--------------------------------------------------------------------------------
1 | import models
2 | from os.path import expanduser
3 | from polyline import decode
4 | from random import randint
5 | #from kafka import KafkaProducer
6 | import json
7 | import redis
8 |
9 | beginDepartures = 21600
10 | endDepartures = 43200
11 |
12 | footRoutes = []
13 | with open(expanduser('~/TransportSim/var/foot/routes.polystrings-1700'),'r') as footRoutesFile:
14 | for route in footRoutesFile:
15 | footRoutes.append(decode(route.rstrip()))
16 |
17 | bicycleRoutes = []
18 | with open(expanduser('~/TransportSim/var/bicycle/routes.polystrings-1700'),'r') as bicycleRoutesFile:
19 | for route in bicycleRoutesFile:
20 | bicycleRoutes.append(decode(route.rstrip()))
21 |
22 | carRoutes = []
23 | with open(expanduser('~/TransportSim/var/car/routes.polystrings-1700'),'r') as carRoutesFile:
24 | for route in carRoutesFile:
25 | carRoutes.append(decode(route.rstrip()))
26 |
27 | People = []
28 | Bicycles = []
29 | Cars = []
30 |
31 | for a in range(len(carRoutes)):
32 | footBicycleCar = randint(1,100)
33 | departureTime = randint(beginDepartures,endDepartures)
34 | if footBicycleCar <= 80:
35 | p = models.Person(carRoutes[a],departureTime)
36 | People.append(p)
37 | c = models.Car()
38 | p.vehicle = c
39 | c.driver = p
40 | Cars.append(c)
41 | elif 80 < footBicycleCar < 83:
42 | p = models.Person(bicycleRoutes[a],departureTime)
43 | People.append(p)
44 | b = models.Bicycle()
45 | p.vehicle = b
46 | b.driver = p
47 | Bicycles.append(c)
48 | elif 83 < footBicycleCar:
49 | p = models.Person(footRoutes[a],departureTime)
50 | People.append(p)
51 |
52 | footRoutes = False
53 | bicycleRoutes = False
54 | carRoutes = False
55 |
56 | #producer = KafkaProducer(bootstrap_servers='localhost:9092', value_serializer=lambda v: json.dumps(v).encode('utf-8'))
57 | r = redis.StrictRedis(host='localhost', port=6379, db=0)
58 |
59 | for a in range(1000):
60 | print "Round: " + str(a)
61 | map(models.Person.transport,filter((lambda x: not x.crashed and not x.arrived),People))
62 | peopleView = { "people": map(models.Person.view,People) }
63 | #producer.send('people', peopleView)
64 | r.set('people',json.dumps(peopleView))
65 |
--------------------------------------------------------------------------------
/src/peopleModel.py:
--------------------------------------------------------------------------------
1 | # tract files from http://www.census.gov/cgi-bin/geo/shapefiles2010/file-download
2 | # ca_od_2013 files from http://lehd.ces.census.gov/data/lodes/LODES7/
3 | import os
4 | import geopandas
5 | import pandas
6 | from random import uniform
7 | from shapely.geometry import box, Point
8 |
9 | tractFile = os.path.expanduser('~/TransportSim/var/tl_2010_06_tabblock10.zip')
10 | tracts = geopandas.GeoDataFrame.from_file('/', vfs = 'zip://' + tractFile)
11 |
12 | xmax, xmin, ymax, ymin = (-119.97, -116.80, 34.80, 33.33)
13 | boundingBox = box(xmin, ymin, xmax, ymax)
14 |
15 | tractsInBox = []
16 | for name, tract in tracts.iterrows():
17 | if tract['geometry'].within(boundingBox):
18 | tractsInBox.append(tract)
19 |
20 | tracts = geopandas.GeoDataFrame(tractsInBox)
21 |
22 | ca_od_main_2013s = []
23 | for i in range(1,6):
24 | ca_od_main_i_2013 = pandas.DataFrame.from_csv(os.path.expanduser('~/TransportSim/var/ca_od_2013/ca_od_main_JT0' + str(i) + '_2013.csv'), index_col=False)
25 | ca_od_main_2013s.append(ca_od_main_i_2013)
26 |
27 | ca_od_main_2013 = pandas.concat(ca_od_main_2013s)
28 |
29 | fix = lambda x: '0' + unicode(x)
30 |
31 | ca_od_main_2013['wGEOID'] = ca_od_main_2013['w_geocode']
32 | ca_od_main_2013['wGEOID'] = ca_od_main_2013['wGEOID'].apply(fix)
33 | ca_od_main_2013['hGEOID'] = ca_od_main_2013['h_geocode']
34 | ca_od_main_2013['hGEOID'] = ca_od_main_2013['hGEOID'].apply(fix)
35 |
36 | w_coded = pandas.merge(ca_od_main_2013, tracts, how='inner', left_on='wGEOID', right_on='GEOID10')
37 | wh_coded = pandas.merge(w_coded, tracts, how='inner', left_on='hGEOID', right_on='GEOID10', suffixes=['W','H'])
38 |
39 | def randomPointInPolygon(polygon):
40 | (xmin, ymin, xmax, ymax) = polygon.bounds
41 | while True:
42 | x = uniform(xmin,xmax)
43 | y = uniform(ymin,ymax)
44 | point = Point(x,y)
45 | if point.within(polygon):
46 | return point
47 |
48 | trips = []
49 | for name, row in wh_coded.iterrows():
50 | for i in range(0,row['S000']):
51 | hPoint = randomPointInPolygon(row.geometryH)
52 | wPoint = randomPointInPolygon(row.geometryW)
53 | trips.append([hPoint.y, hPoint.x, wPoint.y, wPoint.x])
54 |
55 | pandas.DataFrame(trips, columns = ['hY', 'hX', 'wY', 'wX']).to_csv(os.path.expanduser('~/TransportSim/var/trips.csv'))
56 |
--------------------------------------------------------------------------------
/src/templates/simulation.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 | TransportSim
13 |
14 |
15 |
16 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/models.py:
--------------------------------------------------------------------------------
1 | from shapely.geometry import Point
2 | from random import randint
3 | from random import random
4 | from sys import maxint
5 | from math import sqrt
6 | from math import atan
7 | from math import sin
8 | from math import cos
9 |
10 | class Person:
11 | def __init__(self, routeIn, departureTimeIn):
12 | self.route = routeIn
13 | self.departureTime = departureTimeIn
14 | self.id = randint(0,maxint)
15 | self.width = 1.5 + random()
16 | self.length = 0.5 + random()
17 | self.arrived = False
18 | self.crashed = False
19 | self.travelTime = 0
20 | self.speed = 5.0
21 | self.currentRouteSegment = 0
22 | self.centroid = self.route[0]
23 | self.vehicle = False
24 | def vehicleType(self):
25 | if self.vehicle: return self.vehicle.subtype
26 | else: return("Person")
27 | def view(self):
28 | return({"type": "Feature", "id": self.id, "geometry": {"type": "Point", "coordinates": [self.centroid[1]/10, self.centroid[0]/10]}, "properties": {"arrived": self.arrived, "crashed": self.crashed, "vehicle": self.vehicleType()}})
29 | #return("{\"type\": \"Feature\", \"id\": " + str(self.id) + ", \"geometry\": {\"type\": \"Point\", \"coordinates\": [" + str(self.centroid[1]/10) + "," + str(self.centroid[0]/10) + "]}, \"properties\": {\"arrived\": \"" + str(self.arrived) + "\", \"crashed\": \"" + str(self.crashed) + "\", \"vehicle\": \"" + str(self.vehicleType()) + "\"}}")
30 | # Car/Bike/Ped Route - drive/ride/walk until you hit an intersection, maybe change lanes. at intersection stopLight, stopSign, or go: stright, left, right. Repeat.
31 | # Freeway Route - enter, drive until you exit.
32 | def transport(self):
33 | self.travelTime += 1
34 | nextRouteSegment = self.currentRouteSegment + 1
35 | xDelta = self.route[nextRouteSegment][0] - self.route[self.currentRouteSegment][0]
36 | yDelta = self.route[nextRouteSegment][1] - self.route[self.currentRouteSegment][1]
37 | if sqrt(xDelta**2+yDelta**2) > self.speed: # straightaway logic
38 | theta = atan(xDelta / yDelta)
39 | self.centroid = Point(self.centroid[0] + sin(theta) * self.speed, self.centroid[1] + cos(theta) * self.speed)
40 | else: # intersection logic
41 | self.currentRouteSegment = nextRouteSegment
42 | self.centroid = self.route[self.currentRouteSegment]
43 | if self.currentRouteSegment == len(self.route) - 1:
44 | self.arrived = True
45 |
46 | class Vehicle:
47 | def __init__(self):
48 | self.id = randint(0,maxint)
49 | self.driver = False
50 | self.subtype = "Vehicle"
51 | self.passengers = []
52 | self.maxPassengers = 0
53 | self.width = 0.0
54 | self.length = 0.0
55 | self.crashed = False
56 | self.centroid = Point(0,0)
57 |
58 | class Bicycle(Vehicle):
59 | def __init__(self):
60 | self.subtype = "Bicycle"
61 | self.maxPassengers = 1
62 | self.width = 2.0 + random()
63 | self.length = 6.0 + random()
64 |
65 | class Car(Vehicle):
66 | def __init__(self):
67 | self.subtype = "Car"
68 | self.maxPassengers = 5
69 | self.width = 6.0 + random()
70 | self.length = 12.0 + random()
71 |
72 | class Bus(Vehicle):
73 | def __init__(self):
74 | self.subtype = "Bus"
75 | self.maxPassengers = 84
76 | self.width = 8.0 + random()
77 | self.length = 40.0 + random()
78 |
79 | class LightRail(Vehicle):
80 | def __init__(self):
81 | self.subtype = "LightRail"
82 | self.maxPassengers = 220
83 |
84 | class HeavyRail(Vehicle):
85 | def __init__(self):
86 | self.subtype = "HeavyRail"
87 | self.maxPassengers = 800
88 |
89 | class Track:
90 | def __init__(self):
91 | self.width = 4.708
92 |
93 | class Railway:
94 | def __init__(self):
95 | self.tracks = []
96 |
97 | class Lane:
98 | def __init__(self):
99 | self.width = 0.0
100 |
101 | class GeneralLane(Lane):
102 | def __init__(self):
103 | self.width = 10.0
104 | self.allowedVehicles = ["Bicycle,Car,Bus"]
105 |
106 | class FreewayLane(Lane):
107 | def __init__(self):
108 | self.width = 13.0
109 | self.allowedVehicles = ["Car,Bus"]
110 |
111 | class ParkingLane(Lane):
112 | def __init__(self):
113 | self.width = 10.0
114 | self.allowedVehicles = ["Bicycle,Car"]
115 |
116 | class BicycleLane(Lane):
117 | def __init__(self):
118 | self.width = 5.0
119 | self.allowedVehicles = ["Bicycle"]
120 |
121 | class BusLane(Lane):
122 | def __init__(self):
123 | self.width = 5.0
124 | self.allowedVehicles = ["Bicycle,Bus"]
125 |
126 | class Sidewalk(Lane):
127 | def __init__(self):
128 | self.width = 4.0
129 |
130 | class Way: # OSM Way
131 | def __init__(self):
132 | pass
133 |
134 | class HalfRoad:
135 | def __init__(self):
136 | self.lanes = []
137 | self.parkingLane = ParkingLane()
138 | self.bicycleLane = BicycleLane()
139 | self.busLane = BusLane()
140 | self.sidewalk = Sidewalk()
141 |
142 | class TwoWayRoad:
143 | def __init__(self):
144 | self.wayOne = HalfRoad()
145 | self.wayTwo = HalfRoad()
146 | self.centerLine = []
147 |
148 | class OneWayRoad:
149 | def __init__(self):
150 | self.wayOne = HalfRoad()
151 | self.centerLine = []
152 |
153 | class HalfFreeway:
154 | def __init__(self):
155 | self.freewayLanes = []
156 |
157 | class Freeway:
158 | def __init__(self):
159 | self.wayOne = HalfFreeway()
160 | self.wayTwo = HalfFreeway()
161 | self.centerLine = []
162 |
163 | class ParkingSpace:
164 | def __init__(self):
165 | self.id = randint(0,maxint)
166 | self.width = 8.0
167 | self.length = 16.0
168 | self.centroid = (0.0,0.0)
169 | self.occupant = False
170 | def view(self):
171 | return("{\"type\": \"Feature\", \"id\": " + str(self.id) + ", \"geometry\": {\"type\": \"Point\", \"coordinates\": [" + str(self.centroid[0]) + "," + str(self.centroid[1]) + "]}, \"properties\": {\"vehicle\": \"" + str(self.occupant) + "\"}}")
172 |
173 |
--------------------------------------------------------------------------------