20200730原版
今晚还是有点累了,代码先贴这,明天慢慢写积累分析吧。
这个代码写起来还是很一波三折的。
20201126更新
嗯,这个明天来得真是不容易呢。。。
言归正传,系统说起。
下方代码的写作背景
前两天找了老师,老师建议我还是把之前做过的东西继续完善,刚好因为这个代码写了太长时间然后当时找老师的时候因为一点点单位的问题出了点岔子,这不,今天有空,这就来完善一下之前的一些博客。
做这个的背景知识是我之前把每一辆车的位置信息都放在了一个单独的txt文件中,包括这个车在某个速度为0的坐标点的时段。
txt文件名就是这个车的id,五位数的阿拉伯数字。
嗯,大概就是这个样子。
然后内部的信息是这样子的:
格式就是:<车机号,年月日时分秒,经度,纬度,该点速度,方向>。
其实这些都很有用,现在感觉这里面能够提取到的信息其实比单纯的停留点要多得多,甚至源数据文件,就是还没有经过我折腾的数据点里面的信息更多。根据数据能分析出来更多东西。
如何根据这些文档制作热度文件
好了我们言归正传,这里是要制作一个热度文件,该热度文件是要 合并所有车的位置相同的停留点,计算此停留点上的平均停留时间以及此停留点的热度(停到过这里的车辆数目)。
思路就是:
具体的操作的话,就看下面的代码好了!
代码实现
#pragma warning(disable:4996)
//这边的话不需要用到scanf的安全模式所以就用这个取消掉
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<fstream>
#include<io.h>
//io.h可以实现遍历某一个文件夹下的所有文件
#include<iomanip>
using namespace std;
//全局变量定义区***********************************
// 定义宏,之后开数组的时候都方便,长度也好一起修改。
const int DATA_LENGTH(100);
const int LINE_LENGTH(256);
// 这里是用于遍历cars_sorted文件夹下的所有txt文件要用到的变量
// 这个用法比较常用,也很好用,可以专门记一下。
string inPath = "cars_sorted\\*.txt";
long handle;
struct _finddata_t fileinfo;
//用于字符串暂存和分割的两个变量
string line_str;
char line[LINE_LENGTH];
//用于坐标转换(将精确坐标模糊化以得到普适性停留点)的中间变量
double middleVariableLongitude;
double middleVariableLatitude;
//用于判断某车在某点停留时长的几个数据记录变量,
//初始化为没有数据时的情况,之后也好通过这些情况来判断是否有东西,可读性也强。
int stop_time = 0;
string start_time_total = "no_time";
string car_number_in_map = "no_car";
//类与对象定义区*****************************************
//从原始的车辆的数据文件中读取的内容
//这个类是直接从之前的代码搬过来的,有一些变量用不到,但是基本都好用。
class carsInfo
{
public:
char carID[DATA_LENGTH]; //车机号
char GPS_Time[DATA_LENGTH]; //GPS时间
char longitude[DATA_LENGTH]; //经度
char latitude[DATA_LENGTH]; //纬度
char speed[DATA_LENGTH]; //速度
char direction[DATA_LENGTH]; //方向
int year;
int month;
int day;
int hour;
int minute;
int second;
int allTime = 0;
long totalTime = 0;
double speedInt = 0.0;//这里是存储速度,变量名确实容易混淆,但是知道这个是速度就行。。
double longitudeOfCar, latitudeOfCar;//暂存每辆车的坐标信息
int longitudeEsti, latitudeEsti;//近似处理后的车辆坐标点
pair<double, double>pointCoordinate;//每辆车的精确坐标有序数对
pair<double, double>pointCoordinateEsti;//近似处理后的坐标有序数对
int flagOfLast = 0;
void copy(const carsInfo& carsInfoRight)
// 使用了浅拷贝,因为这个没有涉及到静态变量和指针什么的
{
strcpy(this->carID, carsInfoRight.carID);
strcpy(this->GPS_Time, carsInfoRight.GPS_Time);
strcpy(this->longitude, carsInfoRight.longitude);
strcpy(this->latitude, carsInfoRight.latitude);
strcpy(this->speed, carsInfoRight.speed);
strcpy(this->direction, carsInfoRight.direction);
this->year = carsInfoRight.year;
this->month = carsInfoRight.month;
this->day = carsInfoRight.day;
this->hour = carsInfoRight.hour;
this->minute = carsInfoRight.minute;
this->second = carsInfoRight.second;
this->allTime = carsInfoRight.allTime;
this->totalTime = carsInfoRight.totalTime;
this->speedInt = carsInfoRight.speedInt;
this->longitudeOfCar = carsInfoRight.longitudeOfCar;
this->latitudeOfCar = carsInfoRight.latitudeOfCar;
this->pointCoordinate = carsInfoRight.pointCoordinate;
}
};
//实例化两个车辆的信息对象,方便后续使用
carsInfo carNow, carNow1;
//存储在核心散列里的第二个元素的元素(有点拗口,往下面几行看,看到map那里^O^)
class cars_info_into_point
{
public:
string carNumber;
double thisCarStopTime;//该车在该点的停留时间
};
cars_info_into_point carsPointLocal;
map<pair<double, double>, vector<pair<string,double> > >pointInfo;
// 这个map是重头戏,这个里面存储着每一个点及其停留信息,停留信息是存储在一个元素为cars_info_into_point的向量中的。
//这个cars_info_into_point,往前看,是存了一个<车机号,该车在该点停留时间>的内容。
//先这么存了,等遍历过后最终结果需要时再对整个数组进行计算,也比较不复杂,而且只要计算一次。
//这里后面还是弄成一个向量,存储每一个车的数据,最后算总和也好算,直接size()访问就行了
//这个第一个pair应当是模糊的坐标。
pair<double, double>pointCoordinate;
pair<double, double>pointCoordinateEsti;
ifstream input;
ofstream output;
//函数声明区***********************************************
void calStopTime();
void readDataAndCal();//读取数据然后计算相应的数据
void saveBufferToFile();//一并将整理好的数据信息输出到目标文档
int goThrough();//整个函数的执行流程,在主函数中调用。
//主函数***************************************************
int main()
{
goThrough();
return 0;
}
//函数定义区***********************************************
int goThrough()
{
//利用io.h进行遍历的流程
handle = _findfirst(inPath.c_str(), &fileinfo);
if (handle == -1){
return -1;
}
do
{
string fileInfoName = fileinfo.name;
input.open("cars_sorted\\" + fileInfoName);
while (!input.eof())
{
readDataAndCal();
}
input.close();
} while (!_findnext(handle, &fileinfo));
// 上方的循环跳出来了,说明目标文件夹下的所有文件都被遍历过了。
// 接着就进行存储。
saveBufferToFile();
_findclose(handle);
}
void readDataAndCal()//从原始数据文件中读取数据然后计算相应的数据
{
getline(input, line_str);
strcpy(line, line_str.c_str());
sscanf(line, "%*[<]%[^,],%[^,],%[^,],%[^,],%[^,],%[^>]%*[>]",
carNow.carID,
carNow.GPS_Time,
carNow.longitude,
carNow.latitude,
carNow.speed,
carNow.direction);
sscanf(carNow.GPS_Time, "%d-%d-%d %d:%d:%d",
&carNow.year,
&carNow.month,
&carNow.day,
&carNow.hour,
&carNow.minute,
&carNow.second);
//车的精确的经纬度的计算转化
carNow.longitudeOfCar = strtod(carNow.longitude, NULL);//使用strtod函数来把char*转换成double。
carNow.latitudeOfCar = strtod(carNow.latitude, NULL);
//车辆的精确速度的转化
carNow.speedInt = strtod(carNow.speed, NULL);
//车辆的坐标pair生成
carNow.pointCoordinate = make_pair(carNow.longitudeOfCar, carNow.latitudeOfCar);
//下面是车辆的模糊坐标 用来估算的
middleVariableLongitude = carNow.longitudeOfCar * 1e5;
middleVariableLatitude = carNow.latitudeOfCar * 1e5;
middleVariableLongitude /= 10;
middleVariableLatitude /= 10;
middleVariableLongitude = floor(middleVariableLongitude);
middleVariableLatitude = floor(middleVariableLatitude);
carNow.pointCoordinateEsti = make_pair(middleVariableLongitude / (1e4), middleVariableLatitude / (1e4));
//计算完成----------------------------要进行当前的比较
if (carNow.speedInt == 0.0)
{
calStopTime();
}
}
void calStopTime()//没加判断速度是否为0--但是我在readDataAndCal()中已经进行了判断了。
{
进行当前的比较的时候,只要当前点的速度为0,那么在该点的停留时间就加十秒。
那么,怎么判断当前点有没有呢?可以通过map来访问map对应的key的size(),如果size()为0的话就创建一个车的点然后把这个车在这个点的对应的时间赋值成10,
之后如果需要获得某个点整体的车辆数据的话就可以直接用size(),
那么如何获得一个车?还不如直接map映射成map就完事。?????????????????????????
其实都不用这样,其实直接用一个pair就行了。不过也区别不大。
如果需要获得这个点所有的停留时间总和的话就用一个迭代器加一个循环。
这样后面的也就方便了。
所以现在就是两种情况:没有当前点下当前车的数据和有当前点下当前车的数据。
如果有当前点的数据的话那就最好了,那就直接在当前点的当前车的second+=10就行
如果当前点下没有当前车的数据,那就加一个。这里就用到的是向量的append
如果连当前点都没有,那就建一个点,然后重复上一步。
进行当前的比较的时候,只要当前点的速度为0,那么在该点的停留时间就加十秒。
那么,怎么判断当前点有没有呢?可以通过map来访问map对应的key的size(),如果size()为0的话就创建一个车的点然后把这个点的对应的时间赋值成10,
这样后面的也就方便了。
//嗯,,现在看当时的心路历程注释真的有点难顶呢……
//【简而言之】,就是要看当前点速度是否为0,当前点速度为0,就给这个点加十秒就完事
//具体可以看下面的代码实现。比较易懂。
if (pointInfo[carNow.pointCoordinateEsti].size() == 0)//当前点啥也没有
{
pointInfo[carNow.pointCoordinateEsti].push_back(make_pair(carNow.carID, 10.0));
}
else//当前点有东西,但是不一定有当前的车。
{
//那么就要判断有没有当前的车。如何判断?用循环。
bool isHaveFlag = false;
string carLocalNumber = carNow.carID;
for (auto it_car = pointInfo[carNow.pointCoordinateEsti].begin(); ((it_car != pointInfo[carNow.pointCoordinateEsti].end()) && (isHaveFlag == false)); it_car++)
{
if (it_car->first == carLocalNumber)//如果有的话肯定有10秒了至少,所以可以直接+=10,然后isHaveFlag置位
{
it_car->second += 10.0;
isHaveFlag = true;
}
}
if (isHaveFlag == false)//依然没有找到对应的车上的点,所以就直接按照上面那个没有该点来弄。
//【其实这里可以优化,可以先循环然后直接弄,不过这样的话思路比较清晰,主要也不知道当size()==0的时候直接使用迭代器会出现什么问题。】
{
pointInfo[carNow.pointCoordinateEsti].push_back(make_pair(carNow.carID, 10.0));
}
}
}
void saveBufferToFile()
//计算直接在这里计算吧,就不用再弄一个函数了。数据包还是有点点问题。
{
output.open("stop.txt", ios_base::app);
for (auto it_data = pointInfo.begin(); it_data != pointInfo.end(); it_data++)
{
//计算总车辆数
int totalCarNums = 0;
double totalTimes = 0;
for (auto begvec = it_data->second.begin(); begvec != it_data->second.end(); begvec++)
{
totalCarNums++;
totalTimes += begvec->second;
}
//计算平均时间
double aveStopTime = totalTimes / totalCarNums;
//输出到文件里
output <<std::fixed<<setprecision(4)<< it_data->first.first << "," << std::fixed << setprecision(4)<< it_data->first.second << " " << std::fixed << setprecision(0)<< aveStopTime << " " << totalCarNums << endl;
}
output.close();
}
写了这么多,还是挺有感触的。当时我在家里可是想了半天……
其实还是一个是知识储备不太够的问题,就比如我当时写了这个之后不久,八月份那会儿有小学期,小学期我们老师告诉我们STL在存的时候最好还是存指针,毕竟指针其实是等长度的,然后如果存的东西不等长度可能会出现其他的一些情况。
话虽这样说,小学期却依然用unity做了个东西……没有用c++……
扯远了,写这些的时候,我就感觉我对数据结构的需求量还是蛮大的。还是得好好学一下数据结构的使用。这是实战啊!
好了,就这么多,现在这个写完了,该补充其他的去了。
——来自一个学不会大物的小笨鸟。