Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 NBNetBuilder.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Thimor Bohn
18 : /// @author Sascha Krieg
19 : /// @author Michael Behrisch
20 : /// @author Walter Bamberger
21 : /// @date 20 Nov 2001
22 : ///
23 : // Instance responsible for building networks
24 : /****************************************************************************/
25 : #include <config.h>
26 :
27 : #include <string>
28 : #include <fstream>
29 : #include <utils/options/OptionsCont.h>
30 : #include <utils/common/MsgHandler.h>
31 : #include <utils/common/UtilExceptions.h>
32 : #include <utils/common/StringTokenizer.h>
33 : #include <utils/common/SUMOVehicleClass.h>
34 : #include <utils/common/SysUtils.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/geom/GeoConvHelper.h>
37 : #include "NBAlgorithms.h"
38 : #include "NBAlgorithms_Ramps.h"
39 : #include "NBAlgorithms_Railway.h"
40 : #include "NBHeightMapper.h"
41 : #include "NBNodeCont.h"
42 : #include "NBEdgeCont.h"
43 : #include "NBPTStop.h"
44 : #include "NBTrafficLightLogicCont.h"
45 : #include "NBDistrictCont.h"
46 : #include "NBDistrict.h"
47 : #include "NBRequest.h"
48 : #include "NBTypeCont.h"
49 : #include "NBNetBuilder.h"
50 :
51 :
52 : // ===========================================================================
53 : // method definitions
54 : // ===========================================================================
55 2002 : NBNetBuilder::NBNetBuilder() :
56 2002 : myEdgeCont(myTypeCont),
57 4004 : myNetworkHaveCrossings(false) {
58 2002 : }
59 :
60 :
61 4004 : NBNetBuilder::~NBNetBuilder() {}
62 :
63 :
64 : void
65 2002 : NBNetBuilder::applyOptions(OptionsCont& oc) {
66 : // apply options to type control
67 6006 : myTypeCont.setEdgeTypeDefaults(oc.getInt("default.lanenumber"), oc.getFloat("default.lanewidth"), oc.getFloat("default.speed"), oc.getFloat("default.friction"),
68 6006 : oc.getInt("default.priority"), parseVehicleClasses(oc.getString("default.allow"), oc.getString("default.disallow")),
69 2002 : SUMOXMLDefinitions::LaneSpreadFunctions.get(oc.getString("default.spreadtype")));
70 : // apply options to edge control
71 2002 : myEdgeCont.applyOptions(oc);
72 : // apply options to traffic light logics control
73 1992 : myTLLCont.applyOptions(oc);
74 1992 : NBEdge::setDefaultConnectionLength(oc.getFloat("default.connection-length"));
75 1992 : }
76 :
77 :
78 : void
79 1704 : NBNetBuilder::compute(OptionsCont& oc, const std::set<std::string>& explicitTurnarounds, bool mayAddOrRemove) {
80 : // reset shapes and angles for stable netedit computation
81 1704 : if (myNodeCont.resetNodeShapes()) {
82 0 : myEdgeCont.computeAngles();
83 : }
84 :
85 : GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
86 :
87 1704 : const bool lefthand = oc.getBool("lefthand");
88 1704 : if (lefthand) {
89 23 : mirrorX();
90 : }
91 :
92 : // MODIFYING THE SETS OF NODES AND EDGES
93 : // Removes edges that are connecting the same node
94 3408 : long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing self-loops"));
95 : int numRemovedEdges = 0;
96 1704 : numRemovedEdges += myNodeCont.removeSelfLoops(myDistrictCont, myEdgeCont, myTLLCont);
97 1704 : PROGRESS_TIME_MESSAGE(before);
98 5024 : if (mayAddOrRemove && oc.exists("remove-edges.isolated") && oc.getBool("remove-edges.isolated")) {
99 10 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding isolated roads"));
100 5 : numRemovedEdges += myNodeCont.removeIsolatedRoads(myDistrictCont, myEdgeCont);
101 5 : PROGRESS_TIME_MESSAGE(before);
102 : }
103 6617 : if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
104 46 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding largest components"));
105 46 : const bool hasStops = oc.exists("ptstop-output") && oc.isSet("ptstop-output") && !myPTStopCont.getStops().empty();
106 23 : numRemovedEdges += myNodeCont.removeComponents(myDistrictCont, myEdgeCont, oc.getInt("keep-edges.components"), hasStops);
107 23 : PROGRESS_TIME_MESSAGE(before);
108 : }
109 5024 : if (mayAddOrRemove && oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload")) {
110 : // pre-process lines to set permissions
111 4 : if (!myPTLineCont.getLines().empty()) {
112 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
113 1 : myPTLineCont.process(myEdgeCont, myPTStopCont);
114 1 : PROGRESS_TIME_MESSAGE(before);
115 : }
116 7 : if (oc.isSet("keep-edges.explicit") || oc.isSet("keep-edges.input-file")) {
117 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing unwished edges"));
118 2 : numRemovedEdges += myEdgeCont.removeUnwishedEdges(myDistrictCont);
119 2 : PROGRESS_TIME_MESSAGE(before);
120 : }
121 4 : const int removed = myEdgeCont.removeEdgesBySpeed(myDistrictCont);
122 4 : if (removed > 0) {
123 0 : numRemovedEdges += removed;
124 0 : WRITE_MESSAGEF(TL(" Removed % edges because by minimum speed."), removed);
125 : }
126 4 : const int removed2 = myEdgeCont.removeEdgesByPermissions(myDistrictCont);
127 4 : if (removed2 > 0) {
128 1 : numRemovedEdges += removed2;
129 2 : WRITE_MESSAGEF(TL(" Removed % edges based on vClass."), removed2);
130 : }
131 : }
132 3408 : if (mayAddOrRemove && oc.getFloat("keep-lanes.min-width") > 0.) {
133 1704 : const int removed = myEdgeCont.removeLanesByWidth(myDistrictCont, oc.getFloat("keep-lanes.min-width"));
134 1704 : if (removed > 0) {
135 0 : numRemovedEdges += removed;
136 0 : WRITE_MESSAGEF(TL(" Removed % edges because of lane width."), removed);
137 : }
138 : }
139 : // Processing pt stops and lines
140 1704 : if (!myPTStopCont.getStops().empty()) {
141 298 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing public transport stops"));
142 447 : if (!(oc.exists("ptline-output") && oc.isSet("ptline-output"))
143 403 : && !oc.getBool("ptstop-output.no-bidi")) {
144 103 : myPTStopCont.localizePTStops(myEdgeCont);
145 : }
146 149 : myPTStopCont.assignEdgeForFloatingStops(myEdgeCont, 20);
147 149 : myPTStopCont.assignLanes(myEdgeCont);
148 149 : PROGRESS_TIME_MESSAGE(before);
149 : }
150 6617 : if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
151 : // post process rail components unless they have stops
152 23 : numRemovedEdges += myNodeCont.removeRailComponents(myDistrictCont, myEdgeCont, myPTStopCont);
153 : }
154 : // removal is done, clean up roundabouts
155 1704 : if (numRemovedEdges > 0) {
156 20 : myEdgeCont.cleanupRoundabouts();
157 : }
158 :
159 1704 : if (!myPTLineCont.getLines().empty()) {
160 194 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
161 97 : myPTLineCont.process(myEdgeCont, myPTStopCont);
162 97 : PROGRESS_TIME_MESSAGE(before);
163 : }
164 :
165 3320 : if (oc.exists("ptline-clean-up") && oc.getBool("ptline-clean-up")) {
166 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Cleaning up public transport stops that are not served by any line"));
167 2 : myPTStopCont.postprocess(myPTLineCont.getServedPTStops());
168 2 : PROGRESS_TIME_MESSAGE(before);
169 : } else {
170 1702 : int numDeletedStops = myPTStopCont.cleanupDeleted(myEdgeCont);
171 1702 : if (numDeletedStops > 0) {
172 48 : WRITE_WARNINGF(TL("Removed % pt stops because they could not be assigned to the network"), toString(numDeletedStops));
173 : }
174 : }
175 :
176 1841 : if (!myPTStopCont.getStops().empty() && !oc.getBool("ptstop-output.no-bidi")) {
177 266 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Align pt stop id signs with corresponding edge id signs"));
178 133 : myPTStopCont.alignIdSigns();
179 133 : PROGRESS_TIME_MESSAGE(before);
180 : }
181 : // analyze and fix railway topology
182 : int numAddedBidi = 0;
183 3320 : if (oc.exists("railway.topology.all-bidi") && oc.getBool("railway.topology.all-bidi")) {
184 6 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
185 6 : numAddedBidi = NBRailwayTopologyAnalyzer::makeAllBidi(myEdgeCont);
186 3308 : } else if (oc.exists("railway.topology.repair") && oc.getBool("railway.topology.repair")) {
187 : // correct railway angles for angle-based connectivity heuristic
188 70 : myEdgeCont.checkGeometries(0, false,
189 : oc.getFloat("geometry.min-radius"), false,
190 35 : oc.getBool("geometry.min-radius.fix.railways"), true);
191 35 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
192 35 : numAddedBidi = NBRailwayTopologyAnalyzer::repairTopology(myEdgeCont, myPTStopCont, myPTLineCont);
193 : }
194 1704 : NBRailwaySignalGuesser::guessRailSignals(myEdgeCont, myPTStopCont);
195 1704 : if (numAddedBidi > 0) {
196 : // update routes
197 37 : myPTLineCont.process(myEdgeCont, myPTStopCont, true);
198 : }
199 3320 : if (oc.exists("railway.topology.direction-priority") && oc.getBool("railway.topology.direction-priority")) {
200 1 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
201 1 : NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, true);
202 3318 : } else if (oc.exists("railway.topology.extend-priority") && oc.getBool("railway.topology.extend-priority")) {
203 3 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
204 3 : NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, false);
205 : }
206 3320 : if (oc.exists("railway.topology.output") && oc.isSet("railway.topology.output")) {
207 29 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
208 29 : NBRailwayTopologyAnalyzer::analyzeTopology(myEdgeCont);
209 : }
210 3320 : if (oc.exists("railway.geometry.straighten") && oc.getBool("railway.geometry.straighten")) {
211 0 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
212 0 : NBRailwayGeometryHelper::straigthenCorrdidor(myEdgeCont, oc.getFloat("geometry.max-angle"));
213 : }
214 :
215 :
216 6634 : if (mayAddOrRemove && oc.exists("edges.join-tram-dist") && oc.getFloat("edges.join-tram-dist") >= 0) {
217 : // should come before joining junctions
218 12 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining tram edges"));
219 6 : int numJoinedTramEdges = myEdgeCont.joinTramEdges(myDistrictCont, myPTStopCont, myPTLineCont, oc.getFloat("edges.join-tram-dist"));
220 6 : PROGRESS_TIME_MESSAGE(before);
221 6 : if (numJoinedTramEdges > 0) {
222 12 : WRITE_MESSAGEF(TL(" Joined % tram edges into roads."), toString(numJoinedTramEdges));
223 : }
224 : }
225 3408 : if (oc.getBool("junctions.join")
226 4904 : || (oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
227 3271 : || oc.getBool("tls.guess.joining")
228 6342 : || (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals"))) {
229 : // preliminary geometry computations to determine the length of edges
230 : // This depends on turning directions and sorting of edge list
231 : // in case junctions are joined geometry computations have to be repeated
232 : // preliminary roundabout computations to avoid damaging roundabouts via junctions.join or ramps.guess
233 147 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
234 147 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
235 147 : myEdgeCont.computeLaneShapes();
236 147 : myNodeCont.computeNodeShapes();
237 147 : myEdgeCont.computeEdgeShapes();
238 294 : if (oc.getBool("roundabouts.guess")) {
239 147 : myEdgeCont.guessRoundabouts();
240 : }
241 147 : const std::set<EdgeSet>& roundabouts = myEdgeCont.getRoundabouts();
242 147 : for (std::set<EdgeSet>::const_iterator it_round = roundabouts.begin();
243 161 : it_round != roundabouts.end(); ++it_round) {
244 : std::vector<std::string> nodeIDs;
245 111 : for (EdgeSet::const_iterator it_edge = it_round->begin(); it_edge != it_round->end(); ++it_edge) {
246 97 : nodeIDs.push_back((*it_edge)->getToNode()->getID());
247 : }
248 14 : myNodeCont.addJoinExclusion(nodeIDs);
249 14 : }
250 147 : NBNodeTypeComputer::validateRailCrossings(myNodeCont, myTLLCont);
251 3162 : } else if ((myEdgeCont.hasGuessedRoundabouts() || oc.getBool("crossings.guess")) && oc.getBool("roundabouts.guess")) {
252 48 : myEdgeCont.guessRoundabouts();
253 48 : myEdgeCont.markRoundabouts();
254 : }
255 : // join junctions (may create new "geometry"-nodes so it needs to come before removing these
256 5024 : if (mayAddOrRemove && oc.exists("junctions.join-exclude") && oc.isSet("junctions.join-exclude")) {
257 2 : myNodeCont.addJoinExclusion(oc.getStringVector("junctions.join-exclude"));
258 : }
259 1704 : int numJoined = myNodeCont.joinLoadedClusters(myDistrictCont, myEdgeCont, myTLLCont);
260 3408 : if (mayAddOrRemove && oc.getBool("junctions.join")) {
261 208 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junction clusters"));
262 104 : numJoined += myNodeCont.joinJunctions(oc.getFloat("junctions.join-dist"), myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont);
263 104 : PROGRESS_TIME_MESSAGE(before);
264 : }
265 1704 : if (numJoined > 0) {
266 180 : WRITE_MESSAGEF(TL(" Joined % junction cluster(s)."), toString(numJoined));
267 : }
268 5112 : if (mayAddOrRemove && oc.exists("junctions.join-same") && oc.getBool("junctions.join-same")) {
269 20 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junctions with identical coordinates"));
270 10 : int numJoined2 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont);
271 10 : PROGRESS_TIME_MESSAGE(before);
272 10 : if (numJoined2 > 0) {
273 6 : WRITE_MESSAGEF(TL(" Joined % junctions."), toString(numJoined2));
274 : }
275 : }
276 : //
277 5024 : if (mayAddOrRemove && oc.exists("join-lanes") && oc.getBool("join-lanes")) {
278 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining lanes"));
279 1 : const int num = myEdgeCont.joinLanes(SVC_IGNORING) + myEdgeCont.joinLanes(SVC_PEDESTRIAN);
280 1 : PROGRESS_TIME_MESSAGE(before);
281 2 : WRITE_MESSAGEF(TL(" Joined lanes on % edges."), toString(num));
282 : }
283 : //
284 1704 : if (mayAddOrRemove) {
285 3408 : const bool removeGeometryNodes = oc.exists("geometry.remove") && oc.getBool("geometry.remove");
286 6717 : before = PROGRESS_BEGIN_TIME_MESSAGE("Removing empty nodes" + std::string(removeGeometryNodes ? " and geometry nodes" : ""));
287 : // removeUnwishedNodes needs turnDirections. @todo: try to call this less often
288 1704 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
289 1704 : const int numRemoved = myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, removeGeometryNodes);
290 1704 : PROGRESS_TIME_MESSAGE(before);
291 3408 : WRITE_MESSAGEF(TL(" % nodes removed."), toString(numRemoved));
292 : }
293 :
294 : // MOVE TO ORIGIN
295 : // compute new boundary after network modifications have taken place
296 1704 : Boundary boundary;
297 52395 : for (std::map<std::string, NBNode*>::const_iterator it = myNodeCont.begin(); it != myNodeCont.end(); ++it) {
298 50691 : boundary.add(it->second->getPosition());
299 : }
300 92462 : for (std::map<std::string, NBEdge*>::const_iterator it = myEdgeCont.begin(); it != myEdgeCont.end(); ++it) {
301 90758 : boundary.add(it->second->getGeometry().getBoxBoundary());
302 : }
303 : geoConvHelper.setConvBoundary(boundary);
304 :
305 4206 : if (!oc.getBool("offset.disable-normalization") && oc.isDefault("offset.x") && oc.isDefault("offset.y")) {
306 1249 : moveToOrigin(geoConvHelper, lefthand);
307 : }
308 1704 : geoConvHelper.computeFinal(lefthand); // information needed for location element fixed at this point
309 :
310 4933 : if (oc.exists("geometry.min-dist") && !oc.isDefault("geometry.min-dist")) {
311 6 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Reducing geometries"));
312 3 : myEdgeCont.reduceGeometries(oc.getFloat("geometry.min-dist"));
313 3 : PROGRESS_TIME_MESSAGE(before);
314 : }
315 : // @note: removing geometry can create similar edges so joinSimilarEdges must come afterwards
316 : // @note: likewise splitting can destroy similarities so joinSimilarEdges must come before
317 3408 : if (mayAddOrRemove && oc.getBool("edges.join")) {
318 38 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining similar edges"));
319 38 : const bool removeDuplicates = oc.exists("junctions.join-same") && oc.getBool("junctions.join-same");
320 19 : myNodeCont.joinSimilarEdges(myDistrictCont, myEdgeCont, myTLLCont, removeDuplicates);
321 : // now we may have new chances to remove geometry if wished
322 38 : if (oc.exists("geometry.remove") && oc.getBool("geometry.remove")) {
323 1 : myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, true);
324 : }
325 19 : PROGRESS_TIME_MESSAGE(before);
326 : }
327 3408 : if (oc.getBool("opposites.guess")) {
328 32 : PROGRESS_BEGIN_MESSAGE(TL("guessing opposite direction edges"));
329 16 : myEdgeCont.guessOpposites();
330 16 : PROGRESS_DONE_MESSAGE();
331 : }
332 : //
333 5112 : if (mayAddOrRemove && oc.exists("geometry.split") && oc.getBool("geometry.split")) {
334 6 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Splitting geometry edges"));
335 3 : myEdgeCont.splitGeometry(myDistrictCont, myNodeCont);
336 : // newly split junctions might also be joinable
337 3 : PROGRESS_TIME_MESSAGE(before);
338 6 : if (oc.getBool("junctions.join-same")) {
339 1 : int numJoined3 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont);
340 1 : if (numJoined3 > 0) {
341 2 : WRITE_MESSAGEF(TL(" Joined % junctions after splitting geometry."), toString(numJoined3));
342 : }
343 : }
344 : }
345 : // turning direction
346 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing turning directions"));
347 1704 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont);
348 1704 : PROGRESS_TIME_MESSAGE(before);
349 : // correct edge geometries to avoid overlap
350 3320 : if (oc.exists("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap")) {
351 1601 : myNodeCont.avoidOverlap();
352 : }
353 :
354 : // GUESS TLS POSITIONS
355 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Assigning nodes to traffic lights"));
356 3408 : if (oc.isSet("tls.set")) {
357 8 : std::vector<std::string> tlControlledNodes = oc.getStringVector("tls.set");
358 16 : TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(oc.getString("tls.default-type"));
359 16 : for (std::vector<std::string>::const_iterator i = tlControlledNodes.begin(); i != tlControlledNodes.end(); ++i) {
360 8 : NBNode* node = myNodeCont.retrieve(*i);
361 8 : if (node == nullptr) {
362 0 : WRITE_WARNING("Building a tl-logic for junction '" + *i + "' is not possible." + "\n The junction '" + *i + "' is not known.");
363 : } else {
364 16 : myNodeCont.setAsTLControlled(node, myTLLCont, type);
365 : }
366 : }
367 8 : }
368 1704 : myNodeCont.guessTLs(oc, myTLLCont);
369 1704 : PROGRESS_TIME_MESSAGE(before);
370 :
371 : // guess ramps (after guessing tls because ramps should not be build at traffic lights)
372 3408 : const bool modifyRamps = mayAddOrRemove && (
373 5024 : (oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
374 4922 : || (oc.exists("ramps.set") && oc.isSet("ramps.set")));
375 4906 : if (modifyRamps || (oc.exists("ramps.guess-acceleration-lanes") && oc.getBool("ramps.guess-acceleration-lanes"))) {
376 1616 : before = SysUtils::getCurrentMillis();
377 1616 : if (modifyRamps) {
378 177 : PROGRESS_BEGIN_MESSAGE(TL("Guessing and setting on-/off-ramps"));
379 : }
380 1616 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
381 1616 : NBRampsComputer rc;
382 1616 : rc.computeRamps(*this, oc, mayAddOrRemove);
383 :
384 1616 : if (modifyRamps) {
385 59 : PROGRESS_TIME_MESSAGE(before);
386 : }
387 : }
388 : // guess bike lanes
389 5107 : if (mayAddOrRemove && ((oc.getBool("bikelanes.guess") || oc.getBool("bikelanes.guess.from-permissions")))) {
390 18 : const int bikelanes = myEdgeCont.guessSpecialLanes(SVC_BICYCLE, oc.getFloat("default.bikelane-width"),
391 : oc.getFloat("bikelanes.guess.min-speed"),
392 : oc.getFloat("bikelanes.guess.max-speed"),
393 12 : oc.getBool("bikelanes.guess.from-permissions"),
394 : "bikelanes.guess.exclude",
395 6 : myTLLCont);
396 12 : WRITE_MESSAGEF(TL("Guessed % bike lanes."), toString(bikelanes));
397 : }
398 :
399 : // guess sidewalks
400 5100 : if (mayAddOrRemove && ((oc.getBool("sidewalks.guess") || oc.getBool("sidewalks.guess.from-permissions")))) {
401 42 : const int sidewalks = myEdgeCont.guessSpecialLanes(SVC_PEDESTRIAN, oc.getFloat("default.sidewalk-width"),
402 : oc.getFloat("sidewalks.guess.min-speed"),
403 : oc.getFloat("sidewalks.guess.max-speed"),
404 28 : oc.getBool("sidewalks.guess.from-permissions"),
405 : "sidewalks.guess.exclude",
406 14 : myTLLCont);
407 28 : WRITE_MESSAGEF(TL("Guessed % sidewalks."), toString(sidewalks));
408 : }
409 : // check whether any not previously setable connections may be set now
410 1704 : myEdgeCont.recheckPostProcessConnections();
411 :
412 : // remap ids if wished
413 1704 : if (mayAddOrRemove) {
414 1704 : int numChangedEdges = myEdgeCont.remapIDs(oc.getBool("numerical-ids"), oc.isSet("reserved-ids"), oc.getString("prefix"), myPTStopCont);
415 1704 : int numChangedNodes = myNodeCont.remapIDs(oc.getBool("numerical-ids"), oc.isSet("reserved-ids"), oc.getString("prefix"), myTLLCont);
416 1704 : if (numChangedEdges + numChangedNodes > 0) {
417 46 : WRITE_MESSAGEF(TL("Remapped % edge IDs and % node IDs."), toString(numChangedEdges), toString(numChangedNodes));
418 : }
419 : }
420 :
421 : //
422 3408 : if (oc.exists("geometry.max-angle")) {
423 3232 : myEdgeCont.checkGeometries(
424 3232 : DEG2RAD(oc.getFloat("geometry.max-angle")),
425 3232 : oc.getBool("geometry.max-angle.fix"),
426 : oc.getFloat("geometry.min-radius"),
427 3232 : oc.getBool("geometry.min-radius.fix"),
428 3232 : oc.getBool("geometry.min-radius.fix.railways"));
429 : }
430 :
431 : // GEOMETRY COMPUTATION
432 : //
433 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Sorting nodes' edges"));
434 1704 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
435 1704 : PROGRESS_TIME_MESSAGE(before);
436 1704 : myEdgeCont.computeLaneShapes();
437 : //
438 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node shapes"));
439 3408 : if (oc.exists("geometry.junction-mismatch-threshold")) {
440 3232 : myNodeCont.computeNodeShapes(oc.getFloat("geometry.junction-mismatch-threshold"));
441 : } else {
442 88 : myNodeCont.computeNodeShapes();
443 : }
444 1704 : PROGRESS_TIME_MESSAGE(before);
445 : //
446 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing edge shapes"));
447 2960 : myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
448 1704 : PROGRESS_TIME_MESSAGE(before);
449 : // resort edges based on the node and edge shapes
450 1704 : NBNodesEdgesSorter::sortNodesEdges(myNodeCont, true);
451 1704 : NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
452 :
453 : // APPLY SPEED MODIFICATIONS
454 3408 : if (oc.exists("speed.offset")) {
455 1616 : const double speedOffset = oc.getFloat("speed.offset");
456 1616 : const double speedFactor = oc.getFloat("speed.factor");
457 1616 : const double speedMin = oc.getFloat("speed.minimum");
458 1616 : if (speedOffset != 0 || speedFactor != 1 || speedMin > 0) {
459 6 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Applying speed modifications"));
460 7 : for (const auto& it : myEdgeCont) {
461 5 : NBEdge* const e = it.second;
462 10 : for (int i = 0; i < e->getNumLanes(); i++) {
463 6 : e->setSpeed(i, MAX2(e->getLaneSpeed(i) * speedFactor + speedOffset, speedMin));
464 : }
465 : }
466 2 : PROGRESS_TIME_MESSAGE(before);
467 : }
468 : }
469 :
470 : // CONNECTIONS COMPUTATION
471 : //
472 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node types"));
473 1704 : NBNodeTypeComputer::computeNodeTypes(myNodeCont, myTLLCont);
474 1704 : PROGRESS_TIME_MESSAGE(before);
475 : //
476 1704 : myNetworkHaveCrossings = oc.getBool("walkingareas");
477 3408 : if (mayAddOrRemove && oc.getBool("crossings.guess")) {
478 58 : myNetworkHaveCrossings = true;
479 58 : int crossings = 0;
480 3447 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
481 3389 : crossings += (*i).second->guessCrossings();
482 : }
483 116 : WRITE_MESSAGEF(TL("Guessed % pedestrian crossings."), toString(crossings));
484 : }
485 1704 : if (!myNetworkHaveCrossings) {
486 : bool haveValidCrossings = false;
487 : // recheck whether we had crossings in the input
488 48349 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
489 46815 : if (i->second->getCrossings().size() > 0) {
490 103 : myNetworkHaveCrossings = true;
491 : haveValidCrossings = true;
492 103 : break;
493 46712 : } else if (i->second->getCrossingsIncludingInvalid().size() > 0) {
494 0 : myNetworkHaveCrossings = true;
495 : }
496 : }
497 1637 : if (myNetworkHaveCrossings && !haveValidCrossings) {
498 : // initial crossings removed or invalidated, keep walkingareas
499 0 : oc.resetWritable();
500 0 : oc.set("walkingareas", "true");
501 : }
502 : }
503 :
504 1704 : if (!mayAddOrRemove && myNetworkHaveCrossings) {
505 : // crossings added via netedit
506 0 : oc.resetWritable();
507 0 : oc.set("no-internal-links", "false");
508 : }
509 :
510 : //
511 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing priorities"));
512 1704 : NBEdgePriorityComputer::computeEdgePriorities(myNodeCont);
513 1704 : PROGRESS_TIME_MESSAGE(before);
514 : //
515 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approached edges"));
516 1704 : myEdgeCont.computeEdge2Edges(oc.getBool("no-left-connections"));
517 1704 : PROGRESS_TIME_MESSAGE(before);
518 : //
519 3408 : if (oc.getBool("roundabouts.guess")) {
520 3354 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing and setting roundabouts"));
521 1677 : const int numGuessed = myEdgeCont.guessRoundabouts();
522 1677 : if (numGuessed > 0) {
523 28 : WRITE_MESSAGEF(TL(" Guessed % roundabout(s)."), toString(numGuessed));
524 : }
525 1677 : PROGRESS_TIME_MESSAGE(before);
526 : }
527 1704 : myEdgeCont.markRoundabouts();
528 : //
529 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approaching lanes"));
530 1704 : myEdgeCont.computeLanes2Edges();
531 1704 : PROGRESS_TIME_MESSAGE(before);
532 : //
533 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Dividing of lanes on approached lanes"));
534 1704 : myNodeCont.computeLanes2Lanes();
535 1704 : myEdgeCont.sortOutgoingLanesConnections();
536 1704 : PROGRESS_TIME_MESSAGE(before);
537 : //
538 3408 : if (oc.getBool("fringe.guess")) {
539 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing Network fringe"));
540 2 : const int numGuessed = myNodeCont.guessFringe();
541 2 : if (numGuessed > 0) {
542 4 : WRITE_MESSAGEF(TL(" Guessed % fringe nodes."), toString(numGuessed));
543 : }
544 2 : PROGRESS_TIME_MESSAGE(before);
545 : }
546 : //
547 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing turnarounds"));
548 3408 : if (!oc.getBool("no-turnarounds")) {
549 1020 : myEdgeCont.appendTurnarounds(
550 2040 : oc.getBool("no-turnarounds.tls"),
551 2040 : oc.getBool("no-turnarounds.fringe"),
552 2040 : oc.getBool("no-turnarounds.except-deadend"),
553 2040 : oc.getBool("no-turnarounds.except-turnlane"),
554 2040 : oc.getBool("no-turnarounds.geometry"));
555 : } else {
556 1368 : myEdgeCont.appendTurnarounds(explicitTurnarounds, oc.getBool("no-turnarounds.tls"));
557 : }
558 5057 : if (oc.exists("railway.topology.repair.stop-turn") && oc.getBool("railway.topology.repair.stop-turn")
559 1737 : && myPTStopCont.getStops().size() > 0) {
560 : // allow direction reversal at all bidi-edges with stops
561 25 : myEdgeCont.appendRailwayTurnarounds(myPTStopCont);
562 : }
563 1704 : PROGRESS_TIME_MESSAGE(before);
564 : //
565 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Rechecking of lane endings"));
566 1704 : myEdgeCont.recheckLanes();
567 1704 : PROGRESS_TIME_MESSAGE(before);
568 :
569 1876 : if (myNetworkHaveCrossings && !oc.getBool("no-internal-links")) {
570 4314 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
571 4146 : i->second->buildCrossingsAndWalkingAreas();
572 : }
573 : } else {
574 48193 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
575 : // needed by netedit if the last crossings was deleted from the network
576 : // and walkingareas have been invalidated since the last call to compute()
577 46657 : i->second->discardWalkingareas();
578 : }
579 3072 : if (oc.getBool("no-internal-links")) {
580 23871 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
581 23319 : i->second->discardAllCrossings(false);
582 : }
583 : }
584 : }
585 : // join traffic lights (after building connections)
586 3408 : if (oc.getBool("tls.join")) {
587 72 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining traffic light nodes"));
588 36 : myNodeCont.joinTLS(myTLLCont, oc.getFloat("tls.join-dist"));
589 36 : PROGRESS_TIME_MESSAGE(before);
590 : }
591 :
592 : // COMPUTING RIGHT-OF-WAY AND TRAFFIC LIGHT PROGRAMS
593 : //
594 3408 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light control information"));
595 1704 : myTLLCont.setTLControllingInformation(myEdgeCont, myNodeCont);
596 3318 : if (oc.exists("opendrive-files") && oc.isSet("opendrive-files")) {
597 22 : myTLLCont.setOpenDriveSignalParameters();
598 : }
599 1703 : PROGRESS_TIME_MESSAGE(before);
600 : //
601 3406 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node logics"));
602 1703 : myNodeCont.computeLogics(myEdgeCont);
603 1703 : PROGRESS_TIME_MESSAGE(before);
604 :
605 : //
606 3406 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light logics"));
607 1703 : std::pair<int, int> numbers = myTLLCont.computeLogics(oc);
608 1703 : PROGRESS_TIME_MESSAGE(before);
609 1703 : std::string progCount = "";
610 1703 : if (numbers.first != numbers.second) {
611 27 : progCount = "(" + toString(numbers.second) + " programs) ";
612 : }
613 5109 : WRITE_MESSAGEF(TL(" % traffic light(s) %computed."), toString(numbers.first), progCount);
614 3340 : if (oc.exists("opendrive-files") && oc.isSet("opendrive-files") && oc.getBool("opendrive.signal-groups")) {
615 0 : myTLLCont.applyOpenDriveControllers(oc);
616 : }
617 :
618 92557 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
619 90854 : (*i).second->sortOutgoingConnectionsByIndex();
620 : }
621 : // FINISHING INNER EDGES
622 : std::set<NBTrafficLightDefinition*> largeNodeTLS;
623 3406 : if (!oc.getBool("no-internal-links")) {
624 3453 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Building inner edges"));
625 : // walking areas shall only be built if crossings are wished as well
626 28631 : for (const auto& item : myNodeCont) {
627 27480 : if (item.second->buildInnerEdges() > NBTrafficLightDefinition::MIN_YELLOW_SECONDS) {
628 4817 : const std::set<NBTrafficLightDefinition*>& tlDefs = item.second->getControllingTLS();
629 4817 : largeNodeTLS.insert(tlDefs.begin(), tlDefs.end());
630 : }
631 : }
632 1151 : PROGRESS_TIME_MESSAGE(before);
633 : }
634 : // PATCH NODE SHAPES
635 3406 : if (oc.getFloat("junctions.scurve-stretch") > 0) {
636 : // @note: nodes have collected correction hints in buildInnerEdges()
637 4 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("stretching junctions to smooth geometries"));
638 2 : myEdgeCont.computeLaneShapes();
639 2 : myNodeCont.computeNodeShapes();
640 3 : myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
641 15 : for (const auto& item : myNodeCont) {
642 13 : item.second->buildInnerEdges();
643 : }
644 2 : PROGRESS_TIME_MESSAGE(before);
645 : }
646 1886 : if (myEdgeCont.getNumEdgeSplits() > 0 && !oc.getBool("no-internal-links")) {
647 : // edges with custom lengths were split, this has to take into account
648 : // internal edge lengths (after geometry computation)
649 75 : myEdgeCont.fixSplitCustomLength();
650 : }
651 : // recheck phases for large junctions
652 2425 : for (NBTrafficLightDefinition* def : largeNodeTLS) {
653 722 : myTLLCont.computeSingleLogic(oc, def);
654 : }
655 : // compute lane-to-lane node logics (require traffic lights and inner edges to be done)
656 1703 : myNodeCont.computeLogics2(myEdgeCont, oc);
657 :
658 : // remove guessed traffic lights at junctions without conflicts (requires computeLogics2)
659 1703 : myNodeCont.recheckGuessedTLS(myTLLCont);
660 :
661 : // compute keepClear status (requires computeLogics2)
662 1703 : myNodeCont.computeKeepClear();
663 :
664 : //
665 3406 : if (oc.isSet("street-sign-output")) {
666 2 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Generating street signs"));
667 1 : myEdgeCont.generateStreetSigns();
668 1 : PROGRESS_TIME_MESSAGE(before);
669 : }
670 :
671 :
672 3406 : if (lefthand != oc.getBool("flip-y-axis")) {
673 23 : mirrorX();
674 : }
675 :
676 4933 : if (oc.exists("geometry.check-overlap") && oc.getFloat("geometry.check-overlap") > 0) {
677 0 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking overlapping edges"));
678 0 : myEdgeCont.checkOverlap(oc.getFloat("geometry.check-overlap"), oc.getFloat("geometry.check-overlap.vertical-threshold"));
679 0 : PROGRESS_TIME_MESSAGE(before);
680 : }
681 1724 : if (geoConvHelper.getConvBoundary().getZRange() > 0 && oc.getFloat("geometry.max-grade") > 0) {
682 42 : before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking edge grade"));
683 : // user input is in %
684 21 : myEdgeCont.checkGrade(oc.getFloat("geometry.max-grade") / 100);
685 21 : PROGRESS_TIME_MESSAGE(before);
686 : }
687 :
688 : // find accesses for pt rail stops and add bidi-stops
689 1703 : if (!myPTStopCont.getStops().empty()) {
690 : // re-adapt stop lanes after adding special lanes and cutting edge shapes at junction
691 135 : myPTStopCont.assignLanes(myEdgeCont);
692 135 : before = SysUtils::getCurrentMillis();
693 : int numBidiStops = 0;
694 270 : if (!oc.getBool("ptstop-output.no-bidi")) {
695 133 : numBidiStops = myPTStopCont.generateBidiStops(myEdgeCont);
696 : }
697 270 : PROGRESS_BEGIN_MESSAGE(TL("Find accesses for pt rail stops"));
698 135 : double maxRadius = oc.getFloat("railway.access-distance");
699 135 : double accessFactor = oc.getFloat("railway.access-factor");
700 135 : int maxCount = oc.getInt("railway.max-accesses");
701 135 : myPTStopCont.findAccessEdgesForRailStops(myEdgeCont, maxRadius, maxCount, accessFactor);
702 135 : PROGRESS_TIME_MESSAGE(before);
703 135 : if (numBidiStops > 0) {
704 63 : myPTLineCont.fixBidiStops(myEdgeCont);
705 : }
706 : }
707 1703 : myPTLineCont.removeInvalidEdges(myEdgeCont);
708 : // ensure that all turning lanes have sufficient permissions
709 1703 : myPTLineCont.fixPermissions();
710 :
711 4930 : if (oc.exists("ignore-change-restrictions") && !oc.isDefault("ignore-change-restrictions")) {
712 3 : SVCPermissions ignoring = parseVehicleClasses(oc.getStringVector("ignore-change-restrictions"));
713 3 : myEdgeCont.updateAllChangeRestrictions(ignoring);
714 : }
715 :
716 1703 : NBRequest::reportWarnings();
717 : // report on very large networks
718 5107 : if (MAX2(geoConvHelper.getConvBoundary().xmax(), geoConvHelper.getConvBoundary().ymax()) > 1000000 ||
719 1701 : MIN2(geoConvHelper.getConvBoundary().xmin(), geoConvHelper.getConvBoundary().ymin()) < -1000000) {
720 4 : WRITE_WARNING(TL("Network contains very large coordinates and will probably flicker in the GUI. Check for outlying nodes and make sure the network is shifted to the coordinate origin"));
721 : }
722 :
723 : // clean up OSM processing params
724 3318 : if (oc.exists("osm-files") && oc.isSet("osm-files")) {
725 34341 : for (auto item : myEdgeCont) {
726 34174 : item.second->unsetParameter(NBTrafficLightDefinition::OSM_DIRECTION);
727 : }
728 : }
729 1704 : }
730 :
731 :
732 : void
733 1249 : NBNetBuilder::moveToOrigin(GeoConvHelper& geoConvHelper, bool lefthand) {
734 2498 : long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Moving network to origin"));
735 1249 : Boundary boundary = geoConvHelper.getConvBoundary();
736 1249 : const double x = -boundary.xmin();
737 1249 : const double y = -(lefthand ? boundary.ymax() : boundary.ymin());
738 : //if (lefthand) {
739 : // y = boundary.ymax();
740 : //}
741 38529 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
742 37280 : (*i).second->reshiftPosition(x, y);
743 : }
744 69072 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
745 67823 : (*i).second->reshiftPosition(x, y);
746 : }
747 1299 : for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
748 50 : (*i).second->reshiftPosition(x, y);
749 : }
750 2899 : for (const auto& stopIt : myPTStopCont.getStops()) {
751 1650 : stopIt.second->reshiftPosition(x, y);
752 : }
753 1249 : geoConvHelper.moveConvertedBy(x, y);
754 1249 : PROGRESS_TIME_MESSAGE(before);
755 1249 : }
756 :
757 :
758 : void
759 46 : NBNetBuilder::mirrorX() {
760 : // mirror the network along the X-axis
761 888 : for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
762 842 : (*i).second->mirrorX();
763 : }
764 1190 : for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
765 1144 : (*i).second->mirrorX();
766 : }
767 46 : for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
768 0 : (*i).second->mirrorX();
769 : }
770 105 : for (const auto& stopIt : myPTStopCont.getStops()) {
771 59 : stopIt.second->mirrorX();
772 : }
773 46 : }
774 :
775 :
776 : bool
777 428909 : NBNetBuilder::transformCoordinate(Position& from, bool includeInBoundary, GeoConvHelper* from_srs) {
778 428909 : Position orig(from);
779 : bool ok = true;
780 : if (GeoConvHelper::getNumLoaded() > 1
781 2500 : && GeoConvHelper::getLoaded().usingGeoProjection()
782 2326 : && from_srs != nullptr
783 2326 : && from_srs->usingGeoProjection()
784 431235 : && *from_srs != GeoConvHelper::getLoaded()) {
785 2326 : from_srs->cartesian2geo(from);
786 2326 : ok &= GeoConvHelper::getLoaded().x2cartesian(from, false);
787 : }
788 428909 : if (from_srs == nullptr || !GeoConvHelper::getProcessing().usingGeoProjection()) {
789 : // if getProcessing is not a geo-projection, assume it a cartesian transformation (i.e. shift)
790 428794 : ok &= GeoConvHelper::getProcessing().x2cartesian(from, includeInBoundary);
791 :
792 189060 : if (from_srs == nullptr && GeoConvHelper::getProcessing().usingGeoProjection()
793 160016 : && GeoConvHelper::getNumLoaded() > 0
794 428830 : && GeoConvHelper::getLoaded().usingGeoProjection()) {
795 : // apply geo patch to loaded geo-network (offset must match)
796 36 : from = from + GeoConvHelper::getLoaded().getOffset();
797 : }
798 : }
799 428909 : if (ok) {
800 428909 : const NBHeightMapper& hm = NBHeightMapper::get();
801 428909 : if (hm.ready()) {
802 604 : if (from_srs != nullptr && from_srs->usingGeoProjection()) {
803 600 : from_srs->cartesian2geo(orig);
804 : }
805 604 : from.setz(hm.getZ(orig));
806 : }
807 : }
808 : const double eps = 1e-6;
809 428909 : from.set(std::round(from.x() / eps) * eps, std::round(from.y() / eps) * eps, std::round(from.z() / eps) * eps);
810 428909 : return ok;
811 : }
812 :
813 :
814 : bool
815 144218 : NBNetBuilder::transformCoordinates(PositionVector& from, bool includeInBoundary, GeoConvHelper* from_srs) {
816 144218 : const double maxLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
817 144218 : if (maxLength > 0 && from.size() > 1) {
818 : // transformation to cartesian coordinates must happen before we can check segment length
819 : PositionVector copy = from;
820 118 : for (int i = 0; i < (int) from.size(); i++) {
821 85 : transformCoordinate(copy[i], false);
822 : }
823 33 : addGeometrySegments(from, copy, maxLength);
824 33 : }
825 : bool ok = true;
826 504993 : for (int i = 0; i < (int) from.size(); i++) {
827 360775 : ok = ok && transformCoordinate(from[i], includeInBoundary, from_srs);
828 : }
829 144218 : return ok;
830 : }
831 :
832 :
833 : int
834 71 : NBNetBuilder::addGeometrySegments(PositionVector& from, const PositionVector& cartesian, const double maxLength) {
835 : // check lengths and insert new points where needed (in the original
836 : // coordinate system)
837 : int inserted = 0;
838 161 : for (int i = 0; i < (int)cartesian.size() - 1; i++) {
839 90 : Position start = from[i + inserted];
840 90 : Position end = from[i + inserted + 1];
841 90 : double length = cartesian[i].distanceTo(cartesian[i + 1]);
842 90 : const Position step = (end - start) * (maxLength / length);
843 : int steps = 0;
844 312 : while (length > maxLength) {
845 222 : length -= maxLength;
846 222 : steps++;
847 222 : from.insert(from.begin() + i + inserted + 1, start + (step * steps));
848 222 : inserted++;
849 : }
850 : }
851 71 : return inserted;
852 : }
853 :
854 :
855 : bool
856 622 : NBNetBuilder::runningNetedit() {
857 : // see GNELoadThread::fillOptions
858 1244 : return OptionsCont::getOptions().exists("new");
859 : }
860 :
861 :
862 : /****************************************************************************/
|