package org.opentrafficsim.demo.ntm;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.djunits.unit.DurationUnit;
import org.djunits.unit.FrequencyUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.unit.TimeUnit;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Frequency;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.Time;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.opentrafficsim.demo.ntm.NTMNode.TrafficBehaviourType;
import org.opentrafficsim.demo.ntm.trafficdemand.DepartureTimeProfile;
import org.opentrafficsim.demo.ntm.trafficdemand.FractionOfTripDemandByTimeSegment;
import org.opentrafficsim.demo.ntm.trafficdemand.TripDemand;
import org.opentrafficsim.demo.ntm.trafficdemand.TripInfoTimeDynamic;
/**
* A Cell extends a Zone and is used for the NetworkTransmissionModel The Cells cover a preferably homogeneous area and have
* their specific characteristics such as their free speed, a capacity and an NFD diagram A trip matrix quantifies the amount of
* trips between Cells in a network. The connection of neighbouring Cells are expressed by Links (connectors) The cost to go
* from one to another Cell is quantified through the weights on the Connectors
*
*
* Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* $LastChangedDate$, @version $Revision$, by $Author$,
* initial version 4 Sep 2014
* @author Guus Tamminga
*/
public class CsvFileReader
{
/**
* Class reads the demand and others from .csv type of files.
* @param csvFileName String; name of file
* @param csvSplitBy String; : token that defines how to split a line
* @param csvSplitByTwo String; : two tokens that defines how to split a line
* @param centroids Map<String,NTMNode>; sources of traffic
* @param links Map<String,NTMLink>; roads
* @param connectors Map<String,NTMLink>; artificial roads connecting the links and centroids
* @param settingsNTM NTMSettings; the parameters of the NTM
* @param profiles ArrayList<DepartureTimeProfile>; departure profile of Trips
* @param areas Map<String,Area>; the NTM model areas
* @return the TripDemand (nested LinkedHashMap: >
* @throws IOException
* @throws Throwable
*/
public static TripDemand readOmnitransExportDemand(final NTMModel model, final String csvFileName,
final String csvSplitBy, final String csvSplitByTwo, final Map centroids,
final Map links, final Map connectors, final NTMSettings settingsNTM,
final ArrayList profiles, final Map areas) throws Throwable
{
BufferedReader bufferedReader = null;
String line = "";
URL url;
if (new File(csvFileName).canRead())
{
url = new File(csvFileName).toURI().toURL();
}
else
{
url = ShapeFileReader.class.getResource(csvFileName);
}
String path = url.getPath();
TripDemand tripDemand = new TripDemand();
Map> demand = new LinkedHashMap<>();
Map centroidsAndCordonConnectors = new LinkedHashMap<>();
try
{
bufferedReader = new BufferedReader(new FileReader(path));
// read the first line of the demand file from Omnitrans
// this line contains the time period of the demand file: as an example....
// TimePeriod: 07:00:00 - 09:00:00
if ((line = bufferedReader.readLine()) != null)
{
String[] timePeriod = line.split(csvSplitByTwo);
int counter = 0;
for (String name : timePeriod)
{
if (name.length() > 0)
{
if (name.charAt(2) == ':')
{
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH);
Calendar timeInstance = Calendar.getInstance();
timeInstance.setTime(sdf.parse(name));
if (counter == 0)
{
Time startTime = new Time(timeInstance.getTimeInMillis() / 1000, TimeUnit.BASE_SECOND);
tripDemand.setStartTime(startTime);
settingsNTM.setDurationSinceMidnight(new Duration(startTime.getSI(), DurationUnit.SI));
}
else if (counter == 1)
{
Time endTime = new Time(timeInstance.getTimeInMillis() / 1000, TimeUnit.BASE_SECOND);
Duration timeSpan = endTime.minus(tripDemand.getStartTime());
tripDemand.setTimeSpan(timeSpan);
settingsNTM.setDurationOfSimulation(timeSpan);
}
counter++;
}
}
}
}
// read the second line of the demand file from Omnitrans
// this line contains the destinations: put them in the array
// The internal centroids start wit a capital "C",
// in case of a subarea model, other centroids are the nodes that are at the cordon of the subarea. They
// have the link number plus (sometimes) a name of the road
// "Links + Centroids";"3569";"11212";"95014";"95608";"116117";"116738";...................";
// ..... "563089";"563430";"C1";"C2";"C3";"C4";"C5";"C6";".........."
LinkedHashMap orderedZones = new LinkedHashMap();
if ((line = bufferedReader.readLine()) != null)
{
String[] namesZone = line.split(csvSplitBy);
int index = 0;
for (String name : namesZone)
{
// first we inspect if it is a centroid
name = CsvFileReader.removeQuotes(name);
String nameBA = name + "_BA";
boolean isCentroid = ShapeFileReader.inspectNodeCentroid(name);
NTMNode cordonPoint = null;
if (isCentroid)
{
centroidsAndCordonConnectors.put(name, centroids.get(name));
orderedZones.put(index, name);
}
// otherwise it is a cordon link: detect the "zoneConnector" node (at the cordon)
// this is often a dangling link, but not always!
// we add the Node of the cordon Link to the "centroidsAndCordonConnectors"
else if (links.get(name) != null || connectors.get(name) != null || links.get(nameBA) != null
|| connectors.get(nameBA) != null)
{
if (links.get(name) == null && connectors.get(name) == null)
{
name = nameBA;
}
NTMLink cordonConnector = null;
boolean createArea = false;
if (links.get(name) != null)
{
cordonConnector = links.get(name);
createArea = true;
// remove cordonLink from normal links to connectors!!
links.remove(cordonConnector.getId());
cordonConnector.setBehaviourType(TrafficBehaviourType.CORDON);
connectors.put(name, cordonConnector);
}
else if (connectors.get(name) != null)
{
// link 516821 is gekoppeld aan zone C203, maar in de matrix als cordonzone benoemd:
// TODO corrigeren
System.out.println("Connector already defined: probably a zoneconnector cut by the cordon");
cordonConnector = connectors.get(name);
createArea = true;
}
else
{
System.out.println("Strange: no connector found ??????????!!!!");
}
NTMNode nodeA = (NTMNode) cordonConnector.getStartNode();
NTMNode nodeB = (NTMNode) cordonConnector.getEndNode();
int countedNodesA = 0;
int countedNodesB = 0;
for (NTMLink link : links.values())
{
if (link.getStartNode().equals(nodeA))
{
countedNodesA++;
}
if (link.getStartNode().equals(nodeB))
{
countedNodesB++;
}
if (link.getEndNode().equals(nodeA))
{
countedNodesA++;
}
if (link.getEndNode().equals(nodeB))
{
countedNodesB++;
}
}
if (countedNodesA > countedNodesB)
{
NTMNode node = null;
// there could be more connectors attached to this node. If so, create a new Node
if (centroidsAndCordonConnectors.get(nodeB.getId()) != null)
{
double x = nodeB.getPoint().x + 3;
double y = nodeB.getPoint().y + 3;
Coordinate point = NTMNode.createPoint(x, y);
String nr = nodeB.getId() + "_" + nodeA.getId();
node = new NTMNode(model.getNetwork(), nr, point, TrafficBehaviourType.CORDON);
centroids.put(nr, node);
centroidsAndCordonConnectors.put(node.getId(), node);
orderedZones.put(index, node.getId());
cordonPoint = node;
connectors.remove(cordonConnector);
cordonConnector = new NTMLink(model.getNetwork(), model.getSimulator(),
cordonConnector.getDesignLine(), cordonConnector.getId(), cordonConnector.getLength(),
(NTMNode) cordonConnector.getStartNode(), node, cordonConnector.getFreeSpeed(),
cordonConnector.getDuration(), cordonConnector.getCapacity(),
cordonConnector.getBehaviourType(), cordonConnector.getLinkData());
connectors.put(name, cordonConnector);
}
else
{
nodeB.setBehaviourType(TrafficBehaviourType.CORDON);
centroidsAndCordonConnectors.put(nodeB.getId(), nodeB);
orderedZones.put(index, nodeB.getId());
cordonPoint = nodeB;
centroids.put(cordonPoint.getId(), cordonPoint);
}
}
else
{
NTMNode node = null;
// there could be more connectors attached to this node. If so, create a new Node
if (centroidsAndCordonConnectors.get(nodeA.getId()) != null)
{
double x = nodeA.getPoint().x + 3;
double y = nodeA.getPoint().y + 3;
Coordinate point = NTMNode.createPoint(x, y);
String nr = nodeA.getId() + "_" + nodeB.getId();
node = new NTMNode(model.getNetwork(), nr, point, TrafficBehaviourType.CORDON);
centroids.put(nr, node);
centroidsAndCordonConnectors.put(node.getId(), node);
orderedZones.put(index, node.getId());
cordonPoint = node;
connectors.remove(cordonConnector);
cordonConnector = new NTMLink(model.getNetwork(), model.getSimulator(),
cordonConnector.getDesignLine(), cordonConnector.getId(), cordonConnector.getLength(),
node, (NTMNode) cordonConnector.getEndNode(), cordonConnector.getFreeSpeed(),
cordonConnector.getDuration(), cordonConnector.getCapacity(),
cordonConnector.getBehaviourType(), cordonConnector.getLinkData());
connectors.put(name, cordonConnector);
}
else
{
nodeA.setBehaviourType(TrafficBehaviourType.CORDON);
centroidsAndCordonConnectors.put(nodeA.getId(), nodeA);
orderedZones.put(index, nodeA.getId());
cordonPoint = nodeA;
centroids.put(cordonPoint.getId(), cordonPoint);
}
}
if (createArea)
{
// after determining the new cordon centroid, a new area is created around this feeding
// link. This becomes a feeder type of area
// Geometry buffer = cordonConnector.getStartNode().getPoint().getCoordinate().buffer(40);
GeometryFactory factory = new GeometryFactory();
Geometry point =
factory.createPoint(new Coordinate(cordonPoint.getPoint().x, cordonPoint.getPoint().y));
Geometry buffer = point.buffer(40);
Coordinate centroid = cordonPoint.getPoint().getCoordinate();
String nr = cordonPoint.getId();
String newName = cordonConnector.getLinkData().getName();
String gemeente = cordonConnector.getLinkData().getName();
String gebied = cordonConnector.getLinkData().getName();
String regio = "cordonPoint " + nr;
double dhb = 0.0;
Double increaseDemandByFactor = settingsNTM.getScalingFactorDemand();
double accCritMaxCapStart = 25;
double accCritMaxCapEnd = 50;
double accCritJam = 100;
ArrayList accCritical = new ArrayList();
accCritical.add(accCritMaxCapStart);
accCritical.add(accCritMaxCapEnd);
accCritical.add(accCritJam);
ParametersNTM parametersNTM = new ParametersNTM(accCritical);
Area area = new Area(buffer, nr, newName, gemeente, gebied, regio, dhb, centroid,
TrafficBehaviourType.CORDON, new Length(0, LengthUnit.METER),
new Speed(0, SpeedUnit.KM_PER_HOUR), increaseDemandByFactor, parametersNTM);
areas.put(nr, area);
}
}
else if (name.contentEquals("Links + Centroids"))
{
continue;
}
else
{
System.out.println("Strange: no connector found!!!!");
continue;
}
index++;
}
}
// after these preparations, the tripdata are being read
// only values > 0 are recorded!
// then, read all other lines: first column contains the name of the origin
// this can be either a link or a centroid (starts with "C")
int indexRow = 0;
while ((line = bufferedReader.readLine()) != null && !line.isEmpty())
{
Map tripDemandRow = new LinkedHashMap();
String[] tripData = line.split(csvSplitBy);
boolean firstElement = true;
NTMNode origin = null;
String originLinknr = null;
int indexColumn = 0;
for (String numberOfTrips : tripData)
{
numberOfTrips = removeQuotes(numberOfTrips);
if (firstElement)
{
// System.out.println("I " + indexRow + " name: "
// + centroidsAndCordonConnectors.get(orderedZones.get(indexRow)).getId());
origin = centroidsAndCordonConnectors.get(orderedZones.get(indexRow));
originLinknr = numberOfTrips;
firstElement = false;
}
else
{
numberOfTrips = removeQuotes(numberOfTrips);
// only the non-zero cells
if (Double.parseDouble(numberOfTrips) > 0.0)
{
// TODO now we simply take the first time profile.
// This should be input in file: which profile is connected to which OD pair
DepartureTimeProfile profile = profiles.get(0);
Time startSimulationTimeSinceMidnight =
new Time(settingsNTM.getDurationSinceMidnight().getSI(), TimeUnit.DEFAULT);
NavigableMap