demandify 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. demandify-0.0.1/LICENSE +21 -0
  2. demandify-0.0.1/MANIFEST.in +24 -0
  3. demandify-0.0.1/PKG-INFO +270 -0
  4. demandify-0.0.1/README.md +226 -0
  5. demandify-0.0.1/demandify/__init__.py +5 -0
  6. demandify-0.0.1/demandify/__main__.py +14 -0
  7. demandify-0.0.1/demandify/app.py +49 -0
  8. demandify-0.0.1/demandify/cache/__init__.py +0 -0
  9. demandify-0.0.1/demandify/cache/keys.py +71 -0
  10. demandify-0.0.1/demandify/cache/manager.py +99 -0
  11. demandify-0.0.1/demandify/calibration/__init__.py +0 -0
  12. demandify-0.0.1/demandify/calibration/objective.py +157 -0
  13. demandify-0.0.1/demandify/calibration/optimizer.py +275 -0
  14. demandify-0.0.1/demandify/calibration/worker.py +219 -0
  15. demandify-0.0.1/demandify/cli.py +287 -0
  16. demandify-0.0.1/demandify/config.py +122 -0
  17. demandify-0.0.1/demandify/export/__init__.py +0 -0
  18. demandify-0.0.1/demandify/export/custom_formats.py +221 -0
  19. demandify-0.0.1/demandify/export/exporter.py +170 -0
  20. demandify-0.0.1/demandify/export/report.py +332 -0
  21. demandify-0.0.1/demandify/export/visualize.py +46 -0
  22. demandify-0.0.1/demandify/pipeline.py +776 -0
  23. demandify-0.0.1/demandify/providers/__init__.py +0 -0
  24. demandify-0.0.1/demandify/providers/base.py +45 -0
  25. demandify-0.0.1/demandify/providers/osm.py +128 -0
  26. demandify-0.0.1/demandify/providers/tomtom.py +229 -0
  27. demandify-0.0.1/demandify/static/banner.png +0 -0
  28. demandify-0.0.1/demandify/static/css/style.css +165 -0
  29. demandify-0.0.1/demandify/static/icon.png +0 -0
  30. demandify-0.0.1/demandify/static/js/app.js +472 -0
  31. demandify-0.0.1/demandify/sumo/__init__.py +0 -0
  32. demandify-0.0.1/demandify/sumo/demand.py +358 -0
  33. demandify-0.0.1/demandify/sumo/matching.py +329 -0
  34. demandify-0.0.1/demandify/sumo/network.py +260 -0
  35. demandify-0.0.1/demandify/sumo/simulation.py +386 -0
  36. demandify-0.0.1/demandify/templates/api_key_section.html +23 -0
  37. demandify-0.0.1/demandify/templates/index.html +522 -0
  38. demandify-0.0.1/demandify/utils/__init__.py +0 -0
  39. demandify-0.0.1/demandify/utils/geometry.py +29 -0
  40. demandify-0.0.1/demandify/utils/logger.py +83 -0
  41. demandify-0.0.1/demandify/utils/logging.py +31 -0
  42. demandify-0.0.1/demandify/utils/validation.py +67 -0
  43. demandify-0.0.1/demandify/utils/visualization.py +48 -0
  44. demandify-0.0.1/demandify/web/__init__.py +0 -0
  45. demandify-0.0.1/demandify/web/routes.py +314 -0
  46. demandify-0.0.1/demandify.egg-info/PKG-INFO +270 -0
  47. demandify-0.0.1/demandify.egg-info/SOURCES.txt +51 -0
  48. demandify-0.0.1/demandify.egg-info/dependency_links.txt +1 -0
  49. demandify-0.0.1/demandify.egg-info/entry_points.txt +2 -0
  50. demandify-0.0.1/demandify.egg-info/requires.txt +28 -0
  51. demandify-0.0.1/demandify.egg-info/top_level.txt +2 -0
  52. demandify-0.0.1/pyproject.toml +90 -0
  53. demandify-0.0.1/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onur Akman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # Exclude development and environment files from Source Distribution
