001/* *********************************************************************** *
002 * project: org.matsim.*
003 * CreatePseudoNetwork
004 *                                                                         *
005 * *********************************************************************** *
006 *                                                                         *
007 * copyright       : (C) 2009 by the members listed in the COPYING,        *
008 *                   LICENSE and WARRANTY file.                            *
009 * email           : info at matsim dot org                                *
010 *                                                                         *
011 * *********************************************************************** *
012 *                                                                         *
013 *   This program is free software; you can redistribute it and/or modify  *
014 *   it under the terms of the GNU General Public License as published by  *
015 *   the Free Software Foundation; either version 2 of the License, or     *
016 *   (at your option) any later version.                                   *
017 *   See also COPYING, LICENSE and WARRANTY file                           *
018 *                                                                         *
019 * *********************************************************************** */
020
021package org.matsim.pt.utils;
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import org.matsim.api.core.v01.Id;
032import org.matsim.api.core.v01.TransportMode;
033import org.matsim.api.core.v01.network.Link;
034import org.matsim.api.core.v01.network.Network;
035import org.matsim.api.core.v01.network.Node;
036import org.matsim.core.population.routes.NetworkRoute;
037import org.matsim.core.population.routes.RouteUtils;
038import org.matsim.core.utils.collections.Tuple;
039import org.matsim.core.utils.geometry.CoordUtils;
040import org.matsim.pt.transitSchedule.api.TransitLine;
041import org.matsim.pt.transitSchedule.api.TransitRoute;
042import org.matsim.pt.transitSchedule.api.TransitRouteStop;
043import org.matsim.pt.transitSchedule.api.TransitSchedule;
044import org.matsim.pt.transitSchedule.api.TransitStopArea;
045import org.matsim.pt.transitSchedule.api.TransitStopFacility;
046
047/**
048 * Builds a network where transit vehicles can drive along and assigns the correct
049 * links to the transit stop facilities and routes of transit lines. As each transit
050 * stop facility can only be connected to at most one link, the algorithm is forced
051 * to duplicated transit stop facilities in certain cases to build the network.
052 *
053 * @author mrieser
054 */
055public class CreatePseudoNetwork {
056
057        private final TransitSchedule schedule;
058        private final Network network;
059        private final String prefix;
060        private final double linkFreeSpeed;
061        private final double linkCapacity;
062        
063
064        private final Map<Tuple<Node, Node>, Link> links = new HashMap<Tuple<Node, Node>, Link>();
065        private final Map<Tuple<Node, Node>, TransitStopFacility> stopFacilities = new HashMap<Tuple<Node, Node>, TransitStopFacility>();
066        private final Map<TransitStopFacility, Node> nodes = new HashMap<TransitStopFacility, Node>();
067        private final Map<TransitStopFacility, List<TransitStopFacility>> facilityCopies = new HashMap<TransitStopFacility, List<TransitStopFacility>>();
068
069        private long linkIdCounter = 0;
070
071        private final Set<String> transitModes = Collections.singleton(TransportMode.pt);
072
073        public CreatePseudoNetwork(final TransitSchedule schedule, final Network network, final String networkIdPrefix) {
074                this.schedule = schedule;
075                this.network = network;
076                this.prefix = networkIdPrefix;
077                this.linkFreeSpeed = 100.0 / 3.6;
078                this.linkCapacity = 100000.0;
079        }
080        
081        public CreatePseudoNetwork(final TransitSchedule schedule, final Network network, final String networkIdPrefix, 
082                        final double linkFreeSpeed, final double linkCapacity) {
083                this.schedule = schedule;
084                this.network = network;
085                this.prefix = networkIdPrefix;
086                this.linkFreeSpeed = linkFreeSpeed;
087                this.linkCapacity = linkCapacity;
088        }
089
090        public void createNetwork() {
091
092                List<Tuple<TransitLine, TransitRoute>> toBeRemoved = new LinkedList<Tuple<TransitLine, TransitRoute>>();
093
094                for (TransitLine tLine : this.schedule.getTransitLines().values()) {
095                        for (TransitRoute tRoute : tLine.getRoutes().values()) {
096                                ArrayList<Id<Link>> routeLinks = new ArrayList<Id<Link>>();
097                                TransitRouteStop prevStop = null;
098                                for (TransitRouteStop stop : tRoute.getStops()) {
099                                        Link link = getNetworkLink(prevStop, stop);
100                                        routeLinks.add(link.getId());
101                                        prevStop = stop;
102                                }
103
104                                if (routeLinks.size() > 0) {
105                                        NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks, this.network);
106                                        tRoute.setRoute(route);
107                                } else {
108                                        System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule.");
109                                        toBeRemoved.add(new Tuple<TransitLine, TransitRoute>(tLine, tRoute));
110                                }
111                        }
112                }
113
114                for (Tuple<TransitLine, TransitRoute> remove : toBeRemoved) {
115                        remove.getFirst().removeRoute(remove.getSecond());
116                }
117        }
118
119        private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) {
120                TransitStopFacility fromFacility = (fromStop == null) ? toStop.getStopFacility() : fromStop.getStopFacility();
121                TransitStopFacility toFacility = toStop.getStopFacility();
122
123                Node fromNode = this.nodes.get(fromFacility);
124                if (fromNode == null) {
125                        fromNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), fromFacility.getCoord());
126                        this.network.addNode(fromNode);
127                        this.nodes.put(toFacility, fromNode);
128                }
129
130                Node toNode = this.nodes.get(toFacility);
131                if (toNode == null) {
132                        toNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), toFacility.getCoord());
133                        this.network.addNode(toNode);
134                        this.nodes.put(toFacility, toNode);
135                }
136
137                Tuple<Node, Node> connection = new Tuple<Node, Node>(fromNode, toNode);
138                Link link = this.links.get(connection);
139                if (link == null) {
140                        link = createAndAddLink(fromNode, toNode, connection);
141
142                        if (toFacility.getLinkId() == null) {
143                                toFacility.setLinkId(link.getId());
144                                this.stopFacilities.put(connection, toFacility);
145                        } else {
146                                List<TransitStopFacility> copies = this.facilityCopies.get(toFacility);
147                                if (copies == null) {
148                                        copies = new ArrayList<TransitStopFacility>();
149                                        this.facilityCopies.put(toFacility, copies);
150                                }
151                                Id<TransitStopFacility> newId = Id.create(toFacility.getId().toString() + "." + Integer.toString(copies.size() + 1), TransitStopFacility.class);
152                                TransitStopFacility newFacility = this.schedule.getFactory().createTransitStopFacility(newId, toFacility.getCoord(), toFacility.getIsBlockingLane());
153                                newFacility.setStopAreaId(Id.create(toFacility.getId(), TransitStopArea.class));
154                                newFacility.setLinkId(link.getId());
155                                newFacility.setName(toFacility.getName());
156                                copies.add(newFacility);
157                                this.nodes.put(newFacility, toNode);
158                                this.schedule.addStopFacility(newFacility);
159                                toStop.setStopFacility(newFacility);
160                                this.stopFacilities.put(connection, newFacility);
161                        }
162                } else {
163                        toStop.setStopFacility(this.stopFacilities.get(connection));
164                }
165                return link;
166        }
167
168        private Link createAndAddLink(Node fromNode, Node toNode,
169                        Tuple<Node, Node> connection) {
170                Link link;
171                link = this.network.getFactory().createLink(Id.create(this.prefix + this.linkIdCounter++, Link.class), fromNode, toNode);
172                if (fromNode == toNode) {
173                        link.setLength(50);
174                } else {
175                        link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord()));
176                }
177                link.setFreespeed(linkFreeSpeed);
178                link.setCapacity(linkCapacity);
179                link.setNumberOfLanes(1);
180                this.network.addLink(link);
181                link.setAllowedModes(this.transitModes);
182                this.links.put(connection, link);
183                return link;
184        }
185
186        public Link getLinkBetweenStops(final TransitStopFacility fromStop, final TransitStopFacility toStop) {
187                Node fromNode = this.nodes.get(fromStop);
188                Node toNode = this.nodes.get(toStop);
189                Tuple<Node, Node> connection = new Tuple<Node, Node>(fromNode, toNode);
190                return this.links.get(connection);
191        }
192
193}