Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2006-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file ODMatrix.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Yun-Pang Floetteroed
19 : /// @author Mirko Barthauer
20 : /// @date 05 Apr. 2006
21 : ///
22 : // An O/D (origin/destination) matrix
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <iostream>
27 : #include <algorithm>
28 : #include <list>
29 : #include <iterator>
30 : #include <utils/options/OptionsCont.h>
31 : #include <utils/common/FileHelpers.h>
32 : #include <utils/common/StdDefs.h>
33 : #include <utils/common/MsgHandler.h>
34 : #include <utils/common/ToString.h>
35 : #include <utils/common/RandHelper.h>
36 : #include <utils/common/StringUtils.h>
37 : #include <utils/common/StringUtils.h>
38 : #include <utils/common/StringTokenizer.h>
39 : #include <utils/common/SUMOTime.h>
40 : #include <utils/iodevices/OutputDevice.h>
41 : #include <utils/importio/LineReader.h>
42 : #include <utils/xml/SUMOSAXHandler.h>
43 : #include <utils/xml/XMLSubSys.h>
44 : #include <router/RORoute.h>
45 : #include "ODAmitranHandler.h"
46 : #include "ODMatrix.h"
47 :
48 :
49 : // ===========================================================================
50 : // method definitions
51 : // ===========================================================================
52 247 : ODMatrix::ODMatrix(const ODDistrictCont& dc, double scale) :
53 247 : myDistricts(dc),
54 247 : myNumLoaded(0),
55 247 : myNumWritten(0),
56 247 : myNumDiscarded(0),
57 247 : myBegin(-1),
58 247 : myEnd(-1),
59 247 : myScale(scale)
60 247 : {}
61 :
62 :
63 247 : ODMatrix::~ODMatrix() {
64 9214 : for (ODCell* const cell : myContainer) {
65 8967 : delete cell;
66 : }
67 : myContainer.clear();
68 494 : }
69 :
70 :
71 : bool
72 820 : ODMatrix::add(double vehicleNumber, const std::pair<SUMOTime, SUMOTime>& beginEnd,
73 : const std::string& origin, const std::string& destination,
74 : const std::string& vehicleType, const bool originIsEdge, const bool destinationIsEdge,
75 : bool noScaling) {
76 820 : if (vehicleNumber == 0) {
77 : return false;
78 : }
79 820 : myNumLoaded += vehicleNumber;
80 829 : if (!originIsEdge && !destinationIsEdge && myDistricts.get(origin) == nullptr && myDistricts.get(destination) == nullptr) {
81 15 : WRITE_WARNINGF(TL("Missing origin '%' and destination '%' (% vehicles)."), origin, destination, toString(vehicleNumber));
82 5 : myNumDiscarded += vehicleNumber;
83 : myMissingDistricts.insert(origin);
84 : myMissingDistricts.insert(destination);
85 5 : return false;
86 815 : } else if (!originIsEdge && myDistricts.get(origin) == 0) {
87 27 : WRITE_ERRORF(TL("Missing origin '%' (% vehicles)."), origin, toString(vehicleNumber));
88 9 : myNumDiscarded += vehicleNumber;
89 : myMissingDistricts.insert(origin);
90 9 : return false;
91 806 : } else if (!destinationIsEdge && myDistricts.get(destination) == 0) {
92 27 : WRITE_ERRORF(TL("Missing destination '%' (% vehicles)."), destination, toString(vehicleNumber));
93 9 : myNumDiscarded += vehicleNumber;
94 : myMissingDistricts.insert(destination);
95 9 : return false;
96 : }
97 1581 : if (!originIsEdge && myDistricts.get(origin)->sourceNumber() == 0) {
98 36 : WRITE_ERRORF(TL("District '%' has no source."), origin);
99 12 : myNumDiscarded += vehicleNumber;
100 12 : return false;
101 1557 : } else if (!destinationIsEdge && myDistricts.get(destination)->sinkNumber() == 0) {
102 36 : WRITE_ERRORF(TL("District '%' has no sink."), destination);
103 12 : myNumDiscarded += vehicleNumber;
104 12 : return false;
105 : }
106 773 : ODCell* cell = new ODCell();
107 773 : cell->begin = beginEnd.first;
108 773 : cell->end = beginEnd.second;
109 773 : cell->origin = origin;
110 773 : cell->destination = destination;
111 773 : cell->vehicleType = vehicleType;
112 773 : cell->vehicleNumber = vehicleNumber * (noScaling ? 1 : myScale);
113 773 : cell->originIsEdge = originIsEdge;
114 773 : cell->destinationIsEdge = destinationIsEdge;
115 773 : myContainer.push_back(cell);
116 773 : if (myBegin == -1 || cell->begin < myBegin) {
117 208 : myBegin = cell->begin;
118 : }
119 773 : if (cell->end > myEnd) {
120 208 : myEnd = cell->end;
121 : }
122 : return true;
123 : }
124 :
125 :
126 : bool
127 1510 : ODMatrix::add(const SUMOVehicleParameter& veh, bool originIsEdge, bool destinationIsEdge) {
128 : const std::string fromTaz = veh.fromTaz;
129 : const std::string toTaz = veh.toTaz;
130 : if (myMissingDistricts.count(fromTaz) > 0 || myMissingDistricts.count(toTaz) > 0) {
131 0 : myNumLoaded += 1.;
132 0 : myNumDiscarded += 1.;
133 0 : return false;
134 : }
135 : // we start looking from the end because there is a high probability that the input is sorted by time
136 3020 : std::vector<ODCell*>& odList = myShortCut[std::make_pair(fromTaz, toTaz)];
137 1510 : ODCell* cell = nullptr;
138 1510 : for (std::vector<ODCell*>::const_reverse_iterator c = odList.rbegin(); c != odList.rend(); ++c) {
139 1474 : if ((*c)->begin <= veh.depart && (*c)->end > veh.depart && (*c)->vehicleType == veh.vtypeid) {
140 1474 : cell = *c;
141 1474 : break;
142 : }
143 : }
144 1510 : if (cell == nullptr) {
145 36 : const SUMOTime interval = string2time(OptionsCont::getOptions().getString("aggregation-interval"));
146 36 : const int intervalIdx = (int)(veh.depart / interval);
147 : // single vehicles are already scaled
148 36 : if (add(1., std::make_pair(intervalIdx * interval, (intervalIdx + 1) * interval),
149 36 : fromTaz, toTaz, veh.vtypeid, originIsEdge, destinationIsEdge, true)) {
150 35 : cell = myContainer.back();
151 35 : odList.push_back(cell);
152 : } else {
153 : return false;
154 : }
155 : } else {
156 1474 : myNumLoaded += 1.;
157 1474 : cell->vehicleNumber += 1.;
158 : }
159 1509 : cell->departures[veh.depart].push_back(veh);
160 : return true;
161 : }
162 :
163 :
164 : double
165 6826 : ODMatrix::computeDeparts(ODCell* cell,
166 : int& vehName, std::vector<ODVehicle>& into,
167 : const bool uniform, const bool differSourceSink,
168 : const std::string& prefix) {
169 6826 : int vehicles2insert = (int) cell->vehicleNumber;
170 : // compute whether the fraction forces an additional vehicle insertion
171 6826 : if (RandHelper::rand() < cell->vehicleNumber - (double)vehicles2insert) {
172 2128 : vehicles2insert++;
173 : }
174 6826 : if (vehicles2insert == 0) {
175 : return cell->vehicleNumber;
176 : }
177 :
178 3098 : const double offset = (double)(cell->end - cell->begin) / (double) vehicles2insert / (double) 2.;
179 18986 : for (int i = 0; i < vehicles2insert; ++i) {
180 : ODVehicle veh;
181 15888 : veh.id = prefix + toString(vehName++);
182 :
183 15888 : if (uniform) {
184 1856 : veh.depart = cell->begin + (SUMOTime)(offset + ((double)(cell->end - cell->begin) * (double) i / (double) vehicles2insert));
185 : } else {
186 14032 : veh.depart = (SUMOTime)RandHelper::rand(cell->begin, cell->end);
187 : }
188 47464 : const bool canDiffer = myDistricts.get(cell->origin)->sourceNumber() > 1 || myDistricts.get(cell->destination)->sinkNumber() > 1;
189 : do {
190 15963 : veh.from = myDistricts.getRandomSourceFromDistrict(cell->origin);
191 15963 : veh.to = myDistricts.getRandomSinkFromDistrict(cell->destination);
192 15963 : } while (canDiffer && differSourceSink && (veh.to == veh.from));
193 15888 : if (!canDiffer && differSourceSink && (veh.to == veh.from)) {
194 30 : WRITE_WARNINGF(TL("Cannot find different source and sink edge for origin '%' and destination '%'."), cell->origin, cell->destination);
195 : }
196 15888 : veh.cell = cell;
197 15888 : into.push_back(veh);
198 15888 : }
199 3098 : return cell->vehicleNumber - vehicles2insert;
200 : }
201 :
202 :
203 : void
204 16886 : ODMatrix::writeDefaultAttrs(OutputDevice& dev, const bool noVtype,
205 : const ODCell* const cell) {
206 16886 : const OptionsCont& oc = OptionsCont::getOptions();
207 16886 : if (!noVtype && cell->vehicleType != "") {
208 1942 : dev.writeAttr(SUMO_ATTR_TYPE, cell->vehicleType);
209 : }
210 16886 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, cell->origin).writeAttr(SUMO_ATTR_TO_TAZ, cell->destination);
211 50658 : if (oc.isSet("departlane") && oc.getString("departlane") != "default") {
212 33772 : dev.writeAttr(SUMO_ATTR_DEPARTLANE, oc.getString("departlane"));
213 : }
214 33772 : if (oc.isSet("departpos")) {
215 2 : dev.writeAttr(SUMO_ATTR_DEPARTPOS, oc.getString("departpos"));
216 : }
217 50658 : if (oc.isSet("departspeed") && oc.getString("departspeed") != "default") {
218 33772 : dev.writeAttr(SUMO_ATTR_DEPARTSPEED, oc.getString("departspeed"));
219 : }
220 33772 : if (oc.isSet("arrivallane")) {
221 2 : dev.writeAttr(SUMO_ATTR_ARRIVALLANE, oc.getString("arrivallane"));
222 : }
223 33772 : if (oc.isSet("arrivalpos")) {
224 2 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, oc.getString("arrivalpos"));
225 : }
226 33772 : if (oc.isSet("arrivalspeed")) {
227 2 : dev.writeAttr(SUMO_ATTR_ARRIVALSPEED, oc.getString("arrivalspeed"));
228 : }
229 16886 : }
230 :
231 :
232 : void
233 86 : ODMatrix::write(SUMOTime begin, const SUMOTime end,
234 : OutputDevice& dev, const bool uniform,
235 : const bool differSourceSink, const bool noVtype,
236 : const std::string& prefix, const bool stepLog,
237 : bool pedestrians, bool persontrips,
238 : const std::string& modes) {
239 86 : if (myContainer.size() == 0) {
240 0 : return;
241 : }
242 : std::map<std::pair<std::string, std::string>, double> fractionLeft;
243 86 : int vehName = 0;
244 86 : sortByBeginTime();
245 : // recheck begin time
246 86 : begin = MAX2(begin, myContainer.front()->begin);
247 : std::vector<ODCell*>::iterator next = myContainer.begin();
248 : std::vector<ODVehicle> vehicles;
249 86 : SUMOTime lastOut = -DELTA_T;
250 :
251 86 : const OptionsCont& oc = OptionsCont::getOptions();
252 88 : std::string personDepartPos = oc.isSet("departpos") ? oc.getString("departpos") : "random";
253 88 : std::string personArrivalPos = oc.isSet("arrivalpos") ? oc.getString("arrivalpos") : "random";
254 171 : SumoXMLAttr fromAttr = oc.getBool("junctions") ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM;
255 171 : SumoXMLAttr toAttr = oc.getBool("junctions") ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO;
256 172 : const std::string vType = oc.isSet("vtype") ? oc.getString("vtype") : "";
257 :
258 : // go through the time steps
259 16061 : for (SUMOTime t = begin; t < end;) {
260 16060 : if (stepLog && t - lastOut >= DELTA_T) {
261 0 : std::cout << "Parsing time " + time2string(t) << '\r';
262 : lastOut = t;
263 : }
264 : // recheck whether a new cell got valid
265 : bool changed = false;
266 22886 : while (next != myContainer.end() && (*next)->begin <= t && (*next)->end > t) {
267 6826 : std::pair<std::string, std::string> odID = std::make_pair((*next)->origin, (*next)->destination);
268 : // check whether the current cell must be extended by the last fraction
269 6826 : if (fractionLeft.find(odID) != fractionLeft.end()) {
270 6210 : (*next)->vehicleNumber += fractionLeft[odID];
271 6210 : fractionLeft[odID] = 0;
272 : }
273 : // get the new departures (into tmp)
274 6826 : const int oldSize = (int)vehicles.size();
275 6826 : const double fraction = computeDeparts(*next, vehName, vehicles, uniform, differSourceSink, prefix);
276 6826 : if (oldSize != (int)vehicles.size()) {
277 : changed = true;
278 : }
279 6826 : if (fraction != 0) {
280 6472 : fractionLeft[odID] = fraction;
281 : }
282 : ++next;
283 : }
284 16060 : if (changed) {
285 192 : sort(vehicles.begin(), vehicles.end(), descending_departure_comperator());
286 : }
287 :
288 31875 : for (std::vector<ODVehicle>::reverse_iterator i = vehicles.rbegin(); i != vehicles.rend() && (*i).depart == t; ++i) {
289 15815 : if (t >= begin) {
290 15793 : myNumWritten++;
291 15793 : if (pedestrians) {
292 400 : dev.openTag(SUMO_TAG_PERSON).writeAttr(SUMO_ATTR_ID, (*i).id).writeAttr(SUMO_ATTR_DEPART, time2string(t));
293 400 : dev.writeAttr(SUMO_ATTR_DEPARTPOS, personDepartPos);
294 400 : if (!noVtype && vType.size() > 0) {
295 0 : dev.writeAttr(SUMO_ATTR_TYPE, vType);
296 : }
297 400 : dev.openTag(SUMO_TAG_WALK);
298 400 : dev.writeAttr(fromAttr, (*i).from);
299 400 : dev.writeAttr(toAttr, (*i).to);
300 400 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, (*i).cell->origin).writeAttr(SUMO_ATTR_TO_TAZ, (*i).cell->destination);
301 400 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, personArrivalPos);
302 400 : dev.closeTag();
303 800 : dev.closeTag();
304 15393 : } else if (persontrips) {
305 600 : dev.openTag(SUMO_TAG_PERSON).writeAttr(SUMO_ATTR_ID, (*i).id).writeAttr(SUMO_ATTR_DEPART, time2string(t));
306 600 : dev.writeAttr(SUMO_ATTR_DEPARTPOS, personDepartPos);
307 600 : dev.openTag(SUMO_TAG_PERSONTRIP);
308 600 : dev.writeAttr(fromAttr, (*i).from);
309 600 : dev.writeAttr(toAttr, (*i).to);
310 600 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, (*i).cell->origin).writeAttr(SUMO_ATTR_TO_TAZ, (*i).cell->destination);
311 600 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, personArrivalPos);
312 600 : if (modes != "") {
313 100 : dev.writeAttr(SUMO_ATTR_MODES, modes);
314 : }
315 600 : dev.closeTag();
316 1200 : dev.closeTag();
317 : } else {
318 14793 : dev.openTag(SUMO_TAG_TRIP).writeAttr(SUMO_ATTR_ID, (*i).id).writeAttr(SUMO_ATTR_DEPART, time2string(t));
319 14793 : dev.writeAttr(fromAttr, (*i).from);
320 14793 : dev.writeAttr(toAttr, (*i).to);
321 14793 : writeDefaultAttrs(dev, noVtype, i->cell);
322 29586 : dev.closeTag();
323 : }
324 : }
325 : }
326 31875 : while (vehicles.size() != 0 && vehicles.back().depart == t) {
327 : vehicles.pop_back();
328 : }
329 16060 : if (!vehicles.empty()) {
330 15810 : t = vehicles.back().depart;
331 : }
332 16060 : if (next != myContainer.end() && (t > (*next)->begin || vehicles.empty())) {
333 : t = (*next)->begin;
334 : }
335 32035 : if (next == myContainer.end() && vehicles.empty()) {
336 : break;
337 : }
338 : }
339 86 : }
340 :
341 :
342 : void
343 33 : ODMatrix::writeFlows(const SUMOTime begin, const SUMOTime end,
344 : OutputDevice& dev, bool noVtype,
345 : const std::string& prefix,
346 : bool asProbability,
347 : bool asPoisson,
348 : bool pedestrians, bool persontrips,
349 : const std::string& modes) {
350 33 : if (myContainer.size() == 0) {
351 : return;
352 : }
353 : int flowName = 0;
354 33 : sortByBeginTime();
355 : // recheck begin time
356 209 : for (std::vector<ODCell*>::const_iterator i = myContainer.begin(); i != myContainer.end(); ++i) {
357 176 : const ODCell* const c = *i;
358 176 : if (c->end > begin && c->begin < end) {
359 176 : const double probability = float(c->vehicleNumber) / STEPS2TIME(c->end - c->begin);
360 176 : if (probability <= 0) {
361 72 : continue;
362 : }
363 : //Person flows
364 104 : if (pedestrians) {
365 4 : dev.openTag(SUMO_TAG_PERSONFLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
366 4 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin)).writeAttr(SUMO_ATTR_END, time2string(c->end));
367 4 : if (asPoisson) {
368 0 : dev.writeAttr(SUMO_ATTR_PERIOD, "exp(" + StringUtils::adjustDecimalValue(probability, 10) + ")");
369 4 : } else if (!asProbability) {
370 4 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
371 : } else {
372 0 : if (probability > 1) {
373 0 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
374 0 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
375 : } else {
376 0 : dev.setPrecision(6);
377 0 : dev.writeAttr(SUMO_ATTR_PROB, probability);
378 0 : dev.setPrecision();
379 : }
380 : }
381 4 : dev.openTag(SUMO_TAG_WALK);
382 4 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, c->origin).writeAttr(SUMO_ATTR_TO_TAZ, c->destination);
383 4 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, "random");
384 4 : dev.closeTag();
385 8 : dev.closeTag();
386 100 : } else if (persontrips) {
387 14 : dev.openTag(SUMO_TAG_PERSONFLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
388 14 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin)).writeAttr(SUMO_ATTR_END, time2string(c->end));
389 14 : if (asPoisson) {
390 0 : dev.writeAttr(SUMO_ATTR_PERIOD, "exp(" + StringUtils::adjustDecimalValue(probability, 10) + ")");
391 14 : } else if (!asProbability) {
392 12 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
393 : } else {
394 2 : if (probability > 1) {
395 2 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
396 1 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
397 : } else {
398 1 : dev.setPrecision(6);
399 1 : dev.writeAttr(SUMO_ATTR_PROB, probability);
400 1 : dev.setPrecision();
401 : }
402 : }
403 14 : dev.openTag(SUMO_TAG_PERSONTRIP);
404 14 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, c->origin).writeAttr(SUMO_ATTR_TO_TAZ, c->destination);
405 14 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, "random");
406 14 : if (modes != "") {
407 0 : dev.writeAttr(SUMO_ATTR_MODES, modes);
408 : }
409 14 : dev.closeTag();
410 28 : dev.closeTag();
411 : } else {
412 : // Normal flow output
413 86 : dev.openTag(SUMO_TAG_FLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
414 86 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin));
415 86 : dev.writeAttr(SUMO_ATTR_END, time2string(c->end));
416 86 : if (asPoisson) {
417 3 : dev.writeAttr(SUMO_ATTR_PERIOD, "exp(" + StringUtils::adjustDecimalValue(probability, 10) + ")");
418 85 : } else if (!asProbability) {
419 10 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
420 : } else {
421 75 : if (probability > 1) {
422 2 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
423 1 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
424 : } else {
425 74 : dev.setPrecision(6);
426 74 : dev.writeAttr(SUMO_ATTR_PROB, probability);
427 74 : dev.setPrecision();
428 : }
429 : }
430 86 : writeDefaultAttrs(dev, noVtype, *i);
431 172 : dev.closeTag();
432 : }
433 : }
434 : }
435 : }
436 :
437 :
438 : std::string
439 931 : ODMatrix::getNextNonCommentLine(LineReader& lr) {
440 2195 : while (lr.good() && lr.hasMore()) {
441 2194 : const std::string line = lr.readLine();
442 2194 : if (line[0] != '*') {
443 1860 : return StringUtils::prune(line);
444 : }
445 : }
446 2 : throw ProcessError(TLF("End of file while reading %.", lr.getFileName()));
447 : }
448 :
449 :
450 : SUMOTime
451 350 : ODMatrix::parseSingleTime(const std::string& time) {
452 350 : if (time.find('.') == std::string::npos) {
453 16 : throw NumberFormatException("no separator");
454 : }
455 342 : const std::string hours = time.substr(0, time.find('.'));
456 342 : const std::string minutes = time.substr(time.find('.') + 1);
457 684 : return TIME2STEPS(StringUtils::toInt(hours) * 3600 + StringUtils::toInt(minutes) * 60);
458 : }
459 :
460 :
461 : std::pair<SUMOTime, SUMOTime>
462 179 : ODMatrix::readTime(LineReader& lr) {
463 179 : std::string line = getNextNonCommentLine(lr);
464 : try {
465 358 : StringTokenizer st(line, StringTokenizer::WHITECHARS);
466 179 : const SUMOTime begin = parseSingleTime(st.next());
467 175 : const SUMOTime end = parseSingleTime(st.next());
468 167 : if (begin >= end) {
469 16 : throw ProcessError("Matrix begin time " + time2string(begin) + " is larger than end time " + time2string(end) + ".");
470 : }
471 163 : return std::make_pair(begin, end);
472 195 : } catch (OutOfBoundsException&) {
473 12 : throw ProcessError(TLF("Broken period definition '%'.", line));
474 12 : } catch (NumberFormatException& e) {
475 24 : throw ProcessError("Broken period definition '" + line + "' (" + e.what() + ").");
476 8 : }
477 : }
478 :
479 :
480 : double
481 163 : ODMatrix::readFactor(LineReader& lr, double scale) {
482 163 : std::string line = getNextNonCommentLine(lr);
483 : double factor = -1;
484 : try {
485 163 : factor = StringUtils::toDouble(line) * scale;
486 6 : } catch (NumberFormatException&) {
487 18 : throw ProcessError(TLF("Broken factor: '%'.", line));
488 6 : }
489 157 : return factor;
490 : }
491 :
492 : void
493 66 : ODMatrix::readV(LineReader& lr, double scale,
494 : std::string vehType, bool matrixHasVehType) {
495 198 : PROGRESS_BEGIN_MESSAGE("Reading matrix '" + lr.getFileName() + "' stored as VMR");
496 : // parse first defs
497 : std::string line;
498 66 : if (matrixHasVehType) {
499 37 : line = getNextNonCommentLine(lr);
500 37 : if (vehType == "") {
501 74 : vehType = StringUtils::prune(line);
502 : }
503 : }
504 :
505 66 : const std::pair<SUMOTime, SUMOTime> beginEnd = readTime(lr);
506 58 : const double factor = readFactor(lr, scale);
507 :
508 : // districts
509 56 : line = getNextNonCommentLine(lr);
510 78 : const int numDistricts = StringUtils::toInt(StringUtils::prune(line));
511 : // parse district names (normally ints)
512 : std::vector<std::string> names;
513 134 : while ((int)names.size() != numDistricts && lr.hasMore()) {
514 78 : line = getNextNonCommentLine(lr);
515 156 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
516 392 : while (st2.hasNext()) {
517 628 : names.push_back(st2.next());
518 : }
519 78 : }
520 56 : if (!lr.hasMore()) {
521 6 : throw ProcessError(TLF("Missing line with % district names.", toString(numDistricts)));
522 : }
523 :
524 : // parse the cells
525 204 : for (std::vector<std::string>::iterator si = names.begin(); si != names.end(); ++si) {
526 : std::vector<std::string>::iterator di = names.begin();
527 : do {
528 : try {
529 369 : line = getNextNonCommentLine(lr);
530 1 : } catch (ProcessError&) {
531 3 : throw ProcessError(TLF("Missing line for district %.", (*si)));
532 1 : }
533 184 : if (line.length() == 0) {
534 1 : continue;
535 : }
536 : try {
537 366 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
538 951 : while (st2.hasNext()) {
539 : assert(di != names.end());
540 776 : double vehNumber = StringUtils::toDouble(st2.next()) * factor;
541 768 : if (vehNumber != 0) {
542 556 : add(vehNumber, beginEnd, *si, *di, vehType);
543 : }
544 768 : if (di == names.end()) {
545 0 : throw ProcessError(TL("More entries than districts found."));
546 : }
547 : ++di;
548 : }
549 191 : } catch (NumberFormatException&) {
550 24 : throw ProcessError(TLF("Not numeric vehicle number in line '%'.", line));
551 8 : }
552 175 : if (!lr.hasMore()) {
553 : break;
554 : }
555 137 : } while (di != names.end());
556 : }
557 44 : PROGRESS_DONE_MESSAGE();
558 100 : }
559 :
560 :
561 : void
562 113 : ODMatrix::readO(LineReader& lr, double scale,
563 : std::string vehType, bool matrixHasVehType) {
564 339 : PROGRESS_BEGIN_MESSAGE("Reading matrix '" + lr.getFileName() + "' stored as OR");
565 : // parse first defs
566 : std::string line;
567 113 : if (matrixHasVehType) {
568 11 : line = getNextNonCommentLine(lr);
569 11 : int type = StringUtils::toInt(StringUtils::prune(line));
570 11 : if (vehType == "") {
571 22 : vehType = toString(type);
572 : }
573 : }
574 :
575 113 : const std::pair<SUMOTime, SUMOTime> beginEnd = readTime(lr);
576 105 : const double factor = readFactor(lr, scale);
577 :
578 : // parse the cells
579 321 : while (lr.hasMore()) {
580 444 : line = getNextNonCommentLine(lr);
581 222 : if (line.length() == 0) {
582 26 : continue;
583 : }
584 406 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
585 196 : if (st2.size() == 0) {
586 : continue;
587 : }
588 : try {
589 196 : std::string sourceD = st2.next();
590 196 : std::string destD = st2.next();
591 198 : double vehNumber = StringUtils::toDouble(st2.next()) * factor;
592 194 : if (vehNumber != 0) {
593 194 : add(vehNumber, beginEnd, sourceD, destD, vehType);
594 : }
595 2 : } catch (OutOfBoundsException&) {
596 0 : throw ProcessError(TLF("Missing at least one information in line '%'.", line));
597 2 : } catch (NumberFormatException&) {
598 6 : throw ProcessError(TLF("Not numeric vehicle number in line '%'.", line));
599 2 : }
600 196 : }
601 99 : PROGRESS_DONE_MESSAGE();
602 99 : }
603 :
604 :
605 :
606 : double
607 468 : ODMatrix::getNumLoaded() const {
608 468 : return myNumLoaded;
609 : }
610 :
611 :
612 : double
613 108 : ODMatrix::getNumWritten() const {
614 108 : return myNumWritten;
615 : }
616 :
617 :
618 : double
619 269 : ODMatrix::getNumDiscarded() const {
620 269 : return myNumDiscarded;
621 : }
622 :
623 :
624 : void
625 362 : ODMatrix::applyCurve(const Distribution_Points& ps, ODCell* cell, std::vector<ODCell*>& newCells) {
626 : const std::vector<double>& times = ps.getVals();
627 8918 : for (int i = 0; i < (int)times.size() - 1; ++i) {
628 8556 : ODCell* ncell = new ODCell();
629 8556 : ncell->begin = TIME2STEPS(times[i]);
630 8556 : ncell->end = TIME2STEPS(times[i + 1]);
631 8556 : ncell->origin = cell->origin;
632 8556 : ncell->destination = cell->destination;
633 8556 : ncell->vehicleType = cell->vehicleType;
634 8556 : ncell->vehicleNumber = cell->vehicleNumber * ps.getProbs()[i] / ps.getOverallProb();
635 8556 : newCells.push_back(ncell);
636 : }
637 362 : }
638 :
639 :
640 : void
641 19 : ODMatrix::applyCurve(const Distribution_Points& ps) {
642 19 : std::vector<ODCell*> oldCells = myContainer;
643 : myContainer.clear();
644 381 : for (std::vector<ODCell*>::iterator i = oldCells.begin(); i != oldCells.end(); ++i) {
645 : std::vector<ODCell*> newCells;
646 362 : applyCurve(ps, *i, newCells);
647 : copy(newCells.begin(), newCells.end(), back_inserter(myContainer));
648 362 : delete *i;
649 362 : }
650 19 : }
651 :
652 :
653 : void
654 247 : ODMatrix::loadMatrix(OptionsCont& oc) {
655 494 : std::vector<std::string> files = oc.getStringVector("od-matrix-files");
656 390 : for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); ++i) {
657 181 : LineReader lr(*i);
658 181 : if (!lr.good()) {
659 6 : throw ProcessError(TLF("Could not open '%'.", (*i)));
660 : }
661 179 : std::string type = lr.readLine();
662 : // get the type only
663 179 : if (type.find(';') != std::string::npos) {
664 252 : type = type.substr(0, type.find(';'));
665 : }
666 : // parse type-dependant
667 179 : if (type.length() > 1 && type[1] == 'V') {
668 : // process ptv's 'V'-matrices
669 66 : if (type.find('N') != std::string::npos) {
670 0 : throw ProcessError(TLF("'%' does not contain the needed information about the time described.", *i));
671 : }
672 110 : readV(lr, 1, oc.getString("vtype"), type.find('M') != std::string::npos);
673 113 : } else if (type.length() > 1 && type[1] == 'O') {
674 : // process ptv's 'O'-matrices
675 113 : if (type.find('N') != std::string::npos) {
676 0 : throw ProcessError(TLF("'%' does not contain the needed information about the time described.", *i));
677 : }
678 248 : readO(lr, 1, oc.getString("vtype"), type.find('M') != std::string::npos);
679 : } else {
680 0 : throw ProcessError("'" + *i + "' uses an unknown matrix type '" + type + "'.");
681 : }
682 181 : }
683 418 : std::vector<std::string> amitranFiles = oc.getStringVector("od-amitran-files");
684 236 : for (std::vector<std::string>::iterator i = amitranFiles.begin(); i != amitranFiles.end(); ++i) {
685 54 : if (!FileHelpers::isReadable(*i)) {
686 0 : throw ProcessError(TLF("Could not access matrix file '%' to load.", *i));
687 : }
688 81 : PROGRESS_BEGIN_MESSAGE("Loading matrix in Amitran format from '" + *i + "'");
689 27 : ODAmitranHandler handler(*this, *i);
690 27 : if (!XMLSubSys::runParser(handler, *i)) {
691 4 : PROGRESS_FAILED_MESSAGE();
692 : } else {
693 23 : PROGRESS_DONE_MESSAGE();
694 : }
695 27 : }
696 209 : myVType = oc.getString("vtype");
697 425 : for (std::string file : oc.getStringVector("tazrelation-files")) {
698 14 : if (!FileHelpers::isReadable(file)) {
699 0 : throw ProcessError(TLF("Could not access matrix file '%' to load.", file));
700 : }
701 21 : PROGRESS_BEGIN_MESSAGE("Loading matrix in tazRelation format from '" + file + "'");
702 :
703 : std::vector<SAXWeightsHandler::ToRetrieveDefinition*> retrieverDefs;
704 7 : retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition(oc.getString("tazrelation-attribute"), true, *this));
705 7 : SAXWeightsHandler handler(retrieverDefs, "");
706 7 : if (!XMLSubSys::runParser(handler, file)) {
707 0 : PROGRESS_FAILED_MESSAGE();
708 : } else {
709 7 : PROGRESS_DONE_MESSAGE();
710 : }
711 7 : }
712 247 : }
713 :
714 : void
715 7 : ODMatrix::addTazRelWeight(const std::string intervalID, const std::string& from, const std::string& to,
716 : double val, double beg, double end) {
717 8 : add(val, std::make_pair(TIME2STEPS(beg), TIME2STEPS(end)), from, to, myVType == "" ? intervalID : myVType);
718 7 : }
719 :
720 :
721 : void
722 86 : ODMatrix::loadRoutes(OptionsCont& oc, SUMOSAXHandler& handler) {
723 172 : std::vector<std::string> routeFiles = oc.getStringVector("route-files");
724 119 : for (std::vector<std::string>::iterator i = routeFiles.begin(); i != routeFiles.end(); ++i) {
725 66 : if (!FileHelpers::isReadable(*i)) {
726 0 : throw ProcessError(TLF("Could not access route file '%' to load.", *i));
727 : }
728 99 : PROGRESS_BEGIN_MESSAGE("Loading routes and trips from '" + *i + "'");
729 33 : if (!XMLSubSys::runParser(handler, *i)) {
730 1 : PROGRESS_FAILED_MESSAGE();
731 : } else {
732 32 : PROGRESS_DONE_MESSAGE();
733 : }
734 : }
735 86 : }
736 :
737 :
738 : Distribution_Points
739 19 : ODMatrix::parseTimeLine(const std::vector<std::string>& def, bool timelineDayInHours) {
740 19 : Distribution_Points result("N/A");
741 19 : if (timelineDayInHours) {
742 13 : if (def.size() != 24) {
743 0 : throw ProcessError(TLF("Assuming 24 entries for a day timeline, but got %.", toString(def.size())));
744 : }
745 325 : for (int chour = 0; chour < 24; ++chour) {
746 312 : result.add(chour * 3600., StringUtils::toDouble(def[chour]));
747 : }
748 13 : result.add(24 * 3600., 0.); // dummy value to finish the last interval
749 : } else {
750 24 : for (int i = 0; i < (int)def.size(); i++) {
751 54 : StringTokenizer st2(def[i], ":");
752 18 : if (st2.size() != 2) {
753 0 : throw ProcessError(TLF("Broken time line definition: missing a value in '%'.", def[i]));
754 : }
755 18 : const double time = StringUtils::toDouble(st2.next());
756 18 : result.add(time, StringUtils::toDouble(st2.next()));
757 18 : }
758 : }
759 19 : return result;
760 0 : }
761 :
762 :
763 : void
764 193 : ODMatrix::sortByBeginTime() {
765 193 : std::sort(myContainer.begin(), myContainer.end(), cell_by_begin_comparator());
766 193 : }
767 :
768 :
769 : /****************************************************************************/
|