2
+ exclude DEVELOPER_GUIDE.md
3
+ exclude pyproject.toml.bak
4
+ exclude test_ready.py
5
+ exclude SPEC.md
6
+ exclude .env
7
+ exclude .env.example
8
+ exclude pipeline.log
9
+
10
+ # Exclude directories
11
+ recursive-exclude .agent *
12
+ recursive-exclude .gemini *
13
+ recursive-exclude demandify_runs *
14
+ recursive-exclude logs *
15
+ recursive-exclude cache *
16
+ recursive-exclude .demandify *
17
+ recursive-exclude venv *
18
+ recursive-exclude tests *
19
+ recursive-exclude scripts *
20
+ recursive-exclude reference *
21
+
22
+ # Ensure operational data is included (matching pyproject.toml)
23
+ recursive-include demandify/templates *.html
24
+ recursive-include demandify/static *
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.4
2
+ Name: demandify
3
+ Version: 0.0.1
4
+ Summary: Calibrate SUMO traffic simulations against real-world congestion data
5
+ Author: demandify contributors
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: Topic :: Scientific/Engineering :: GIS
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: fastapi>=0.104.0
17
+ Requires-Dist: uvicorn[standard]>=0.24.0
18
+ Requires-Dist: jinja2>=3.1.0
19
+ Requires-Dist: httpx>=0.25.0
20
+ Requires-Dist: python-multipart>=0.0.6
21
+ Requires-Dist: shapely>=2.0.0
22
+ Requires-Dist: rtree>=1.1.0
23
+ Requires-Dist: protobuf>=4.24.0
24
+ Requires-Dist: numpy>=1.24.0
25
+ Requires-Dist: pandas>=2.0.0
26
+ Requires-Dist: lxml>=4.9.0
27
+ Requires-Dist: xmltodict>=0.13.0
28
+ Requires-Dist: deap>=1.4.0
29
+ Requires-Dist: matplotlib>=3.7.0
30
+ Requires-Dist: plotly>=5.17.0
31
+ Requires-Dist: python-dotenv>=1.0.0
32
+ Requires-Dist: pydantic>=2.0.0
33
+ Requires-Dist: pydantic-settings>=2.0.0
34
+ Requires-Dist: pyproj>=3.4.0
35
+ Requires-Dist: tqdm>=4.65.0
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
39
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
40
+ Requires-Dist: black>=23.7.0; extra == "dev"
41
+ Requires-Dist: ruff>=0.0.286; extra == "dev"
42
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ ![demandify](static/banner.png)
46
+
47
+ # Welcome to demandify!
48
+
49
+ > [!CAUTION]
50
+ > **BETA VERSION**: This project is currently in early beta (0.0.1). Expect frequent breaking changes and instability.
51
+ > Use at your own risk.
52
+
53
+ **Turn real-world traffic data into accurate SUMO simulations.**
54
+
55
+ Have you ever wanted to create a realistic traffic simulation but got stuck figuring out where the cars should go? **demandify** solves that.
56
+
57
+ It's simple: Pick a spot on the map and demandify will:
58
+ 1. Fetch real-time congestion data from TomTom 🗺️
59
+ 2. Build a clean SUMO network 🛣️
60
+ 3. Use the Genetic Algorithm to figure out the demand pattern to match that traffic 🧬
61
+
62
+ The result? A ready-to-run SUMO scenario that allows you to test your urban routing policies, even for your CAVs! ([wink](https://github.com/COeXISTENCE-PROJECT/URB) [wink](https://github.com/COeXISTENCE-PROJECT/RouteRL)).
63
+
64
+ ## Features
65
+
66
+ - 🌍 **Real-world calibration**: Uses TomTom Traffic Flow API for live congestion data
67
+ - 🎯 **Seeded & reproducible**: Same seed = identical results for same congestion and bbox
68
+ - 🚗 **Car-only SUMO networks**: Automatic OSM → SUMO conversion with car filtering
69
+ - 🧬 **Genetic algorithm**: Optimizes demand to match observed speeds
70
+ - 💾 **Smart caching**: Content-addressed caching for fast re-runs
71
+ - 📊 **Beautiful reports**: HTML reports with visualizations and statistics
72
+ - 🖥️ **Clean web UI**: Leaflet map, real-time progress stepper, log console
73
+
74
+ ## Quickstart
75
+
76
+ ### 1. Install demandify
77
+
78
+ ```bash
79
+ # Clone the repository
80
+ git clone <repository-url>
81
+ cd demandify
82
+
83
+ # Install in development mode
84
+ pip install -e .
85
+ ```
86
+
87
+ ### 2. Install SUMO 🚦
88
+
89
+ **demandify** requires SUMO (Simulation of Urban MObility) to power its simulations.
90
+
91
+ 👉 **[Download SUMO from the official website](https://eclipse.dev/sumo/)**
92
+
93
+ Once installed, verify it's working:
94
+ ```bash
95
+ demandify doctor
96
+ ```
97
+
98
+ ### 3. Get a TomTom API Key
99
+
100
+ 1. Sign up at [https://developer.tomtom.com/](https://developer.tomtom.com/)
101
+ 2. Create a new app and copy the API key
102
+ 3. The free tier includes 2,500 requests/day
103
+
104
+ ### 4. Run demandify
105
+
106
+ ```bash
107
+ demandify
108
+ ```
109
+
110
+ This starts the web server at [http://127.0.0.1:8000](http://127.0.0.1:8000)
111
+
112
+ ### 5. Calibrate a scenario
113
+
114
+ 1. **Draw a bounding box** on the map
115
+ 2. **Configure parameters** (defaults work well):
116
+ - Time window: 15 or 30 minutes
117
+ - Seed: any integer for reproducibility
118
+ - GA population/generations: controls quality vs speed
119
+ 3. **Paste your API key** (one-time, stored locally)
120
+ 4. **Click "Start Calibration"**
121
+ 5. **Watch the progress** through 8 stages
122
+ 6. **Download your scenario** with `demand.csv`, SUMO network, and report
123
+
124
+ ### 6. Run Headless (Optional) 🤖
125
+
126
+ You can run the full calibration pipeline directly from the command line, ideal for automation or remote servers.
127
+
128
+ ```bash
129
+ # Basic usage (defaults: window=15, pop=50, gen=20)
130
+ demandify run "2.2961,48.8469,2.3071,48.8532" --name Paris_Test_01
131
+
132
+ # Advanced usage with custom parameters
133
+ demandify run "2.2961,48.8469,2.3071,48.8532" \
134
+ --name Paris_Optimization \
135
+ --window 30 \
136
+ --seed 123 \
137
+ --pop 100 \
138
+ --gen 50 \
139
+ --mutation 0.5 \
140
+ --elitism 2
141
+ ```
142
+
143
+ > **Note:** The CLI will pause after fetching/matching data to show matching statistics and ask for confirmation before starting the intensive calibration.
144
+
145
+ #### Parameters
146
+
147
+ | Argument | Type | Default | Description |
148
+ |----------|------|---------|-------------|
149
+ | `bbox` | String | (Req) | Bounding box (`west,south,east,north`) |
150
+ | `--name` | String | Auto | Custom Run ID/Name |
151
+ | `--window` | Int | 15 | Simulation duration (min) |
152
+ | `--seed` | Int | 42 | Random seed |
153
+ | `--pop` | Int | 50 | GA Population size |
154
+ | `--gen` | Int | 20 | GA Generations |
155
+ | `--mutation`| Float | 0.5 | Mutation rate (per individual) |
156
+ | `--crossover`| Float| 0.7 | Crossover rate |
157
+ | `--elitism` | Int | 2 | Top individuals to keep |
158
+ | `--sigma` | Int | 20 | Mutation magnitude (step size) |
159
+ | `--indpb` | Float | 0.3 | Mutation probability (per gene) |
160
+ | `--origins` | Int | 10 | Number of origin candidates |
161
+ | `--destinations` | Int | 10 | Number of destination candidates |
162
+ | `--max-ods` | Int | 50 | Max OD pairs to generate |
163
+ | `--bin-size` | Int | 5 | Time bin size in minutes |
164
+ | `--initial-population` | Int | 1000 | Target initial number of vehicles (controls sparse initialization) |
165
+
166
+ ## How It Works
167
+
168
+ demandify follows an 8-stage pipeline:
169
+
170
+ 1. **Validate inputs** - Check bbox, parameters, API key
171
+ 2. **Fetch traffic snapshot** - Get real-time speeds from TomTom
172
+ 3. **Fetch OSM extract** - Download road network data
173
+ 4. **Build SUMO network** - Convert OSM to car-only SUMO `.net.xml`
174
+ 5. **Map matching** - Match traffic segments to SUMO edges
175
+ 6. **Initialize demand** - Select origin/destination pairs and time bins
176
+ 7. **Calibrate demand** - Run GA to optimize vehicle counts
177
+ 8. **Export scenario** - Generate `demand.csv`, routes, config, and report
178
+
179
+ ### Variability & Consistency
180
+
181
+ While demandify uses seeding (random seed) for all internal stochastic operations (OD selection, GA evolution), **perfect reproducibility is not guaranteed** due to the inherently chaotic nature of traffic microsimulation (SUMO) and real-time data inputs.
182
+
183
+ Seeding ensures *consistency* (runs look similar), but small timing differences in OS scheduling or dynamic routing decisions can lead to divergent outcomes.
184
+
185
+ ### Caching
186
+
187
+ demandify caches:
188
+ - OSM extracts (by bbox)
189
+ - SUMO networks (by bbox + conversion params)
190
+ - Traffic snapshots (by bbox + timestamp bucket)
191
+ - Map matching results
192
+
193
+ Cache location: `~/.demandify/cache/`
194
+
195
+ Clear cache: `demandify cache clear`
196
+
197
+ ## CLI Commands
198
+
199
+ ```bash
200
+ # Start web server (default)
201
+ demandify
202
+
203
+ # Run headless calibration
204
+ demandify run "west,south,east,north"
205
+
206
+ # Check system requirements
207
+ demandify doctor
208
+
209
+ # Clear cache
210
+ demandify cache clear
211
+
212
+ # Show version
213
+ demandify --version
214
+ ```
215
+
216
+ ## Output Files
217
+
218
+ Each run creates a folder with:
219
+
220
+ - **`demand.csv`** - Travel demand with exact schema:
221
+ - `ID`, `origin link id`, `destination link id`, `departure timestep`
222
+ - **`trips.xml`** - SUMO trips file
223
+ - **`routes.rou.xml`** - Routed SUMO routes
224
+ - **`network.net.xml`** - SUMO network
225
+ - **`scenario.sumocfg`** - SUMO configuration (ready to run)
226
+ - **`observed_edges.csv`** - Observed traffic speeds
227
+ - **`run_meta.json`** - Complete run metadata
228
+ - **`report.html`** - Calibration report with visualizations
229
+
230
+ Run the scenario:
231
+ ```bash
232
+ cd demandify_runs/run_<timestamp>
233
+ sumo-gui -c scenario.sumocfg
234
+ ```
235
+
236
+ ## Configuration
237
+
238
+ ### API Keys
239
+
240
+ Three ways to provide your TomTom API key:
241
+
242
+ 1. **Web UI**: Paste in the form (saved to `~/.demandify/config.json`)
243
+ 2. **Environment variable**: `export TOMTOM_API_KEY=your_key`
244
+ 3. **`.env` file**: Copy `.env.example` to `.env` and add your key
245
+
246
+ ## Development
247
+
248
+ ```bash
249
+ # Install with dev dependencies
250
+ pip install -e ".[dev]"
251
+
252
+ # Run tests
253
+ pytest
254
+
255
+ # Format code
256
+ black demandify/
257
+
258
+ # Lint
259
+ ruff check demandify/
260
+ ```
261
+
262
+ ## License
263
+
264
+ MIT
265
+
266
+ ## Acknowledgments
267
+
268
+ - **SUMO**: [Eclipse SUMO](https://eclipse.dev/sumo/)
269
+ - **TomTom**: [Traffic Flow API](https://developer.tomtom.com/traffic-api)
270
+ - **OpenStreetMap**: [© OpenStreetMap contributors](https://www.openstreetmap.org/copyright)
@@ -0,0 +1,226 @@
1
+ ![demandify](static/banner.png)
2
+
3
+ # Welcome to demandify!
4
+
5
+ > [!CAUTION]
6
+ > **BETA VERSION**: This project is currently in early beta (0.0.1). Expect frequent breaking changes and instability.
7
+ > Use at your own risk.
8
+
9
+ **Turn real-world traffic data into accurate SUMO simulations.**
10
+
11
+ Have you ever wanted to create a realistic traffic simulation but got stuck figuring out where the cars should go? **demandify** solves that.
12
+
13
+ It's simple: Pick a spot on the map and demandify will:
14
+ 1. Fetch real-time congestion data from TomTom 🗺️
15
+ 2. Build a clean SUMO network 🛣️
16
+ 3. Use the Genetic Algorithm to figure out the demand pattern to match that traffic 🧬
17
+
18
+ The result? A ready-to-run SUMO scenario that allows you to test your urban routing policies, even for your CAVs! ([wink](https://github.com/COeXISTENCE-PROJECT/URB) [wink](https://github.com/COeXISTENCE-PROJECT/RouteRL)).
19
+
20
+ ## Features
21
+
22
+ - 🌍 **Real-world calibration**: Uses TomTom Traffic Flow API for live congestion data
23
+ - 🎯 **Seeded & reproducible**: Same seed = identical results for same congestion and bbox
24
+ - 🚗 **Car-only SUMO networks**: Automatic OSM → SUMO conversion with car filtering
25
+ - 🧬 **Genetic algorithm**: Optimizes demand to match observed speeds
26
+ - 💾 **Smart caching**: Content-addressed caching for fast re-runs
27
+ - 📊 **Beautiful reports**: HTML reports with visualizations and statistics
28
+ - 🖥️ **Clean web UI**: Leaflet map, real-time progress stepper, log console
29
+
30
+ ## Quickstart
31
+
32
+ ### 1. Install demandify
33
+
34
+ ```bash
35
+ # Clone the repository
36
+ git clone <repository-url>
37
+ cd demandify
38
+
39
+ # Install in development mode
40
+ pip install -e .
41
+ ```
42
+
43
+ ### 2. Install SUMO 🚦
44
+
45
+ **demandify** requires SUMO (Simulation of Urban MObility) to power its simulations.
46
+
47
+ 👉 **[Download SUMO from the official website](https://eclipse.dev/sumo/)**
48
+
49
+ Once installed, verify it's working:
50
+ ```bash
51
+ demandify doctor
52
+ ```
53
+
54
+ ### 3. Get a TomTom API Key
55
+
56
+ 1. Sign up at [https://developer.tomtom.com/](https://developer.tomtom.com/)
57
+ 2. Create a new app and copy the API key
58
+ 3. The free tier includes 2,500 requests/day
59
+
60
+ ### 4. Run demandify
61
+
62
+ ```bash
63
+ demandify
64
+ ```
65
+
66
+ This starts the web server at [http://127.0.0.1:8000](http://127.0.0.1:8000)
67
+
68
+ ### 5. Calibrate a scenario
69
+
70
+ 1. **Draw a bounding box** on the map
71
+ 2. **Configure parameters** (defaults work well):
72
+ - Time window: 15 or 30 minutes
73
+ - Seed: any integer for reproducibility
74
+ - GA population/generations: controls quality vs speed
75
+ 3. **Paste your API key** (one-time, stored locally)
76
+ 4. **Click "Start Calibration"**
77
+ 5. **Watch the progress** through 8 stages
78
+ 6. **Download your scenario** with `demand.csv`, SUMO network, and report
79
+
80
+ ### 6. Run Headless (Optional) 🤖
81
+
82
+ You can run the full calibration pipeline directly from the command line, ideal for automation or remote servers.
83
+
84
+ ```bash
85
+ # Basic usage (defaults: window=15, pop=50, gen=20)
86
+ demandify run "2.2961,48.8469,2.3071,48.8532" --name Paris_Test_01
87
+
88
+ # Advanced usage with custom parameters
89
+ demandify run "2.2961,48.8469,2.3071,48.8532" \
90
+ --name Paris_Optimization \
91
+ --window 30 \
92
+ --seed 123 \
93
+ --pop 100 \
94
+ --gen 50 \
95
+ --mutation 0.5 \
96
+ --elitism 2
97
+ ```
98
+
99
+ > **Note:** The CLI will pause after fetching/matching data to show matching statistics and ask for confirmation before starting the intensive calibration.
100
+
101
+ #### Parameters
102
+
103
+ | Argument | Type | Default | Description |
104
+ |----------|------|---------|-------------|
105
+ | `bbox` | String | (Req) | Bounding box (`west,south,east,north`) |
106
+ | `--name` | String | Auto | Custom Run ID/Name |
107
+ | `--window` | Int | 15 | Simulation duration (min) |
108
+ | `--seed` | Int | 42 | Random seed |
109
+ | `--pop` | Int | 50 | GA Population size |
110
+ | `--gen` | Int | 20 | GA Generations |
111
+ | `--mutation`| Float | 0.5 | Mutation rate (per individual) |
112
+ | `--crossover`| Float| 0.7 | Crossover rate |
113
+ | `--elitism` | Int | 2 | Top individuals to keep |
114
+ | `--sigma` | Int | 20 | Mutation magnitude (step size) |
115
+ | `--indpb` | Float | 0.3 | Mutation probability (per gene) |
116
+ | `--origins` | Int | 10 | Number of origin candidates |
117
+ | `--destinations` | Int | 10 | Number of destination candidates |
118
+ | `--max-ods` | Int | 50 | Max OD pairs to generate |
119
+ | `--bin-size` | Int | 5 | Time bin size in minutes |
120
+ | `--initial-population` | Int | 1000 | Target initial number of vehicles (controls sparse initialization) |
121
+
122
+ ## How It Works
123
+
124
+ demandify follows an 8-stage pipeline:
125
+
126
+ 1. **Validate inputs** - Check bbox, parameters, API key
127
+ 2. **Fetch traffic snapshot** - Get real-time speeds from TomTom
128
+ 3. **Fetch OSM extract** - Download road network data
129
+ 4. **Build SUMO network** - Convert OSM to car-only SUMO `.net.xml`
130
+ 5. **Map matching** - Match traffic segments to SUMO edges
131
+ 6. **Initialize demand** - Select origin/destination pairs and time bins
132
+ 7. **Calibrate demand** - Run GA to optimize vehicle counts
133
+ 8. **Export scenario** - Generate `demand.csv`, routes, config, and report
134
+
135
+ ### Variability & Consistency
136
+
137
+ While demandify uses seeding (random seed) for all internal stochastic operations (OD selection, GA evolution), **perfect reproducibility is not guaranteed** due to the inherently chaotic nature of traffic microsimulation (SUMO) and real-time data inputs.
138
+
139
+ Seeding ensures *consistency* (runs look similar), but small timing differences in OS scheduling or dynamic routing decisions can lead to divergent outcomes.
140
+
141
+ ### Caching
142
+
143
+ demandify caches:
144
+ - OSM extracts (by bbox)
145
+ - SUMO networks (by bbox + conversion params)
146
+ - Traffic snapshots (by bbox + timestamp bucket)
147
+ - Map matching results
148
+
149
+ Cache location: `~/.demandify/cache/`
150
+
151
+ Clear cache: `demandify cache clear`
152
+
153
+ ## CLI Commands
154
+
155
+ ```bash
156
+ # Start web server (default)
157
+ demandify
158
+
159
+ # Run headless calibration
160
+ demandify run "west,south,east,north"
161
+
162
+ # Check system requirements
163
+ demandify doctor
164
+
165
+ # Clear cache
166
+ demandify cache clear
167
+
168
+ # Show version
169
+ demandify --version
170
+ ```
171
+
172
+ ## Output Files
173
+
174
+ Each run creates a folder with:
175
+
176
+ - **`demand.csv`** - Travel demand with exact schema:
177
+ - `ID`, `origin link id`, `destination link id`, `departure timestep`
178
+ - **`trips.xml`** - SUMO trips file
179
+ - **`routes.rou.xml`** - Routed SUMO routes
180
+ - **`network.net.xml`** - SUMO network
181
+ - **`scenario.sumocfg`** - SUMO configuration (ready to run)
182
+ - **`observed_edges.csv`** - Observed traffic speeds
183
+ - **`run_meta.json`** - Complete run metadata
184
+ - **`report.html`** - Calibration report with visualizations
185
+
186
+ Run the scenario:
187
+ ```bash
188
+ cd demandify_runs/run_<timestamp>
189
+ sumo-gui -c scenario.sumocfg
190
+ ```
191
+
192
+ ## Configuration
193
+
194
+ ### API Keys
195
+
196
+ Three ways to provide your TomTom API key:
197
+
198
+ 1. **Web UI**: Paste in the form (saved to `~/.demandify/config.json`)
199
+ 2. **Environment variable**: `export TOMTOM_API_KEY=your_key`
200
+ 3. **`.env` file**: Copy `.env.example` to `.env` and add your key
201
+
202
+ ## Development
203
+
204
+ ```bash
205
+ # Install with dev dependencies
206
+ pip install -e ".[dev]"
207
+
208
+ # Run tests
209
+ pytest
210
+
211
+ # Format code
212
+ black demandify/
213
+
214
+ # Lint
215
+ ruff check demandify/
216
+ ```
217
+
218
+ ## License
219
+
220
+ MIT
221
+
222
+ ## Acknowledgments
223
+
224
+ - **SUMO**: [Eclipse SUMO](https://eclipse.dev/sumo/)
225
+ - **TomTom**: [Traffic Flow API](https://developer.tomtom.com/traffic-api)
226
+ - **OpenStreetMap**: [© OpenStreetMap contributors](https://www.openstreetmap.org/copyright)
@@ -0,0 +1,5 @@
1
+ """
2
+ demandify - Calibrate SUMO traffic simulations against real-world congestion data.
3
+ """
4
+
5
+ __version__ = "0.0.1"
@@ -0,0 +1,14 @@
1
+ """
2
+ CLI entrypoint for demandify.
3
+ """
4
+ import sys
5
+ from demandify.cli import cli
6
+
7
+
8
+ def main():
9
+ """Main entry point."""
10
+ cli()
11
+
12
+
13
+ if __name__ == "__main__":
14
+ main()
@@ -0,0 +1,49 @@
1
+ """
2
+ FastAPI application setup.
3
+ """
4
+ from fastapi import FastAPI
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.templating import Jinja2Templates
7
+ from pathlib import Path
8
+
9
+ from demandify import __version__
10
+ from demandify.web import routes
11
+ from demandify.utils import logging # Setup logging
12
+
13
+
14
+ # Get base directory
15
+ BASE_DIR = Path(__file__).parent
16
+
17
+ # Create FastAPI app
18
+ app = FastAPI(
19
+ title="demandify",
20
+ description="Calibrate SUMO traffic simulations against real-world congestion data",
21
+ version=__version__
22
+ )
23
+
24
+ # Mount static files
25
+ static_dir = BASE_DIR / "static"
26
+ static_dir.mkdir(exist_ok=True)
27
+ app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
28
+
29
+ # Setup templates
30
+ templates_dir = BASE_DIR / "templates"
31
+ templates_dir.mkdir(exist_ok=True)
32
+ templates = Jinja2Templates(directory=str(templates_dir))
33
+
34
+ # Include routes
35
+ app.include_router(routes.router)
36
+
37
+
38
+ @app.on_event("startup")
39
+ async def startup_event():
40
+ """Initialize on startup."""
41
+ from demandify.config import get_config
42
+ config = get_config()
43
+ print(f"Cache directory: {config.cache_dir}")
44
+
45
+
46
+ @app.get("/health")
47
+ async def health_check():
48
+ """Health check endpoint."""
49
+ return {"status": "healthy", "version": __version__}
File without changes
@@ -0,0 +1,71 @@
1
+ """
2
+ Cache key generation for content-addressed caching.
3
+ """
4
+ import hashlib
5
+ import json
6
+ from typing import Any, Dict
7
+
8
+
9
+ def generate_cache_key(data: Dict[str, Any]) -> str:
10
+ """
11
+ Generate a deterministic cache key from data.
12
+
13
+ Args:
14
+ data: Dictionary of parameters
15
+
16
+ Returns:
17
+ Hex string hash
18
+ """
19
+ # Sort keys for deterministic serialization
20
+ serialized = json.dumps(data, sort_keys=True, separators=(',', ':'))
21
+
22
+ # SHA256 hash
23
+ hash_obj = hashlib.sha256(serialized.encode('utf-8'))
24
+
25
+ return hash_obj.hexdigest()
26
+
27
+
28
+ def bbox_key(west: float, south: float, east: float, north: float) -> str:
29
+ """Generate cache key for a bounding box."""
30
+ return generate_cache_key({
31
+ 'type': 'bbox',
32
+ 'west': round(west, 6),
33
+ 'south': round(south, 6),
34
+ 'east': round(east, 6),
35
+ 'north': round(north, 6)
36
+ })
37
+
38
+
39
+ def osm_key(bbox_key: str) -> str:
40
+ """Generate cache key for OSM data."""
41
+ return generate_cache_key({
42
+ 'type': 'osm',
43
+ 'bbox_key': bbox_key
44
+ })
45
+
46
+
47
+ def network_key(osm_key: str, car_only: bool, seed: int) -> str:
48
+ """Generate cache key for SUMO network."""
49
+ return generate_cache_key({
50
+ 'type': 'network',
51
+ 'osm_key': osm_key,
52
+ 'car_only': car_only,
53
+ 'seed': seed
54
+ })
55
+
56
+
57
+ def traffic_key(bbox_key: str, provider: str, timestamp_bucket: str) -> str:
58
+ """
59
+ Generate cache key for traffic data.
60
+
61
+ Args:
62
+ bbox_key: Bbox cache key
63
+ provider: Provider name
64
+ timestamp_bucket: Timestamp rounded to hour (e.g., "2024-01-28T18:00")
65
+ """
66
+ return generate_cache_key({
67
+ 'type': 'traffic',
68
+ 'bbox_key': bbox_key,
69
+ 'provider': provider,
70
+ 'timestamp_bucket': timestamp_bucket
71
+ })