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 246 : ODMatrix::ODMatrix(const ODDistrictCont& dc, double scale) :
53 246 : myDistricts(dc),
54 246 : myNumLoaded(0),
55 246 : myNumWritten(0),
56 246 : myNumDiscarded(0),
57 246 : myBegin(-1),
58 246 : myEnd(-1),
59 246 : myScale(scale)
60 246 : {}
61 :
62 :
63 246 : ODMatrix::~ODMatrix() {
64 9212 : for (ODCell* const cell : myContainer) {
65 8966 : delete cell;
66 : }
67 : myContainer.clear();
68 492 : }
69 :
70 :
71 : bool
72 819 : 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 819 : if (vehicleNumber == 0) {
77 : return false;
78 : }
79 819 : myNumLoaded += vehicleNumber;
80 828 : 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 814 : } 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 805 : } 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 1579 : 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 1555 : } 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 772 : ODCell* cell = new ODCell();
107 772 : cell->begin = beginEnd.first;
108 772 : cell->end = beginEnd.second;
109 772 : cell->origin = origin;
110 772 : cell->destination = destination;
111 772 : cell->vehicleType = vehicleType;
112 772 : cell->vehicleNumber = vehicleNumber * (noScaling ? 1 : myScale);
113 772 : cell->originIsEdge = originIsEdge;
114 772 : cell->destinationIsEdge = destinationIsEdge;
115 772 : myContainer.push_back(cell);
116 772 : if (myBegin == -1 || cell->begin < myBegin) {
117 207 : myBegin = cell->begin;
118 : }
119 772 : if (cell->end > myEnd) {
120 207 : 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 16885 : ODMatrix::writeDefaultAttrs(OutputDevice& dev, const bool noVtype,
205 : const ODCell* const cell) {
206 16885 : const OptionsCont& oc = OptionsCont::getOptions();
207 16885 : if (!noVtype && cell->vehicleType != "") {
208 1942 : dev.writeAttr(SUMO_ATTR_TYPE, cell->vehicleType);
209 : }
210 16885 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, cell->origin).writeAttr(SUMO_ATTR_TO_TAZ, cell->destination);
211 50655 : if (oc.isSet("departlane") && oc.getString("departlane") != "default") {
212 33770 : dev.writeAttr(SUMO_ATTR_DEPARTLANE, oc.getString("departlane"));
213 : }
214 33770 : if (oc.isSet("departpos")) {
215 2 : dev.writeAttr(SUMO_ATTR_DEPARTPOS, oc.getString("departpos"));
216 : }
217 50655 : if (oc.isSet("departspeed") && oc.getString("departspeed") != "default") {
218 33770 : dev.writeAttr(SUMO_ATTR_DEPARTSPEED, oc.getString("departspeed"));
219 : }
220 33770 : if (oc.isSet("arrivallane")) {
221 2 : dev.writeAttr(SUMO_ATTR_ARRIVALLANE, oc.getString("arrivallane"));
222 : }
223 33770 : if (oc.isSet("arrivalpos")) {
224 2 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, oc.getString("arrivalpos"));
225 : }
226 33770 : if (oc.isSet("arrivalspeed")) {
227 2 : dev.writeAttr(SUMO_ATTR_ARRIVALSPEED, oc.getString("arrivalspeed"));
228 : }
229 16885 : }
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 32 : ODMatrix::writeFlows(const SUMOTime begin, const SUMOTime end,
344 : OutputDevice& dev, bool noVtype,
345 : const std::string& prefix,
346 : bool asProbability, bool pedestrians, bool persontrips,
347 : const std::string& modes) {
348 32 : if (myContainer.size() == 0) {
349 : return;
350 : }
351 : int flowName = 0;
352 32 : sortByBeginTime();
353 : // recheck begin time
354 207 : for (std::vector<ODCell*>::const_iterator i = myContainer.begin(); i != myContainer.end(); ++i) {
355 175 : const ODCell* const c = *i;
356 175 : if (c->end > begin && c->begin < end) {
357 175 : const double probability = asProbability ? float(c->vehicleNumber) / STEPS2TIME(c->end - c->begin) : 1;
358 175 : if (probability <= 0) {
359 72 : continue;
360 : }
361 : //Person flows
362 103 : if (pedestrians) {
363 4 : dev.openTag(SUMO_TAG_PERSONFLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
364 4 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin)).writeAttr(SUMO_ATTR_END, time2string(c->end));
365 4 : if (!asProbability) {
366 4 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
367 : } else {
368 0 : if (probability > 1) {
369 0 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
370 0 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
371 : } else {
372 0 : dev.setPrecision(6);
373 0 : dev.writeAttr(SUMO_ATTR_PROB, probability);
374 0 : dev.setPrecision();
375 : }
376 : }
377 4 : dev.openTag(SUMO_TAG_WALK);
378 4 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, c->origin).writeAttr(SUMO_ATTR_TO_TAZ, c->destination);
379 4 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, "random");
380 4 : dev.closeTag();
381 8 : dev.closeTag();
382 99 : } else if (persontrips) {
383 14 : dev.openTag(SUMO_TAG_PERSONFLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
384 14 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin)).writeAttr(SUMO_ATTR_END, time2string(c->end));
385 14 : if (!asProbability) {
386 12 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
387 : } else {
388 2 : if (probability > 1) {
389 2 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
390 1 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
391 : } else {
392 1 : dev.setPrecision(6);
393 1 : dev.writeAttr(SUMO_ATTR_PROB, probability);
394 1 : dev.setPrecision();
395 : }
396 : }
397 14 : dev.openTag(SUMO_TAG_PERSONTRIP);
398 14 : dev.writeAttr(SUMO_ATTR_FROM_TAZ, c->origin).writeAttr(SUMO_ATTR_TO_TAZ, c->destination);
399 14 : dev.writeAttr(SUMO_ATTR_ARRIVALPOS, "random");
400 14 : if (modes != "") {
401 0 : dev.writeAttr(SUMO_ATTR_MODES, modes);
402 : }
403 14 : dev.closeTag();
404 28 : dev.closeTag();
405 : } else {
406 : // Normal flow output
407 85 : dev.openTag(SUMO_TAG_FLOW).writeAttr(SUMO_ATTR_ID, prefix + toString(flowName++));
408 85 : dev.writeAttr(SUMO_ATTR_BEGIN, time2string(c->begin));
409 85 : dev.writeAttr(SUMO_ATTR_END, time2string(c->end));
410 :
411 85 : if (!asProbability) {
412 10 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
413 : } else {
414 75 : if (probability > 1) {
415 2 : WRITE_WARNINGF(TL("Flow density of % vehicles per second, cannot be represented with a simple probability. Falling back to even spacing."), toString(probability));
416 1 : dev.writeAttr(SUMO_ATTR_NUMBER, int(c->vehicleNumber));
417 : } else {
418 74 : dev.setPrecision(6);
419 74 : dev.writeAttr(SUMO_ATTR_PROB, probability);
420 74 : dev.setPrecision();
421 : }
422 : }
423 85 : writeDefaultAttrs(dev, noVtype, *i);
424 170 : dev.closeTag();
425 : }
426 : }
427 : }
428 : }
429 :
430 :
431 : std::string
432 927 : ODMatrix::getNextNonCommentLine(LineReader& lr) {
433 2185 : while (lr.good() && lr.hasMore()) {
434 2184 : const std::string line = lr.readLine();
435 2184 : if (line[0] != '*') {
436 1852 : return StringUtils::prune(line);
437 : }
438 : }
439 2 : throw ProcessError(TLF("End of file while reading %.", lr.getFileName()));
440 : }
441 :
442 :
443 : SUMOTime
444 348 : ODMatrix::parseSingleTime(const std::string& time) {
445 348 : if (time.find('.') == std::string::npos) {
446 16 : throw NumberFormatException("no separator");
447 : }
448 340 : const std::string hours = time.substr(0, time.find('.'));
449 340 : const std::string minutes = time.substr(time.find('.') + 1);
450 680 : return TIME2STEPS(StringUtils::toInt(hours) * 3600 + StringUtils::toInt(minutes) * 60);
451 : }
452 :
453 :
454 : std::pair<SUMOTime, SUMOTime>
455 178 : ODMatrix::readTime(LineReader& lr) {
456 178 : std::string line = getNextNonCommentLine(lr);
457 : try {
458 356 : StringTokenizer st(line, StringTokenizer::WHITECHARS);
459 178 : const SUMOTime begin = parseSingleTime(st.next());
460 174 : const SUMOTime end = parseSingleTime(st.next());
461 166 : if (begin >= end) {
462 16 : throw ProcessError("Matrix begin time " + time2string(begin) + " is larger than end time " + time2string(end) + ".");
463 : }
464 162 : return std::make_pair(begin, end);
465 194 : } catch (OutOfBoundsException&) {
466 12 : throw ProcessError(TLF("Broken period definition '%'.", line));
467 12 : } catch (NumberFormatException& e) {
468 24 : throw ProcessError("Broken period definition '" + line + "' (" + e.what() + ").");
469 8 : }
470 : }
471 :
472 :
473 : double
474 162 : ODMatrix::readFactor(LineReader& lr, double scale) {
475 162 : std::string line = getNextNonCommentLine(lr);
476 : double factor = -1;
477 : try {
478 162 : factor = StringUtils::toDouble(line) * scale;
479 6 : } catch (NumberFormatException&) {
480 18 : throw ProcessError(TLF("Broken factor: '%'.", line));
481 6 : }
482 156 : return factor;
483 : }
484 :
485 : void
486 66 : ODMatrix::readV(LineReader& lr, double scale,
487 : std::string vehType, bool matrixHasVehType) {
488 198 : PROGRESS_BEGIN_MESSAGE("Reading matrix '" + lr.getFileName() + "' stored as VMR");
489 : // parse first defs
490 : std::string line;
491 66 : if (matrixHasVehType) {
492 37 : line = getNextNonCommentLine(lr);
493 37 : if (vehType == "") {
494 74 : vehType = StringUtils::prune(line);
495 : }
496 : }
497 :
498 66 : const std::pair<SUMOTime, SUMOTime> beginEnd = readTime(lr);
499 58 : const double factor = readFactor(lr, scale);
500 :
501 : // districts
502 56 : line = getNextNonCommentLine(lr);
503 78 : const int numDistricts = StringUtils::toInt(StringUtils::prune(line));
504 : // parse district names (normally ints)
505 : std::vector<std::string> names;
506 134 : while ((int)names.size() != numDistricts && lr.hasMore()) {
507 78 : line = getNextNonCommentLine(lr);
508 156 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
509 392 : while (st2.hasNext()) {
510 628 : names.push_back(st2.next());
511 : }
512 78 : }
513 56 : if (!lr.hasMore()) {
514 6 : throw ProcessError(TLF("Missing line with % district names.", toString(numDistricts)));
515 : }
516 :
517 : // parse the cells
518 204 : for (std::vector<std::string>::iterator si = names.begin(); si != names.end(); ++si) {
519 : std::vector<std::string>::iterator di = names.begin();
520 : do {
521 : try {
522 369 : line = getNextNonCommentLine(lr);
523 1 : } catch (ProcessError&) {
524 3 : throw ProcessError(TLF("Missing line for district %.", (*si)));
525 1 : }
526 184 : if (line.length() == 0) {
527 1 : continue;
528 : }
529 : try {
530 366 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
531 951 : while (st2.hasNext()) {
532 : assert(di != names.end());
533 776 : double vehNumber = StringUtils::toDouble(st2.next()) * factor;
534 768 : if (vehNumber != 0) {
535 556 : add(vehNumber, beginEnd, *si, *di, vehType);
536 : }
537 768 : if (di == names.end()) {
538 0 : throw ProcessError(TL("More entries than districts found."));
539 : }
540 : ++di;
541 : }
542 191 : } catch (NumberFormatException&) {
543 24 : throw ProcessError(TLF("Not numeric vehicle number in line '%'.", line));
544 8 : }
545 175 : if (!lr.hasMore()) {
546 : break;
547 : }
548 137 : } while (di != names.end());
549 : }
550 44 : PROGRESS_DONE_MESSAGE();
551 100 : }
552 :
553 :
554 : void
555 112 : ODMatrix::readO(LineReader& lr, double scale,
556 : std::string vehType, bool matrixHasVehType) {
557 336 : PROGRESS_BEGIN_MESSAGE("Reading matrix '" + lr.getFileName() + "' stored as OR");
558 : // parse first defs
559 : std::string line;
560 112 : if (matrixHasVehType) {
561 11 : line = getNextNonCommentLine(lr);
562 11 : int type = StringUtils::toInt(StringUtils::prune(line));
563 11 : if (vehType == "") {
564 22 : vehType = toString(type);
565 : }
566 : }
567 :
568 112 : const std::pair<SUMOTime, SUMOTime> beginEnd = readTime(lr);
569 104 : const double factor = readFactor(lr, scale);
570 :
571 : // parse the cells
572 318 : while (lr.hasMore()) {
573 440 : line = getNextNonCommentLine(lr);
574 220 : if (line.length() == 0) {
575 25 : continue;
576 : }
577 404 : StringTokenizer st2(line, StringTokenizer::WHITECHARS);
578 195 : if (st2.size() == 0) {
579 : continue;
580 : }
581 : try {
582 195 : std::string sourceD = st2.next();
583 195 : std::string destD = st2.next();
584 197 : double vehNumber = StringUtils::toDouble(st2.next()) * factor;
585 193 : if (vehNumber != 0) {
586 193 : add(vehNumber, beginEnd, sourceD, destD, vehType);
587 : }
588 2 : } catch (OutOfBoundsException&) {
589 0 : throw ProcessError(TLF("Missing at least one information in line '%'.", line));
590 2 : } catch (NumberFormatException&) {
591 6 : throw ProcessError(TLF("Not numeric vehicle number in line '%'.", line));
592 2 : }
593 195 : }
594 98 : PROGRESS_DONE_MESSAGE();
595 98 : }
596 :
597 :
598 :
599 : double
600 466 : ODMatrix::getNumLoaded() const {
601 466 : return myNumLoaded;
602 : }
603 :
604 :
605 : double
606 107 : ODMatrix::getNumWritten() const {
607 107 : return myNumWritten;
608 : }
609 :
610 :
611 : double
612 268 : ODMatrix::getNumDiscarded() const {
613 268 : return myNumDiscarded;
614 : }
615 :
616 :
617 : void
618 362 : ODMatrix::applyCurve(const Distribution_Points& ps, ODCell* cell, std::vector<ODCell*>& newCells) {
619 : const std::vector<double>& times = ps.getVals();
620 8918 : for (int i = 0; i < (int)times.size() - 1; ++i) {
621 8556 : ODCell* ncell = new ODCell();
622 8556 : ncell->begin = TIME2STEPS(times[i]);
623 8556 : ncell->end = TIME2STEPS(times[i + 1]);
624 8556 : ncell->origin = cell->origin;
625 8556 : ncell->destination = cell->destination;
626 8556 : ncell->vehicleType = cell->vehicleType;
627 8556 : ncell->vehicleNumber = cell->vehicleNumber * ps.getProbs()[i] / ps.getOverallProb();
628 8556 : newCells.push_back(ncell);
629 : }
630 362 : }
631 :
632 :
633 : void
634 19 : ODMatrix::applyCurve(const Distribution_Points& ps) {
635 19 : std::vector<ODCell*> oldCells = myContainer;
636 : myContainer.clear();
637 381 : for (std::vector<ODCell*>::iterator i = oldCells.begin(); i != oldCells.end(); ++i) {
638 : std::vector<ODCell*> newCells;
639 362 : applyCurve(ps, *i, newCells);
640 : copy(newCells.begin(), newCells.end(), back_inserter(myContainer));
641 362 : delete *i;
642 362 : }
643 19 : }
644 :
645 :
646 : void
647 246 : ODMatrix::loadMatrix(OptionsCont& oc) {
648 492 : std::vector<std::string> files = oc.getStringVector("od-matrix-files");
649 388 : for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); ++i) {
650 180 : LineReader lr(*i);
651 180 : if (!lr.good()) {
652 6 : throw ProcessError(TLF("Could not open '%'.", (*i)));
653 : }
654 178 : std::string type = lr.readLine();
655 : // get the type only
656 178 : if (type.find(';') != std::string::npos) {
657 250 : type = type.substr(0, type.find(';'));
658 : }
659 : // parse type-dependant
660 178 : if (type.length() > 1 && type[1] == 'V') {
661 : // process ptv's 'V'-matrices
662 66 : if (type.find('N') != std::string::npos) {
663 0 : throw ProcessError(TLF("'%' does not contain the needed information about the time described.", *i));
664 : }
665 110 : readV(lr, 1, oc.getString("vtype"), type.find('M') != std::string::npos);
666 112 : } else if (type.length() > 1 && type[1] == 'O') {
667 : // process ptv's 'O'-matrices
668 112 : if (type.find('N') != std::string::npos) {
669 0 : throw ProcessError(TLF("'%' does not contain the needed information about the time described.", *i));
670 : }
671 246 : readO(lr, 1, oc.getString("vtype"), type.find('M') != std::string::npos);
672 : } else {
673 0 : throw ProcessError("'" + *i + "' uses an unknown matrix type '" + type + "'.");
674 : }
675 180 : }
676 416 : std::vector<std::string> amitranFiles = oc.getStringVector("od-amitran-files");
677 235 : for (std::vector<std::string>::iterator i = amitranFiles.begin(); i != amitranFiles.end(); ++i) {
678 54 : if (!FileHelpers::isReadable(*i)) {
679 0 : throw ProcessError(TLF("Could not access matrix file '%' to load.", *i));
680 : }
681 81 : PROGRESS_BEGIN_MESSAGE("Loading matrix in Amitran format from '" + *i + "'");
682 27 : ODAmitranHandler handler(*this, *i);
683 27 : if (!XMLSubSys::runParser(handler, *i)) {
684 4 : PROGRESS_FAILED_MESSAGE();
685 : } else {
686 23 : PROGRESS_DONE_MESSAGE();
687 : }
688 27 : }
689 208 : myVType = oc.getString("vtype");
690 423 : for (std::string file : oc.getStringVector("tazrelation-files")) {
691 14 : if (!FileHelpers::isReadable(file)) {
692 0 : throw ProcessError(TLF("Could not access matrix file '%' to load.", file));
693 : }
694 21 : PROGRESS_BEGIN_MESSAGE("Loading matrix in tazRelation format from '" + file + "'");
695 :
696 : std::vector<SAXWeightsHandler::ToRetrieveDefinition*> retrieverDefs;
697 7 : retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition(oc.getString("tazrelation-attribute"), true, *this));
698 7 : SAXWeightsHandler handler(retrieverDefs, "");
699 7 : if (!XMLSubSys::runParser(handler, file)) {
700 0 : PROGRESS_FAILED_MESSAGE();
701 : } else {
702 7 : PROGRESS_DONE_MESSAGE();
703 : }
704 7 : }
705 246 : }
706 :
707 : void
708 7 : ODMatrix::addTazRelWeight(const std::string intervalID, const std::string& from, const std::string& to,
709 : double val, double beg, double end) {
710 8 : add(val, std::make_pair(TIME2STEPS(beg), TIME2STEPS(end)), from, to, myVType == "" ? intervalID : myVType);
711 7 : }
712 :
713 :
714 : void
715 86 : ODMatrix::loadRoutes(OptionsCont& oc, SUMOSAXHandler& handler) {
716 172 : std::vector<std::string> routeFiles = oc.getStringVector("route-files");
717 119 : for (std::vector<std::string>::iterator i = routeFiles.begin(); i != routeFiles.end(); ++i) {
718 66 : if (!FileHelpers::isReadable(*i)) {
719 0 : throw ProcessError(TLF("Could not access route file '%' to load.", *i));
720 : }
721 99 : PROGRESS_BEGIN_MESSAGE("Loading routes and trips from '" + *i + "'");
722 33 : if (!XMLSubSys::runParser(handler, *i)) {
723 1 : PROGRESS_FAILED_MESSAGE();
724 : } else {
725 32 : PROGRESS_DONE_MESSAGE();
726 : }
727 : }
728 86 : }
729 :
730 :
731 : Distribution_Points
732 19 : ODMatrix::parseTimeLine(const std::vector<std::string>& def, bool timelineDayInHours) {
733 19 : Distribution_Points result("N/A");
734 19 : if (timelineDayInHours) {
735 13 : if (def.size() != 24) {
736 0 : throw ProcessError(TLF("Assuming 24 entries for a day timeline, but got %.", toString(def.size())));
737 : }
738 325 : for (int chour = 0; chour < 24; ++chour) {
739 312 : result.add(chour * 3600., StringUtils::toDouble(def[chour]));
740 : }
741 13 : result.add(24 * 3600., 0.); // dummy value to finish the last interval
742 : } else {
743 24 : for (int i = 0; i < (int)def.size(); i++) {
744 54 : StringTokenizer st2(def[i], ":");
745 18 : if (st2.size() != 2) {
746 0 : throw ProcessError(TLF("Broken time line definition: missing a value in '%'.", def[i]));
747 : }
748 18 : const double time = StringUtils::toDouble(st2.next());
749 18 : result.add(time, StringUtils::toDouble(st2.next()));
750 18 : }
751 : }
752 19 : return result;
753 0 : }
754 :
755 :
756 : void
757 192 : ODMatrix::sortByBeginTime() {
758 192 : std::sort(myContainer.begin(), myContainer.end(), cell_by_begin_comparator());
759 192 : }
760 :
761 :
762 : /****************************************************************************/
|