//*************************************************************** // Copyright 2008 Centre For Advanced Spatial Analysis, UCL // // Author: Joel Dearden, University College London // // Contact: j.dearden@ucl.ac.uk // // Joel Dearden, // Centre for Advanced Spatial Analysis, // University College London, // 1-19 Torrington Place, // London, // WC1E 7HB // // // This file is part of PedTrace. // // PedTrace is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // PedTrace is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with PedTrace. If not, see . // //*************************************************************** using System; using System.Data; using System.Configuration; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; using System.Collections; using System.Drawing; namespace pedTrace { public class PTModel { public enum uniqueColorStage { start = 0, blueToPink, PinkToRed, RedToGreen, GreenToCyan, finish }; private int fTotalAgentsIn; private int fTotalAgentsOut; private ArrayList fFloors; private ArrayList fActivePedestrian; private ArrayList fArchivePedestrian; private ArrayList fLineBarrier; private ArrayList fFurniture; private ArrayList fSteps; private ArrayList fExitDoor; private ArrayList fRouteDoor; private DateTime fSL_StartTime; private PT2DExtents fModelExtents; private Pen fLineBarrierPen; private SolidBrush fFurnitureBrush; private SolidBrush fStepsBrush; private SolidBrush fRouteDoorBrush; private SolidBrush fExitDoorBrush; private int fTotalModelSeconds; private ArrayList fExitTimeGraphPoint; private Pen fGraphPen; private int fUniqueRedComponent; private int fUniqueGreenComponent; private int fUniqueBlueComponent; private uniqueColorStage fUniqueColorStage; private ArrayList fAverageSpeedGraphPoint; private float fAverageSpeedTotal; private int fAverageSpeedPedCount; public PTModel() { fSL_StartTime = DateTime.Now; fTotalAgentsIn = 0; fTotalAgentsOut = 0; fModelExtents = new PT2DExtents(); fFloors = new ArrayList(); fActivePedestrian = new ArrayList(); fArchivePedestrian = new ArrayList(); fLineBarrier = new ArrayList(); fFurniture = new ArrayList(); fSteps = new ArrayList(); fExitDoor = new ArrayList(); fRouteDoor = new ArrayList(); fLineBarrierPen = new Pen(Color.Black, 4); fFurnitureBrush = new SolidBrush(Color.Orange); fStepsBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 255)); fRouteDoorBrush = new SolidBrush(Color.Green); fExitDoorBrush = new SolidBrush(Color.Red); fTotalModelSeconds = 0; fExitTimeGraphPoint = new ArrayList(); fGraphPen = new Pen(Color.Black, 2); //start colour is blue fUniqueRedComponent = 0; fUniqueGreenComponent = 0; fUniqueBlueComponent = 255; fUniqueColorStage = uniqueColorStage.start; fAverageSpeedTotal = 0; fAverageSpeedPedCount = 0; fAverageSpeedGraphPoint = new ArrayList(); } public void pedestrianIn(string UUID, PTPoint p, double slTime) { fTotalAgentsIn++; fActivePedestrian.Add( new PTPedestrian( UUID, new PTTracePoint(p, slTime), uniquePedTraceColour() ) ); fModelExtents.addPoint(p); } private Color uniquePedTraceColour() { Color c; switch (fUniqueColorStage) { case uniqueColorStage.start: fUniqueColorStage++; c = Color.FromArgb( 128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; case uniqueColorStage.blueToPink: fUniqueRedComponent += globals.UNIQUE_COLOUR_STEPPING; if (fUniqueRedComponent >= 255) { fUniqueRedComponent = 255; fUniqueColorStage++; } c = Color.FromArgb(128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; case uniqueColorStage.PinkToRed: fUniqueBlueComponent -= globals.UNIQUE_COLOUR_STEPPING; if (fUniqueBlueComponent <= 0) { fUniqueBlueComponent = 0; fUniqueColorStage++; } c = Color.FromArgb(128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; case uniqueColorStage.RedToGreen: fUniqueRedComponent -= globals.UNIQUE_COLOUR_STEPPING; fUniqueGreenComponent += globals.UNIQUE_COLOUR_STEPPING; if( (fUniqueRedComponent <= 0) || (fUniqueGreenComponent >= 255)) { fUniqueRedComponent = 0; fUniqueGreenComponent = 255; fUniqueColorStage++; } c = Color.FromArgb( 128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; case uniqueColorStage.GreenToCyan: fUniqueBlueComponent += globals.UNIQUE_COLOUR_STEPPING; if (fUniqueBlueComponent >= 255) { fUniqueBlueComponent = 255; fUniqueColorStage++; } c = Color.FromArgb( 128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; case uniqueColorStage.finish: //restart cycle fUniqueColorStage = uniqueColorStage.start; fUniqueRedComponent = 0; fUniqueGreenComponent = 0; fUniqueBlueComponent = 255; c = Color.FromArgb( 128, fUniqueRedComponent, fUniqueGreenComponent, fUniqueBlueComponent ); break; default: throw new Exception("invalid unique colour stage"); } return c; } public void pedestrianUpdate(string UUID, PTPoint p, double slTime, float speed) { //update relevant pedestrian for (int i = 0; i < fActivePedestrian.Count; i++) { if (((PTPedestrian)fActivePedestrian[i]).UUIDMatch(UUID)) { ((PTPedestrian)fActivePedestrian[i]).addTracePoint(p, slTime); fModelExtents.addPoint(p); //add to average speed running total fAverageSpeedTotal += speed; fAverageSpeedPedCount++; return; } } //if we get to here we're trying to update a pedestrian that doesn't exist throw new Exception("No pedestrian with UUID=" + UUID.ToString()); } public void pedestrianOut(string UUID, PTPoint p, double slTime) { fTotalAgentsOut++; //update relevant pedestrian for (int i = 0; i < fActivePedestrian.Count; i++) { if (((PTPedestrian)fActivePedestrian[i]).UUIDMatch(UUID)) { ((PTPedestrian)fActivePedestrian[i]).addTracePoint(p, slTime); fModelExtents.addPoint(p); //now move this pedestrian to the archive list fArchivePedestrian.Add(fActivePedestrian[i]); fActivePedestrian.RemoveAt(i); return; } } //if we get to here we're trying to update a pedestrian that doesn't exist throw new Exception("No pedestrian with UUID=" + UUID.ToString()); } public void registerFloor(float zValue) { //check we don't already have this floor level (we only need to know about each one once) for (int i = 0; i < fFloors.Count; i++) { if (((PTFloor)fFloors[i]).getZValue() == zValue) { //already know about this floor - ignore new registration return; } } //if we get to here then we don't know about the registering floor yet - add it to the list if (fFloors.Count < globals.MAX_FLOORS) { fFloors.Add(new PTFloor(zValue)); } } public void registerSteps(string UUID, PTPoint p) { fSteps.Add(new PTStaticObject(UUID, p)); fModelExtents.addPoint(p); } public void registerExitDoor(string UUID, PTPoint p) { fExitDoor.Add(new PTStaticObject(UUID, p)); fModelExtents.addPoint(p); } public void registerRouteDoor(string UUID, PTPoint p) { fRouteDoor.Add(new PTStaticObject(UUID, p)); fModelExtents.addPoint(p); } public void registerLineBarrier(string UUID, PTPoint p, float orientation, float length) { fLineBarrier.Add(new PTLineBarrier(UUID, p, orientation, length)); fModelExtents.addPoint(p); } public void registerFurniture(string UUID, PTPoint p) { fFurniture.Add(new PTStaticObject(UUID, p)); fModelExtents.addPoint(p); } public void reset() { fSL_StartTime = DateTime.Now; fTotalAgentsIn = 0; fTotalAgentsOut = 0; fFloors.Clear(); fActivePedestrian.Clear(); fArchivePedestrian.Clear(); fLineBarrier.Clear(); fFurniture.Clear(); fSteps.Clear(); fExitDoor.Clear(); fRouteDoor.Clear(); fModelExtents = new PT2DExtents(); fTotalModelSeconds = 0; fExitTimeGraphPoint = new ArrayList(); //start colour is blue fUniqueRedComponent = 0; fUniqueGreenComponent = 0; fUniqueBlueComponent = 255; fUniqueColorStage = uniqueColorStage.start; fAverageSpeedTotal = 0; fAverageSpeedPedCount = 0; fAverageSpeedGraphPoint = new ArrayList(); } public DateTime getStartTime() { return fSL_StartTime; } public static PTPoint projectPointToImage(PTPoint p, PT2DExtents modelExtents, int imageSize) { PTPoint projected = new PTPoint(0, 0, 0); PTPoint modelCentre = modelExtents.getCentre(); float modelWidth = modelExtents.getWidth(); float modelHeight = modelExtents.getHeight(); projected.fX = ( ((p.fX - (modelCentre.fX - (modelWidth / 2))) / modelWidth) * (imageSize - (globals.DISPLAY_BORDER * 2)) ) + globals.DISPLAY_BORDER; projected.fY = ( ((p.fY - (modelCentre.fY - (modelHeight / 2))) / modelHeight) * (imageSize - (globals.DISPLAY_BORDER * 2)) ) + globals.DISPLAY_BORDER; //flip y projected.fY = imageSize - projected.fY; return projected; } public void renderModel(ref System.Drawing.Image targetImage, int floorIndex) { Graphics g = Graphics.FromImage(targetImage); //clear image g.Clear(Color.White); int i; PTPoint projectedA; PTPoint projectedB; //make sure floor index is valid if ((floorIndex < 0) || (floorIndex >= fFloors.Count)) { throw new Exception("invalid floor index=" + floorIndex.ToString()); } //render only entities on the specified floor PTFloor currentFloor = (PTFloor)fFloors[floorIndex]; //draw line barriers that are on the specified floor for (i = 0; i < fLineBarrier.Count; i++) { if (currentFloor.onFloor(((PTLineBarrier)fLineBarrier[i]).getPosition().fZ)) { projectedA = projectPointToImage( ((PTLineBarrier)fLineBarrier[i]).getStartPoint(), fModelExtents, globals.IMAGE_SIZE ); projectedB = projectPointToImage( ((PTLineBarrier)fLineBarrier[i]).getEndPoint(), fModelExtents, globals.IMAGE_SIZE ); //g.FillRectangle(fLineBarrierBrush, projectedA.fX, projectedA.fY, 4, 4); g.DrawLine(fLineBarrierPen, projectedA.fX, projectedA.fY, projectedB.fX, projectedB.fY); } } //draw furniture on the specified floor for (i = 0; i < fFurniture.Count; i++) { if (currentFloor.onFloor(((PTStaticObject)fFurniture[i]).getPosition().fZ)) { projectedA = projectPointToImage(((PTStaticObject)fFurniture[i]).getPosition(), fModelExtents, globals.IMAGE_SIZE); g.FillEllipse(fFurnitureBrush, projectedA.fX - 25, projectedA.fY - 25, 50, 50); } } //draw steps that are on the specified floor for (i = 0; i < fSteps.Count; i++) { if (currentFloor.onFloor(((PTStaticObject)fSteps[i]).getPosition().fZ)) { projectedA = projectPointToImage(((PTStaticObject)fSteps[i]).getPosition(), fModelExtents, globals.IMAGE_SIZE); g.FillRectangle(fStepsBrush, projectedA.fX - 20, projectedA.fY - 20, 40, 40); } } //draw exit doors that are on the specified floor for (i = 0; i < fExitDoor.Count; i++) { if (currentFloor.onFloor(((PTStaticObject)fExitDoor[i]).getPosition().fZ)) { projectedA = projectPointToImage(((PTStaticObject)fExitDoor[i]).getPosition(), fModelExtents, globals.IMAGE_SIZE); g.FillRectangle(fExitDoorBrush, projectedA.fX - 4, projectedA.fY - 4, 8, 8); } } //draw route doors that are on the specified floor for (i = 0; i < fRouteDoor.Count; i++) { if (currentFloor.onFloor(((PTStaticObject)fRouteDoor[i]).getPosition().fZ)) { projectedA = projectPointToImage(((PTStaticObject)fRouteDoor[i]).getPosition(), fModelExtents, globals.IMAGE_SIZE); g.FillRectangle(fRouteDoorBrush, projectedA.fX - 4, projectedA.fY - 4, 8, 8); } } //draw trace from every active pedestrian that is on the specified floor for (i = 0; i < fActivePedestrian.Count; i++) { ((PTPedestrian)fActivePedestrian[i]).renderTrace( ref g, fModelExtents, globals.IMAGE_SIZE, ref currentFloor, true ); } //draw trace from every archived pedestrian that is on the specified floor for (i = 0; i < fArchivePedestrian.Count; i++) { ((PTPedestrian)fArchivePedestrian[i]).renderTrace( ref g, fModelExtents, globals.IMAGE_SIZE, ref currentFloor, false ); } } public static DateTime slTimeToDateTime(double slTime) { //we don't care about milliseconds int roundedTime = System.Convert.ToInt32(slTime); //sl time is seconds since midnight... int hours = roundedTime % globals.SECONDS_PER_HOUR; int mins = (roundedTime - (hours * globals.SECONDS_PER_HOUR)) % globals.SECONDS_PER_MIN; int seconds = roundedTime - ((hours * globals.SECONDS_PER_HOUR) + (mins * globals.SECONDS_PER_MIN)); DateTime d = new DateTime( DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hours, mins, seconds ); return d; } public int getTotalAgentsIn() { return fTotalAgentsIn; } public int getTotalAgentsOut() { return fTotalAgentsOut; } public TimeSpan getRunTime() { TimeSpan ts = DateTime.Now - getStartTime(); return ts; } public int getTotalLineBarriers() { return fLineBarrier.Count; } public bool floorExists(int floorIndex) { return ((floorIndex >= 0) && (floorIndex < fFloors.Count)); } public int getModelSeconds() { return fTotalModelSeconds; } public void modelTick() { fTotalModelSeconds++; //calculate average speed for this model second if (fAverageSpeedPedCount == 0) { fAverageSpeedGraphPoint.Add(0.0F); } else { fAverageSpeedGraphPoint.Add((float)(fAverageSpeedTotal / fAverageSpeedPedCount)); } //reset for next second fAverageSpeedTotal = 0; fAverageSpeedPedCount = 0; //save exit time graph point fExitTimeGraphPoint.Add(fTotalAgentsOut); //render frame for animated gif System.Drawing.Image i; i = new Bitmap(globals.IMAGE_SIZE, globals.IMAGE_SIZE); for (int floorIndex = 0; floorIndex < globals.MAX_FLOORS; floorIndex++) { if (floorExists(floorIndex)) { renderModel(ref i, floorIndex); try { i.Save(globals.TRACE_MOVIE_DATA_PATH + globals.TRACE_MOVIE_FRAME_FILENAME + floorIndex.ToString() + "_" + fTotalModelSeconds.ToString() + globals.TRACE_MOVIE_FRAME_FILEEXTENSION); } catch (Exception) { //ignore } } } } public void renderAverageSpeedGraph(ref System.Drawing.Image targetImage) { Graphics g = Graphics.FromImage(targetImage); //clear image g.Clear(Color.White); if (fAverageSpeedGraphPoint.Count == 0) { //nothing to render return; } //draw "average walking speed" axis g.DrawLine(fGraphPen, globals.DISPLAY_BORDER, globals.DISPLAY_BORDER, globals.DISPLAY_BORDER, (globals.IMAGE_SIZE - globals.DISPLAY_BORDER) ); //draw "time" axis g.DrawLine(fGraphPen, globals.DISPLAY_BORDER, (globals.IMAGE_SIZE - globals.DISPLAY_BORDER), (globals.IMAGE_SIZE - globals.DISPLAY_BORDER), (globals.IMAGE_SIZE - globals.DISPLAY_BORDER) ); //label axes g.DrawString( "Average Walking Speed", new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(0, fAverageSpeedGraphPoint.Count), projectGraphYCoord(globals.MAX_DISPLAY_SPEED, globals.MAX_DISPLAY_SPEED) ); g.DrawString( "Time", new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(fAverageSpeedGraphPoint.Count, fAverageSpeedGraphPoint.Count), projectGraphYCoord(0, globals.MAX_DISPLAY_SPEED) ); for (int i = 0; i < globals.MAX_DISPLAY_SPEED; i+=5) { g.DrawString( i.ToString(), new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(0, fAverageSpeedGraphPoint.Count), projectGraphYCoord(i, globals.MAX_DISPLAY_SPEED) ); } for (int i = 0; i < fAverageSpeedGraphPoint.Count; i+=5) { g.DrawString( i.ToString(), new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(i, fAverageSpeedGraphPoint.Count), projectGraphYCoord(0, globals.MAX_DISPLAY_SPEED) ); } //draw all points for (int i = 1; i < fAverageSpeedGraphPoint.Count; i++) { g.DrawLine( fGraphPen, projectGraphXCoord(i - 1, fAverageSpeedGraphPoint.Count), projectGraphYCoord((float)fAverageSpeedGraphPoint[i - 1], globals.MAX_DISPLAY_SPEED), projectGraphXCoord(i, fAverageSpeedGraphPoint.Count), projectGraphYCoord((float)fAverageSpeedGraphPoint[i], globals.MAX_DISPLAY_SPEED) ); } } public void renderExitTimeGraph(ref System.Drawing.Image targetImage) { Graphics g = Graphics.FromImage(targetImage); //clear image g.Clear(Color.White); if ((fExitTimeGraphPoint.Count == 0) || (fTotalAgentsIn == 0)) { //nothing to render return; } //draw "agents out" axis g.DrawLine( fGraphPen, globals.DISPLAY_BORDER, globals.DISPLAY_BORDER, globals.DISPLAY_BORDER, (globals.IMAGE_SIZE - globals.DISPLAY_BORDER) ); //draw "time" axis g.DrawLine( fGraphPen, globals.DISPLAY_BORDER, (globals.IMAGE_SIZE - globals.DISPLAY_BORDER), (globals.IMAGE_SIZE - globals.DISPLAY_BORDER), (globals.IMAGE_SIZE - globals.DISPLAY_BORDER) ); //label axes g.DrawString( "Agents Out", new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(0, fExitTimeGraphPoint.Count), projectGraphYCoord(fTotalAgentsIn, fTotalAgentsIn) ); g.DrawString( "Time", new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(fExitTimeGraphPoint.Count, fExitTimeGraphPoint.Count), projectGraphYCoord(0, fTotalAgentsIn) ); for (int i = 0; i < fTotalAgentsIn; i+=5) { g.DrawString( i.ToString(), new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(0, fExitTimeGraphPoint.Count), projectGraphYCoord(i, fTotalAgentsIn) ); } for (int i = 0; i < fExitTimeGraphPoint.Count; i+=5) { g.DrawString( i.ToString(), new Font("Arial", 10), new SolidBrush(Color.Black), projectGraphXCoord(i, fExitTimeGraphPoint.Count), projectGraphYCoord(0, fTotalAgentsIn) ); } //draw all points for (int i = 1; i < fExitTimeGraphPoint.Count; i++) { g.DrawLine( fGraphPen, projectGraphXCoord(i - 1, fExitTimeGraphPoint.Count), projectGraphYCoord((int)fExitTimeGraphPoint[i - 1], fTotalAgentsIn), projectGraphXCoord(i, fExitTimeGraphPoint.Count), projectGraphYCoord((int)fExitTimeGraphPoint[i], fTotalAgentsIn) ); } } private int projectGraphXCoord(float xValue, float maxXValue) { if (maxXValue == 0) { //avoid divide by zero return globals.DISPLAY_BORDER; } int graphXWidth = globals.IMAGE_SIZE - (2 * globals.DISPLAY_BORDER); int graphSpaceX = (int) (((xValue / maxXValue) * graphXWidth) + globals.DISPLAY_BORDER); return graphSpaceX; } private int projectGraphYCoord(float yValue, float maxYValue) { if (maxYValue == 0) { //avoid divide by zero return globals.DISPLAY_BORDER; } int graphYWidth = globals.IMAGE_SIZE - (2 * globals.DISPLAY_BORDER); int graphSpaceY = (int) (((yValue / maxYValue) * graphYWidth) + globals.DISPLAY_BORDER); //flip value because rendering is top down graphSpaceY = globals.IMAGE_SIZE - graphSpaceY; return graphSpaceY; } } }