001/* *********************************************************************** * 002 * project: matsim 003 * AgentSnapshotInfoFactory.java 004 * * 005 * *********************************************************************** * 006 * * 007 * copyright : (C) 2010 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.vis.snapshotwriters; 022 023import org.matsim.api.core.v01.Coord; 024import org.matsim.api.core.v01.Id; 025import org.matsim.api.core.v01.network.Link; 026import org.matsim.api.core.v01.population.Person; 027import org.matsim.core.gbl.Gbl; 028 029/** 030 * translation of physical position (e.g. odometer distance on link, lane) into visualization position 031 * 032 * @author nagel 033 * 034 */ 035public class AgentSnapshotInfoFactory { 036 037 private static final double TWO_PI = 2.0 * Math.PI; 038 private static final double PI_HALF = Math.PI / 2.0; 039 private SnapshotLinkWidthCalculator linkWidthCalculator; 040 041 public AgentSnapshotInfoFactory(SnapshotLinkWidthCalculator widthCalculator) { 042 this.linkWidthCalculator = widthCalculator; 043 } 044 045 /** 046 * @param elevation 047 */ 048 @SuppressWarnings("static-method") 049 public AgentSnapshotInfo createAgentSnapshotInfo(Id<Person> agentId, double easting, double northing, double elevation, double azimuth) { 050 PositionInfo info = new PositionInfo() ; 051 info.setId( agentId ) ; 052 info.setEasting( easting ) ; 053 info.setNorthing( northing ) ; 054 info.setAzimuth( azimuth ) ; 055 return info ; 056 } 057 058 059 /** 060 * Generate snapshot info based on Link. 061 * 062 * Comments:<ul> 063 * <li>One could argue that this method should not know about Links at all, 064 * but it shortens code at several places, and since Link is a standard interface, I see no reason to not provide this 065 * as a service. 066 * </ul> 067 */ 068 public AgentSnapshotInfo createAgentSnapshotInfo(Id<Person> agentId, Link link, double distanceOnLink, int lane) { 069 PositionInfo info = new PositionInfo() ; 070 info.setId(agentId) ; 071 double lanePosition = this.linkWidthCalculator.calculateLanePosition(lane); 072 calculateAndSetPosition(info, link.getFromNode().getCoord(), link.getToNode().getCoord(), distanceOnLink, link.getLength(), lanePosition ); 073 return info; 074 } 075 076 /** 077 * creator based on Coord 078 * @param curveLength lengths are usually different (usually longer) than the euclidean distances between the startCoord and endCoord 079 */ 080 public AgentSnapshotInfo createAgentSnapshotInfo(Id<Person> agentId, Coord startCoord, Coord endCoord, double distanceOnLink, 081 Integer lane, double curveLength) { 082 PositionInfo info = new PositionInfo() ; 083 info.setId(agentId) ; 084 double lanePosition = this.linkWidthCalculator.calculateLanePosition(lane); 085 Gbl.assertNotNull( startCoord ); 086 Gbl.assertNotNull( endCoord ); 087 calculateAndSetPosition(info, startCoord, endCoord, distanceOnLink, curveLength, lanePosition) ; 088 return info; 089 } 090 091 092 /** 093 * 094 * @param lanePosition may be null 095 */ 096 private final static void calculateAndSetPosition(PositionInfo info, Coord startCoord, Coord endCoord, double odometerOnLink, 097 double lengthOfCurve, double lanePosition){ 098 099 double dx = -startCoord.getX() + endCoord.getX(); 100 double dy = -startCoord.getY() + endCoord.getY(); 101 double theta = 0.0; 102 if (dx > 0) { 103 theta = Math.atan(dy/dx); 104 } else if (dx < 0) { 105 theta = Math.PI + Math.atan(dy/dx); 106 } else { // i.e. DX==0 107 if (dy > 0) { 108 theta = PI_HALF; 109 } else if ( dy < 0 ) { 110 theta = -PI_HALF; 111 } else { // i.e. DX==0 && DY==0 112 theta = 0.833*Math.PI ; // some default direction towards north north east 113 } 114 } 115 if (theta < 0.0) theta += TWO_PI; 116 117 double euclideanLength = Math.sqrt( dx*dx + dy*dy ) ; 118 // since we already have two atan, two cos and two sin in the method, it seems to make little sense to save on the sqrt. (?) kai, apr'16 119 120 // "correction" is needed because link lengths are usually different (usually longer) than the Euklidean distances. 121 // For the visualization, however, the vehicles are distributed in a straight line between the end points. 122 // Since the simulation, on the other hand, reports odometer distances, this needs to be corrected. kai, apr'10 123 // 124 // The same correction can be used for the orthogonal offsets. kai, aug'10 125 // That did not work so well in some cases. Since the link width also seems to be plotted without that correction, let's 126 // try to also do the "distance from link" without correction. kai, feb'13 127 double correction = 0. ; 128 if ( lengthOfCurve != 0 ){ 129 correction = euclideanLength / lengthOfCurve; 130 } 131 132 info.setEasting( startCoord.getX() 133 + (Math.cos(theta) * odometerOnLink * correction) 134 + (Math.sin(theta) * lanePosition ) ) ; 135 136 info.setNorthing( startCoord.getY() 137 + Math.sin(theta) * odometerOnLink * correction 138 - Math.cos(theta) * lanePosition ); 139 140 info.setAzimuth( theta / TWO_PI * 360. ) ; 141 } 142 143 /** 144 * A helper class to store information about agents (id, position, speed), mainly used to create 145 * {@link SnapshotWriter snapshots}. It also provides a way to convert graph coordinates (linkId, offset) into 146 * Euclidean coordinates. Also does some additional coordinate shifting (e.g. to the "right") to improve visualization. 147 * In contrast to earlier versions of this comment, it does _not_ define a physical position of particles in the queue model; 148 * that functionality needs to be provided elsewhere. 149 * 150 * @author mrieser, knagel 151 */ 152 private static class PositionInfo implements AgentSnapshotInfo { 153 154 private Id<Person> agentId = null; 155 private double easting = Double.NaN; 156 private double northing = Double.NaN; 157 private double azimuth = Double.NaN; 158 private double colorValue = 0; 159 private AgentState agentState = null; 160 private Id<Link> linkId = null; 161 private int user = 0; 162 163 /* package-private */ PositionInfo() { } 164 165 @Override 166 public final Id<Person> getId() { 167 return this.agentId; 168 } 169 public final void setId( Id<Person> tmp ) { 170 this.agentId = tmp ; 171 } 172 173 @Override 174 public final double getEasting() { 175 return this.easting; 176 } 177 public final void setEasting( double tmp ) { 178 this.easting = tmp ; 179 } 180 181 @Override 182 public final double getNorthing() { 183 return this.northing; 184 } 185 public final void setNorthing( double tmp ) { 186 this.northing = tmp ; 187 } 188 189 @Override 190 public final double getAzimuth() { 191 return this.azimuth; 192 } 193 public final void setAzimuth( double tmp ) { 194 this.azimuth = tmp ; 195 } 196 197 @Override 198 public final double getColorValueBetweenZeroAndOne() { 199 return this.colorValue; 200 } 201 @Override 202 public final void setColorValueBetweenZeroAndOne( double tmp ) { 203 this.colorValue = tmp ; 204 } 205 206 @Override 207 public final AgentState getAgentState(){ 208 return this.agentState; 209 } 210 @Override 211 public final void setAgentState( AgentState state ) { 212 this.agentState = state ; 213 } 214 215 public final Id<Link> getLinkId() { 216 return this.linkId; 217 } 218 public final void setLinkId( Id<Link> tmp ) { 219 this.linkId = tmp ; 220 } 221 222 @Override 223 public int getUserDefined() { 224 return this.user; 225 } 226 @Override 227 public void setUserDefined( int tmp ) { 228 this.user = tmp ; 229 } 230 231 @Override 232 public String toString() { 233 return "PositionInfo; agentId: " + this.agentId.toString() 234 + " easting: " + this.easting 235 + " northing: " + this.northing ; 236 } 237 238 } 239 240}