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