/** * \file Analysis.cpp * \date Oct 10, 2014 * \version v0.7 * \copyright <2009-2016> Forschungszentrum Juelich GmbH. All rights reserved. * * \section License * This file is part of JuPedSim. * * JuPedSim is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * JuPedSim 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 Lesser General Public License * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. * * \section Description * The Analysis class represents a process of analyzing groups of pedestrian * trajectories from experiment or simulation. Different measurement methods * can be used and are defined by various parameters and functions. * * **/ #include "Analysis.h" #include "geometry/Room.h" #include "geometry/SubRoom.h" #include "methods/VoronoiDiagram.h" #include "methods/Method_A.h" #include "methods/Method_B.h" #include "methods/Method_C.h" #include "methods/Method_D.h" #include "methods/Method_I.h" #include "methods/PedData.h" #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <cfloat> #include <stdlib.h> #include <algorithm> // std::min_element, std::max_element #ifdef __linux__ #include <sys/stat.h> #include <dirent.h> #elif __APPLE__ #include <sys/stat.h> #include <dirent.h> #else #include <direct.h> #endif using boost::geometry::dsv; using namespace std; OutputHandler* Log = new STDIOHandler(); /************************************************ // Konstruktoren ************************************************/ Analysis::Analysis() { _building = NULL; _projectRootDir=""; _deltaF=5; // half of the time interval that used to calculate instantaneous velocity of ped i. Here v_i = (X(t+deltaF) - X(t+deltaF))/(2*deltaF). X is location. _DoesUseMethodA = false; // Method A (Zhang2011a) _DoesUseMethodB = false; // Method B (Zhang2011a) _DoesUseMethodC = false; // Method C //calculate and save results of classic in separate file _DoesUseMethodD = false; // Method D--Voronoi method _cutByCircle = false; //Adjust whether cut each original voronoi cell by a circle _getProfile = false; // Whether make field analysis or not _outputGraph = false; // Whether output the data for plot the fundamental diagram each frame _calcIndividualFD = false; //Adjust whether analyze the individual density and velocity of each pedestrian in stationary state (ALWAYS VORONOI-BASED) _vComponent = "B"; // to mark whether x, y or x and y coordinate are used when calculating the velocity _IgnoreBackwardMovement = false; _grid_size_X = 0.10; // the size of the grid _grid_size_Y = 0.10; _lowVertexX = 0;// LOWest vertex of the geometry (x coordinate) _lowVertexY = 0; // LOWest vertex of the geometry (y coordinate) _highVertexX = 10; // Highest vertex of the geometry _highVertexY = 10; _cutRadius=1.0; _circleEdges=6; _trajFormat=FileFormat::FORMAT_PLAIN; _isOneDimensional=false; _plotGraph=false; } Analysis::~Analysis() { delete _building; } // file.txt ---> file std::string Analysis::GetBasename (const std::string& str) { std::cout << "Splitting: " << str << '\n'; unsigned found = str.find_last_of("."); return str.substr(0,found); } // c:\\windows\\winhelp.exe ---> winhelp.exe std::string Analysis::GetFilename (const std::string& str) { std::cout << "GetFilename: " << str << '\n'; unsigned found = str.find_last_of("/\\"); return str.substr(found+1); } void Analysis::InitArgs(ArgumentParser* args) { string s = "Parameter:\n"; _building = new Building(); _building->LoadGeometry(args->GetGeometryFilename()); // create the polygons _building->InitGeometry(); // _building->AddSurroundingRoom(); if(args->GetIsMethodA()) { _DoesUseMethodA = true; vector<int> Measurement_Area_IDs = args->GetAreaIDforMethodA(); for(unsigned int i=0; i<Measurement_Area_IDs.size(); i++) { _areaForMethod_A.push_back(dynamic_cast<MeasurementArea_L*>( args->GetMeasurementArea(Measurement_Area_IDs[i]))); } _deltaT = args->GetTimeIntervalA(); _plotTimeseriesA=args->GetIsPlotTimeSeriesA(); } if(args->GetIsMethodB()) { _DoesUseMethodB = true; vector<int> Measurement_Area_IDs = args->GetAreaIDforMethodB(); for(unsigned int i=0; i<Measurement_Area_IDs.size(); i++) { _areaForMethod_B.push_back(dynamic_cast<MeasurementArea_B*>( args->GetMeasurementArea(Measurement_Area_IDs[i]))); } } if(args->GetIsMethodC()) { _DoesUseMethodC = true; vector<int> Measurement_Area_IDs = args->GetAreaIDforMethodC(); for(unsigned int i=0; i<Measurement_Area_IDs.size(); i++) { _areaForMethod_C.push_back(dynamic_cast<MeasurementArea_B*>( args->GetMeasurementArea(Measurement_Area_IDs[i]))); } _plotTimeseriesC=args->GetIsPlotTimeSeriesC(); } if(args->GetIsMethodD()) { _DoesUseMethodD = true; vector<int> Measurement_Area_IDs = args->GetAreaIDforMethodD(); for(unsigned int i=0; i<Measurement_Area_IDs.size(); i++) { _areaForMethod_D.push_back(dynamic_cast<MeasurementArea_B*>( args->GetMeasurementArea(Measurement_Area_IDs[i]))); } _StartFramesMethodD = args->GetStartFramesMethodD(); _StopFramesMethodD = args->GetStopFramesMethodD(); _IndividualFDFlags = args->GetIndividualFDFlags(); _plotTimeseriesD=args->GetIsPlotTimeSeriesD(); _geoPolyMethodD = ReadGeometry(args->GetGeometryFilename(), _areaForMethod_D); } if(args->GetIsMethodI()) { _DoesUseMethodI = true; vector<int> Measurement_Area_IDs = args->GetAreaIDforMethodI(); for(unsigned int i=0; i<Measurement_Area_IDs.size(); i++) { _areaForMethod_I.push_back(dynamic_cast<MeasurementArea_B*>( args->GetMeasurementArea(Measurement_Area_IDs[i]))); } _StartFramesMethodI = args->GetStartFramesMethodI(); _StopFramesMethodI = args->GetStopFramesMethodI(); _IndividualFDFlags = args->GetIndividualFDFlags(); _plotTimeseriesI=args->GetIsPlotTimeSeriesI(); _geoPolyMethodI = ReadGeometry(args->GetGeometryFilename(), _areaForMethod_I); } // ToDo: obsolete ? // if( _DoesUseMethodD && _DoesUseMethodI) // { // Log->Write("Warning:\t Using both method D and I is not safe!"); // // because ReadGeometry() may be called twice (line 169 and 182) // // overwrite _geoPoly -> new value for each Method // Log->Write("Info:\t Using both method D and I is safe! :-)"); // } _deltaF = args->GetDelatT_Vins(); _cutByCircle = args->GetIsCutByCircle(); _getProfile = args->GetIsGetProfile(); _outputGraph = args->GetIsOutputGraph(); _plotGraph = args->GetIsPlotGraph(); _plotIndex = args->GetIsPlotIndex(); _isOneDimensional=args->GetIsOneDimensional(); _vComponent = args->GetVComponent(); _IgnoreBackwardMovement =args->GetIgnoreBackwardMovement(); _grid_size_X = int(args->GetGridSizeX()); _grid_size_Y = int(args->GetGridSizeY()); _geometryFileName=args->GetGeometryFilename(); _projectRootDir=args->GetProjectRootDir(); _trajFormat=args->GetFileFormat(); _cutRadius=args->GetCutRadius(); _circleEdges=args->GetCircleEdges(); _scriptsLocation=args->GetScriptsLocation(); _outputLocation=args->GetOutputLocation(); } std::map<int, polygon_2d> Analysis::ReadGeometry(const fs::path& geometryFile, const std::vector<MeasurementArea_B*>& areas) { Log->Write("INFO:\tReadGeometry with %s", geometryFile.string().c_str()); double geo_minX = FLT_MAX; double geo_minY = FLT_MAX; double geo_maxX = -FLT_MAX; double geo_maxY = -FLT_MAX; std::map<int, polygon_2d> geoPoly; //loop over all areas for(auto&& area: areas) { //search for the subroom that contains that area for (auto&& it_room : _building->GetAllRooms()) { for (auto&& it_sub : it_room.second->GetAllSubRooms()) { SubRoom* subroom = it_sub.second.get(); point_2d point(0,0); boost::geometry::centroid(area->_poly,point); //check if the area is contained in the obstacle if(subroom->IsInSubRoom(Point(point.x()/M2CM,point.y()/M2CM))) { for (auto&& tmp_point : subroom->GetPolygon()) { append(geoPoly[area->_id], make<point_2d>(tmp_point._x*M2CM, tmp_point._y*M2CM)); geo_minX = (tmp_point._x*M2CM<=geo_minX) ? (tmp_point._x*M2CM) : geo_minX; geo_minY = (tmp_point._y*M2CM<=geo_minY) ? (tmp_point._y*M2CM) : geo_minY; geo_maxX = (tmp_point._x*M2CM>=geo_maxX) ? (tmp_point._x*M2CM) : geo_maxX; geo_maxY = (tmp_point._y*M2CM>=geo_maxY) ? (tmp_point._y*M2CM) : geo_maxY; } correct(geoPoly[area->_id]); //cout<<"this is:\t"<<subroom->GetAllObstacles().size()<<endl; //appen the holes/obstacles if any int k=1; for(auto&& obst: subroom->GetAllObstacles()) { geoPoly[area->_id].inners().resize(k++); geoPoly[area->_id].inners().back(); model::ring<point_2d>& inner = geoPoly[area->_id].inners().back(); for(auto&& tmp_point:obst->GetPolygon()) { append(inner, make<point_2d>(tmp_point._x*M2CM, tmp_point._y*M2CM)); } correct(geoPoly[area->_id]); } } } }//room if(geoPoly.count(area->_id)==0) { Log->Write("ERROR: \t No polygon containing the measurement id [%d]", area->_id); geoPoly[area->_id] = area->_poly; } } _highVertexX = geo_maxX; _highVertexY = geo_maxY; _lowVertexX = geo_minX; _lowVertexY = geo_minY; return geoPoly; } int Analysis::RunAnalysis(const fs::path& filename, const fs::path& path) { PedData data; if(data.ReadData(_projectRootDir, _outputLocation, path, filename, _trajFormat, _deltaF, _vComponent, _IgnoreBackwardMovement)==false) { Log->Write("ERROR:\tCould not parse the file %d", filename.c_str()); return -1; } //-----------------------------check whether there is pedestrian outside the whole geometry-------------------------------------------- std::map<int , std::vector<int> > _peds_t=data.GetPedsFrame(); for(int frameNr = 0; frameNr < data.GetNumFrames(); frameNr++ ) { vector<int> ids=_peds_t[frameNr]; vector<int> IdInFrame = data.GetIdInFrame(frameNr, ids); vector<double> XInFrame = data.GetXInFrame(frameNr, ids); vector<double> YInFrame = data.GetYInFrame(frameNr, ids); for( unsigned int i=0;i<IdInFrame.size();i++) { bool IsInBuilding=false; for (auto&& it_room : _building->GetAllRooms()) { for (auto&& it_sub : it_room.second->GetAllSubRooms()) { SubRoom* subroom = it_sub.second.get(); if(subroom->IsInSubRoom(Point(XInFrame[i]*CMtoM,YInFrame[i]*CMtoM))) { IsInBuilding=true; break; } } if(IsInBuilding) { break; } } if(false==IsInBuilding) { Log->Write("Warning:\tAt %dth frame pedestrian at <x=%.4f, y=%.4f> is not in geometry!", frameNr+data.GetMinFrame(), XInFrame[i]*CMtoM, YInFrame[i]*CMtoM ); } } } //----------------------------------------------------------------------------------------------------------------------------------------------------------------- if(_DoesUseMethodA) //Method A { if(_areaForMethod_A.empty()) { Log->Write("ERROR: Method A selected with no measurement area!"); exit(EXIT_FAILURE); } #pragma omp parallel for for(long unsigned int i=0; i < _areaForMethod_A.size(); i++) { Method_A method_A ; method_A.SetMeasurementArea(_areaForMethod_A[i]); method_A.SetTimeInterval(_deltaT[i]); method_A.SetPlotTimeSeries(_plotTimeseriesA[i]); bool result_A=method_A.Process(data,_scriptsLocation,_areaForMethod_A[i]->_zPos); if(result_A) { Log->Write("INFO:\tSuccess with Method A using measurement area id %d!\n",_areaForMethod_A[i]->_id); std::cout << "INFO:\tSuccess with Method A using measurement area id "<< _areaForMethod_A[i]->_id << "\n"; } else { Log->Write("INFO:\tFailed with Method A using measurement area id %d!\n",_areaForMethod_A[i]->_id); } } } if(_DoesUseMethodB) //Method_B { if(_areaForMethod_B.empty()) { Log->Write("ERROR: Method B selected with no measurement area!"); exit(EXIT_FAILURE); } #pragma omp parallel for for(long unsigned int i=0; i < _areaForMethod_B.size(); i++) { Method_B method_B; method_B.SetMeasurementArea(_areaForMethod_B[i]); bool result_B = method_B.Process(data); if(result_B) { Log->Write("INFO:\tSuccess with Method B using measurement area id %d!\n",_areaForMethod_B[i]->_id); std::cout << "INFO:\tSuccess with Method B using measurement area id "<< _areaForMethod_B[i]->_id << "\n"; } else { Log->Write("INFO:\tFailed with Method B using measurement area id %d!\n",_areaForMethod_B[i]->_id); } } } if(_DoesUseMethodC) //Method C { if(_areaForMethod_C.empty()) { Log->Write("ERROR: Method C selected with no measurement area!"); exit(EXIT_FAILURE); } #pragma omp parallel for for(long unsigned int i=0; i < _areaForMethod_C.size(); i++) { Method_C method_C; method_C.SetMeasurementArea(_areaForMethod_C[i]); bool result_C =method_C.Process(data,_areaForMethod_C[i]->_zPos); if(result_C) { Log->Write("INFO:\tSuccess with Method C using measurement area id %d!\n",_areaForMethod_C[i]->_id); std::cout << "INFO:\tSuccess with Method C using measurement area id "<< _areaForMethod_C[i]->_id << "\n"; if(_plotTimeseriesC[i]) { string parameters_Timeseries=" " + _scriptsLocation.string()+ "/_Plot_timeseries_rho_v.py -p "+ _projectRootDir.string()+VORO_LOCATION + " -n "+filename.string()+ " -f "+boost::lexical_cast<std::string>(data.GetFps()); parameters_Timeseries = PYTHON + parameters_Timeseries; int res=system(parameters_Timeseries.c_str()); Log->Write("INFO:\t time series result: %d ",res); } } else { Log->Write("INFO:\tFailed with Method C using measurement area id %d!\n",_areaForMethod_C[i]->_id); } } } if(_DoesUseMethodD) //method_D { if(_areaForMethod_D.empty()) { Log->Write("ERROR: Method D selected with no measurement area!"); exit(EXIT_FAILURE); } #pragma omp parallel for for(long unsigned int i=0; i<_areaForMethod_D.size(); i++) { Method_D method_D; method_D.SetStartFrame(_StartFramesMethodD[i]); method_D.SetStopFrame(_StopFramesMethodD[i]); method_D.SetCalculateIndividualFD(_IndividualFDFlags[i]); method_D.SetGeometryPolygon(_geoPolyMethodD[_areaForMethod_D[i]->_id]); method_D.SetGeometryFileName(_geometryFileName); method_D.SetGeometryBoundaries(_lowVertexX, _lowVertexY, _highVertexX, _highVertexY); method_D.SetGridSize(_grid_size_X, _grid_size_Y); method_D.SetOutputVoronoiCellData(_outputGraph); method_D.SetPlotVoronoiGraph(_plotGraph); method_D.SetPlotVoronoiIndex(_plotIndex); method_D.SetDimensional(_isOneDimensional); method_D.SetCalculateProfiles(_getProfile); method_D.SetTrajectoriesLocation(path); if(_cutByCircle) { method_D.Setcutbycircle(_cutRadius, _circleEdges); } method_D.SetMeasurementArea(_areaForMethod_D[i]); bool result_D = method_D.Process(data,_scriptsLocation, _areaForMethod_D[i]->_zPos); if(result_D) { Log->Write("INFO:\tSuccess with Method D using measurement area id %d!\n",_areaForMethod_D[i]->_id); std::cout << "INFO:\tSuccess with Method D using measurement area id "<< _areaForMethod_D[i]->_id << "\n"; if(_plotTimeseriesD[i]) { string parameters_Timeseries= " " +_scriptsLocation.string()+"/_Plot_timeseries_rho_v.py -p "+ _projectRootDir.string()+VORO_LOCATION + " -n "+filename.string()+ " -f "+boost::lexical_cast<std::string>(data.GetFps()); parameters_Timeseries = PYTHON + parameters_Timeseries; std::cout << parameters_Timeseries << "\n;"; int res=system(parameters_Timeseries.c_str()); Log->Write("INFO:\t time series result: %d ",res); } } else { Log->Write("INFO:\tFailed with Method D using measurement area id %d!\n",_areaForMethod_D[i]->_id); } } } if(_DoesUseMethodI) //method_I { if(_areaForMethod_I.empty()) { Log->Write("ERROR: Method I selected with no measurement area!"); exit(EXIT_FAILURE); } #pragma omp parallel for for(long unsigned int i=0; i<_areaForMethod_I.size(); i++) { Method_I method_I; method_I.SetStartFrame(_StartFramesMethodI[i]); method_I.SetStopFrame(_StopFramesMethodI[i]); method_I.SetCalculateIndividualFD(_IndividualFDFlags[i]); method_I.SetGeometryPolygon(_geoPolyMethodI[_areaForMethod_I[i]->_id]); method_I.SetGeometryFileName(_geometryFileName); method_I.SetGeometryBoundaries(_lowVertexX, _lowVertexY, _highVertexX, _highVertexY); method_I.SetGridSize(_grid_size_X, _grid_size_Y); method_I.SetOutputVoronoiCellData(_outputGraph); // method_I.SetPlotVoronoiGraph(_plotGraph); method_I.SetPlotVoronoiIndex(_plotIndex); method_I.SetDimensional(_isOneDimensional); // method_I.SetCalculateProfiles(_getProfile); method_I.SetTrajectoriesLocation(path); if(_cutByCircle) { method_I.Setcutbycircle(_cutRadius, _circleEdges); } method_I.SetMeasurementArea(_areaForMethod_I[i]); bool result_I = method_I.Process(data,_scriptsLocation, _areaForMethod_I[i]->_zPos); if(result_I) { Log->Write("INFO:\tSuccess with Method I using measurement area id %d!\n",_areaForMethod_I[i]->_id); std::cout << "INFO:\tSuccess with Method I using measurement area id "<< _areaForMethod_I[i]->_id << "\n"; if(_plotTimeseriesI[i]) { string parameters_Timeseries= " " +_scriptsLocation.string()+"/_Plot_timeseries_rho_v.py -p "+ _projectRootDir.string()+VORO_LOCATION + " -n "+filename.string()+ " -f "+boost::lexical_cast<std::string>(data.GetFps()); parameters_Timeseries = PYTHON + parameters_Timeseries; std::cout << parameters_Timeseries << "\n;"; int res=system(parameters_Timeseries.c_str()); Log->Write("INFO:\t time series result: %d ",res); } } else { Log->Write("INFO:\tFailed with Method I using measurement area id %d!\n",_areaForMethod_I[i]->_id); } } } return 0; } FILE* Analysis::CreateFile(const string& filename) { //first try to create the file FILE* fHandle= fopen(filename.c_str(),"w"); if(fHandle) return fHandle; unsigned int found=filename.find_last_of("/\\"); string dir = filename.substr(0,found)+"/"; //string file= filename.substr(found+1); // the directories are probably missing, create it if (mkpath((char*)dir.c_str())==-1) { Log->Write("ERROR:\tcannot create the directory <%s>",dir.c_str()); return NULL; } //second and last attempt return fopen(filename.c_str(),"w"); } #if defined(_WIN32) // @todo: rewrite using boost int Analysis::mkpath(char* file_path) { assert(file_path && *file_path); char* p; for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { *p='\0'; if (_mkdir(file_path)==-1) { if (errno!=EEXIST) { *p='/'; return -1; } } *p='/'; } return 0; } #else // @todo: rewrite using boost int Analysis::mkpath(char* file_path, mode_t mode) { assert(file_path && *file_path); char* p; for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { *p='\0'; if (mkdir(file_path, mode)==-1) { if (errno!=EEXIST) { *p='/'; return -1; } } *p='/'; } return 0; } // delete #endif