Files
park/application/api/controller/YqScreen.php

1631 lines
66 KiB
PHP
Raw Normal View History

2025-12-01 11:19:23 +08:00
<?php
namespace app\api\controller;
use app\admin\model\Admin;
use app\admin\model\yq\alarm\Alarm;
use app\admin\model\yq\alarm\Trend;
use app\admin\model\yq\base_config\EventType;
use app\admin\model\yq\electronic_waybill\Waybill;
use app\admin\model\yq\park\Park;
use app\admin\model\yq\perimeter\EnterpriseCheck;
use app\admin\model\yq\vehicle\LineLog;
use app\admin\model\yq\vehicle\ParkLineLog;
use app\common\controller\Api;
use app\admin\model\yq\vehicle\Vehicle;
use app\admin\model\yq\base_config\Perimeter;
use fast\Date;
use think\Db;
class YqScreen extends Api
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
//登录查验
public function login_check()
{
// public function login_check($param)
// {
//
// if (empty($param['ad']) || empty($param['salt'])) {
// $this->error( '异常操作!', ['status' => false]);
// }
// $where['admin_username'] = $param['ad'];
// $where['admin_pwd_salt'] = $param['salt'];
// $admin = Db::name('admin')->where($where)->find();
$admin = Admin::get(1);
if ($admin) {
$this->success('正常!', ['status' => true]);
} else {
$this->error('失败!', ['status' => false]);
}
}
//大屏地图车辆列表
public function screen_list()
{
$map['is_del'] = array('in', '1,2');
$field = 'id,vehicleNo,longitude,latitude,longitude_84,latitude_84,vec1,vehicle_type,is_waybill,
is_violations,qr_color,positionTime,is_del,zai';
$vehicle_list = Vehicle::where($map)->field($field)->select();
//待优化运单查询
$w_field = 'tow_license,w_mission,waybill_loading,waybill_unloading,sale_product,cargocount,driver_tel';
$day_waybill_list = Waybill::whereTime('waybill_date', 'd')->field($w_field)->select();
$temp = array_column($day_waybill_list, 'tow_license');
if ($vehicle_list) {
foreach ($vehicle_list as $k => $v) {
$vehicle_list[$k]['waybill_status'] = false;
$v_id = in_array($v['vehicleNo'], $temp);
if ($v_id) {
$vehicle_list[$k]['waybill_status'] = true;
$waybill_info = Waybill::whereTime('waybill_date', 'd')->where('tow_license', $v['vehicleNo'])->field($w_field)->find();
$vehicle_list[$k]['waybill_info'] = $waybill_info;
}
}
}
//明宇停车场count车辆总数enter_count当日进入车辆数当日出去车辆数
$mingyu['count'] = Vehicle::where($map)->where('perimeter_id', 76)->field('id')->count();
$mingyu['enter_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 1])->count();
$mingyu['out_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 2])->count();
$data['vehicle_list'] = $vehicle_list;
$data['mingyu'] = $mingyu;
$this->success('请求成功!', $data);
}
//大屏地图车辆列表
public function screen_list22()
{
$map['is_del'] = array('in', '1,2');
$field = 'id,vehicleNo,longitude,latitude,longitude_84,latitude_84,vec1,vehicle_type,is_waybill,
is_violations,qr_color,positionTime,is_del,zai';
$vehicle_list = Vehicle::where($map)->field($field)->select();
if ($vehicle_list) {
foreach ($vehicle_list as $k => $v) {
$w_field = 'w_mission,waybill_loading,waybill_unloading,sale_product,cargocount,driver_tel';
$waybill_info = Db::name('waybill_c')->whereTime('waybill_date', 'd')->where('tow_license', $v['vehicleNo'])->field($w_field)->find();
$vehicle_list[$k]['waybill_status'] = false;
if ($waybill_info) {
$vehicle_list[$k]['waybill_status'] = true;
$vehicle_list[$k]['waybill_info'] = $waybill_info;
}
}
}
//明宇停车场count车辆总数enter_count当日进入车辆数当日出去车辆数
$mingyu['count'] = Vehicle::where($map)->where('perimeter_id', 76)->field('id')->count();
$mingyu['enter_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 1])->count();
$mingyu['out_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 2])->count();
$data['vehicle_list'] = $vehicle_list;
$data['mingyu'] = $mingyu;
$this->success('请求成功!', $data);
}
//大屏-总览
public function overview()
{
$map['is_del'] = 1;
//园区基本情况
//一级企业 二级企业 三级企业
$info['enterprise_count'] = Perimeter::group('region_lv')->where($map)->field('region_lv,count(id) as num')->select();
//片区情况 - 统计每个片区的车辆数量取前3个
$areaStats = $this->getAreaVehicleStatistics();
$info['area'] = [];
// 取前3个片区的车辆数量如果不足3个则补0
for ($i = 0; $i < 3; $i++) {
$info['area'][] = isset($areaStats[$i]) ? $areaStats[$i]['value'] : 0;
}
//道路情况 - 统计道路上的车辆数量总和
$roadStats = $this->getRoadVehicleStatistics();
$info['road_count'] = 0;
if ($roadStats) {
foreach ($roadStats as $road) {
$info['road_count'] += $road['count'];
}
}
//卡口流量统计
$info['road_checkpoints'] = [
'name' => ['卡1', '卡2', '卡2', '卡2', '卡2', '卡2'],//卡口名称
'drive_in' => [35, 32, 36, 47, 14, 60],//驶入
'drive_out' => [47, 20, 16, 52, 40, 18]//驶出
];
//近30天报警统计
$info['alarm_count'] = [];
// 计算30天前的日期
$startDate = date('Y-m-d 00:00:00', strtotime('-30 days'));
$endDate = date('Y-m-d 23:59:59');
$event_type = EventType::select();
foreach ($event_type as $k => $v) {
$info['alarm_count'][] = [
'name' => $v['name'],
'value' => Alarm::where('type', $v['id'])
->where('create_time', '>=', $startDate)
->where('create_time', '<=', $endDate)
->count()
];
}
//报警处理情况 - 统计近30天的报警处理情况
$alarmMap = [
'is_del' => 1,
];
// 已处理res_status = 1已响应或 feedback_status = 1已反馈
$processed = Alarm::where($alarmMap)
->where('create_time', '>=', $startDate)
->where('create_time', '<=', $endDate)
->where(function($query) {
$query->where('res_status', 1)
->whereOr('feedback_status', 1);
})
->count();
// 未处理res_status = 0未响应且 feedback_status = 0未反馈
$untreated = Alarm::where($alarmMap)
->where('create_time', '>=', $startDate)
->where('create_time', '<=', $endDate)
->where('res_status', 0)
->where('feedback_status', 0)
->count();
$he = $processed + $untreated;
$info['alarm_handling'] = [
'count' => array_sum(array_column($info['alarm_count'], 'value')),
'processed' => $processed,
'untreated' => $untreated,
'he' => $he,
'processed_ratio' => $he == 0 ? 0 : round(($processed / $he) * 100, 2),
'untreated_ratio' => $he == 0 ? 0 : round(($untreated / $he) * 100, 2),
];
//园区危化品车辆统计 - 在线/离线根据定位时间判断10分钟内更新为在线
$vehicleMap = ['is_del' => ['in', '1,2']];
// 计算10分钟前的时间戳
$onlineTimeThreshold = time() - 600; // 10分钟 = 600秒
// 查询所有车辆
$allVehicles = Vehicle::where($vehicleMap)
->field('id,vehicleNo,positionTime')
->select();
$online = 0;
$offline = 0;
if ($allVehicles) {
foreach ($allVehicles as $vehicle) {
if (empty($vehicle['positionTime'])) {
$offline++;
continue;
}
// 将定位时间转换为时间戳
$positionTimestamp = is_numeric($vehicle['positionTime'])
? $vehicle['positionTime']
: strtotime($vehicle['positionTime']);
// 如果定位时间在10分钟内则是在线
if ($positionTimestamp >= $onlineTimeThreshold) {
$online++;
} else {
$offline++;
}
}
}
$he = $online + $offline;
$info['vehicle'] = [
'online' => $online,
'offline' => $offline,
'online_ratio' => $he == 0 ? 0 : round(($online / $he) * 100, 2),
'offline_ratio' => $he == 0 ? 0 : round(($offline / $he) * 100, 2)
];
//危化品车流量top5 - 按装货单位统计车流量(装货+卸货)
$waybillMap = [
'is_del' => 1,
'waybill_loading' => ['<>', ''], // 装货单位不为空
];
// 统计每个装货单位的装货数量
$loadingStats = Db::name('waybill')
->where($waybillMap)
->where('w_mission', '装货')
->field('waybill_loading, COUNT(*) as count')
->group('waybill_loading')
->select();
// 统计每个装货单位的卸货数量
$unloadingStats = Db::name('waybill')
->where($waybillMap)
->where('w_mission', '卸货')
->field('waybill_loading, COUNT(*) as count')
->group('waybill_loading')
->select();
// 构建装货单位到数量的映射
$loadingMap = [];
$unloadingMap = [];
if ($loadingStats) {
foreach ($loadingStats as $stat) {
$loadingMap[$stat['waybill_loading']] = intval($stat['count']);
}
}
if ($unloadingStats) {
foreach ($unloadingStats as $stat) {
$unloadingMap[$stat['waybill_loading']] = intval($stat['count']);
}
}
// 合并所有装货单位,计算总车流量(装货+卸货)
$allEnterprises = array_unique(array_merge(array_keys($loadingMap), array_keys($unloadingMap)));
$trafficData = [];
foreach ($allEnterprises as $enterprise) {
$loadingCount = isset($loadingMap[$enterprise]) ? $loadingMap[$enterprise] : 0;
$unloadingCount = isset($unloadingMap[$enterprise]) ? $unloadingMap[$enterprise] : 0;
$totalCount = $loadingCount + $unloadingCount;
$trafficData[] = [
'waybill_loading' => $enterprise,
'loading' => $loadingCount,
'unloading' => $unloadingCount,
'total' => $totalCount
];
}
// 按总车流量排序取前5个
usort($trafficData, function($a, $b) {
return $b['total'] - $a['total'];
});
$top5 = array_slice($trafficData, 0, 5);
// 构建返回数据
$nameList = [];
$loadingList = [];
$unloadingList = [];
foreach ($top5 as $item) {
$nameList[] = [
'waybill_loading' => $item['waybill_loading'],
'count' => $item['total']
];
$loadingList[] = $item['loading'];
$unloadingList[] = $item['unloading'];
}
// 如果不足5个用空数据补齐
while (count($nameList) < 5) {
$nameList[] = ['waybill_loading' => '', 'count' => 0];
$loadingList[] = 0;
$unloadingList[] = 0;
}
$info['traffic_volume'] = [
'name' => $nameList,
'loading' => $loadingList, // 装货数量
'unloading' => $unloadingList // 卸货数量
];
//全年运单统计 - 统计当前年份每个月的运单数量
$year = date('Y');
$startDate = "{$year}-01-01 00:00:00";
$endDate = "{$year}-12-31 23:59:59";
// 查询实际数据(只统计未删除的运单)
$waybillMap = ['is_del' => 1];
$actualStats = Db::name('waybill')
->where($waybillMap)
->where('create_time', '>=', $startDate)
->where('create_time', '<=', $endDate)
->field("DATE_FORMAT(create_time, '%Y-%m') as month, COUNT(*) as count")
->group("DATE_FORMAT(create_time, '%Y-%m')")
->select();
$monthlyData = [];
foreach ($actualStats as $stat) {
$month = date('m', strtotime($stat['month']));
$monthlyData[$month] = [
'month' => $month,
'year_month' => $stat['month'],
'count' => intval($stat['count']),
];
}
// 构建完整的12个月数据包含空月份
$fullYearStats = [];
for ($i = 1; $i <= 12; $i++) {
$month = str_pad($i, 2, '0', STR_PAD_LEFT);
$yearMonth = "{$year}-{$month}";
if (isset($monthlyData[$month])) {
// 有数据的月份
$fullYearStats[] = $monthlyData[$month];
} else {
// 无数据的月份补零
$fullYearStats[] = [
'month' => $month,
'year_month' => $yearMonth,
'count' => 0,
];
}
}
$info['year'] = $fullYearStats;
$this->success('请求成功!', $info);
}
//大屏-车辆码
public function vehicle_code()
{
$map['is_del'] = array('in', '1,2');
//绿码车数量
$info['green_code_count'] = Vehicle::where($map)->where('qr_color', 1)->count();
//黄码车数量
$info['yellow_code_count'] = Vehicle::where($map)->where('qr_color', 2)->count();
//红码车数量
$info['red_code_count'] = Vehicle::where($map)->where('qr_color', 3)->count();
//报警列表 - 只显示当天的报警
$wh_police['is_del'] = 1;
$wh_police['type'] = array('in', '1,2,3,4,5,6');
$field = 'license,name,trigger_type,create_time';
// 查询当天的报警
$police_list['count'] = Alarm::where($wh_police)->whereTime('create_time', 'd')->count();
$police_list['list'] = Alarm::where($wh_police)->whereTime('create_time', 'd')->field($field)->order('create_time desc')->select();
// 如果当天没有报警显示最近7天的报警
if ($police_list['count'] == 0) {
$police_list['count'] = Alarm::where($wh_police)->whereTime('create_time', '-7 days')->count();
$police_list['list'] = Alarm::where($wh_police)->whereTime('create_time', '-7 days')->field($field)->order('create_time desc')->limit(50)->select();
}
$data['info'] = $info;//基础信息-左
$data['police_list'] = $police_list;//运单信息-右3
$this->success('请求成功!', $data);
}
//大屏-车辆动态
public function vehicle_trend()
{
$map['is_del'] = array('in', '1,2');
$park_info = Db::name('perimeter')->where('id', 1)->field('enter_vehicle,out_vehicle')->find();
// 园区围栏坐标(用于判断进出园)
$max_path_json = '[{"lng":"107.01527","lat":"29.895601"},{"lng":"106.997876","lat":"29.89281"},{"lng":"106.991391","lat":"29.887939"},{"lng":"106.985853","lat":"29.880405"},{"lng":"106.981835","lat":"29.871026"},{"lng":"106.960543","lat":"29.849142"},{"lng":"106.950737","lat":"29.83564"},{"lng":"106.945862","lat":"29.824089"},{"lng":"106.943563","lat":"29.814148"},{"lng":"106.945054","lat":"29.799754"},{"lng":"106.942764","lat":"29.783607"},{"lng":"106.970332","lat":"29.766519"},{"lng":"106.983273","lat":"29.751322"},{"lng":"107.017612","lat":"29.737776"},{"lng":"107.050425","lat":"29.734115"},{"lng":"107.064458","lat":"29.737683"},{"lng":"107.064744","lat":"29.739318"},{"lng":"107.058942","lat":"29.748447"},{"lng":"107.055205","lat":"29.762378"},{"lng":"107.071581","lat":"29.77504"},{"lng":"107.086248","lat":"29.789923"},{"lng":"107.088111","lat":"29.796775"},{"lng":"107.084106","lat":"29.807387"},{"lng":"107.080959","lat":"29.81142"},{"lng":"107.068298","lat":"29.813474"},{"lng":"107.06422","lat":"29.813406"},{"lng":"107.060435","lat":"29.82112"},{"lng":"107.050628","lat":"29.827207"},{"lng":"107.04726","lat":"29.828623"},{"lng":"107.046543","lat":"29.829966"},{"lng":"107.04701","lat":"29.830878"},{"lng":"107.04712","lat":"29.834263"},{"lng":"107.044827","lat":"29.838826"},{"lng":"107.053757","lat":"29.842875"},{"lng":"107.056479","lat":"29.849047"},{"lng":"107.057124","lat":"29.85153"},{"lng":"107.056802","lat":"29.853468"},{"lng":"107.060415","lat":"29.86047"},{"lng":"107.061665","lat":"29.864841"},{"lng":"107.054919","lat":"29.881592"},{"lng":"107.052508","lat":"29.894119"},{"lng":"107.040451","lat":"29.898403"},{"lng":"107.028579","lat":"29.89536"},{"lng":"107.014946","lat":"29.895825"},{"lng":"106.997804","lat":"29.89278"}]';
$max_path_arr = json_decode($max_path_json, true);
// 初始化变量
$enterByHour = array_fill(0, 24, 0);
$outByHour = array_fill(0, 24, 0);
// 先从 vehicle_park_line_log 表查询(如果已有数据)
// 注意vehicle_park_line_log 表的 is_del 字段可能不存在或值不同,所以不添加 is_del 条件
$info['enter_count'] = ParkLineLog::whereTime('create_time', 'd')->where('type', 1)->where('is_del', 1)->count();
$info['out_count'] = ParkLineLog::whereTime('create_time', 'd')->where('type', 2)->where('is_del', 1)->count();
// 如果 vehicle_park_line_log 表没有数据,从轨迹数据实时统计
if ($info['enter_count'] == 0 && $info['out_count'] == 0) {
$enterCount = 0;
$outCount = 0;
// 查询当天的轨迹数据
$todayStart = date('Y-m-d 00:00:00');
$todayEnd = date('Y-m-d 23:59:59');
$vehicleLines = Db::name('vehicle_line')
->where('create_time', '>=', $todayStart)
->where('create_time', '<=', $todayEnd)
->where('is_del', 1)
->field('vehicleNo,line')
->select();
if (empty($vehicleLines)) {
// 如果没有轨迹数据返回0
$info['enter_count'] = 0;
$info['out_count'] = 0;
} else {
foreach ($vehicleLines as $lineRecord) {
$lineData = json_decode($lineRecord['line'], true);
if (!is_array($lineData) || empty($lineData)) {
continue;
}
// 遍历轨迹点,判断进出状态(允许同一辆车多次进出)
foreach ($lineData as $o => $point) {
if (empty($point['coordinate']) || empty($point['create_time'])) {
continue;
}
// 解析坐标
$coords = explode(',', $point['coordinate']);
if (count($coords) < 2) {
continue;
}
$point_coord = [
'lng' => floatval($coords[0]),
'lat' => floatval($coords[1])
];
// 判断当前点是否在园区内
$vehicle_exist = $this->is_point_in_polygon($point_coord, $max_path_arr);
// 判断上一个点是否在园区内
$up_exist = false;
if ($o > 0 && isset($lineData[$o-1]['coordinate'])) {
$up_coords = explode(',', $lineData[$o-1]['coordinate']);
if (count($up_coords) >= 2) {
$up_point = [
'lng' => floatval($up_coords[0]),
'lat' => floatval($up_coords[1])
];
$up_exist = $this->is_point_in_polygon($up_point, $max_path_arr);
}
} elseif ($o == 0) {
// 第一个点,假设上一个点在园区外
$up_exist = false;
}
// 判断是否发生进出变化
if ($vehicle_exist && !$up_exist) {
// 从园区外进入园区内 - 进园
$enterCount++;
// 统计该小时
$pointTime = strtotime($point['create_time']);
if ($pointTime !== false) {
$hour = (int)date('H', $pointTime);
if ($hour >= 0 && $hour < 24) {
$enterByHour[$hour]++;
}
}
} elseif (!$vehicle_exist && $up_exist) {
// 从园区内离开到园区外 - 出园
$outCount++;
// 统计该小时
$pointTime = strtotime($point['create_time']);
if ($pointTime !== false) {
$hour = (int)date('H', $pointTime);
if ($hour >= 0 && $hour < 24) {
$outByHour[$hour]++;
}
}
}
}
}
$info['enter_count'] = $enterCount;
$info['out_count'] = $outCount;
}
}
// 统计每小时进出园次数
$info['park'] = [];
$time_arr = [];
for ($x = 0; $x < 24; $x++) {
$time_arr[$x]['s_time'] = date('Y-m-d ') . $x . ':00:00';
$time_arr[$x]['e_time'] = date('Y-m-d ') . $x . ':59:59';
$info['park']['time'][] = $x;
}
// 如果 vehicle_park_line_log 表有数据,从表查询
if ($info['enter_count'] > 0 || $info['out_count'] > 0) {
foreach ($time_arr as $k => $v) {
$enter = ParkLineLog::where('create_time', '>=', $v['s_time'])
->where('create_time', '<=', $v['e_time'])
->where('type', 1)
->where('is_del', 1)
->count();
$out = ParkLineLog::where('create_time', '>=', $v['s_time'])
->where('create_time', '<=', $v['e_time'])
->where('type', 2)
->where('is_del', 1)
->count();
$info['park']['enter'][] = $enter;
$info['park']['out'][] = $out;
}
} else {
// 如果表没有数据,使用从轨迹数据统计的结果
foreach ($time_arr as $k => $v) {
$info['park']['enter'][] = isset($enterByHour[$k]) ? $enterByHour[$k] : 0;
$info['park']['out'][] = isset($outByHour[$k]) ? $outByHour[$k] : 0;
}
}
//片区车流统计 - 统计每个企业区域region_type=6内的车辆数量
$info['area'] = $this->getAreaVehicleStatistics();
//道路情况 - 统计每条道路上的真实车辆数量
$info['road_count'] = $this->getRoadVehicleStatistics();
// //园区停止车辆数
// $info['stop_count'] = Vehicle::where($map)->where('vec1', 0)->count();
// //园区行驶车辆数
// $info['driving_count'] = Vehicle::where($map)->where('vec1', '>', 0)->count();
// 园区内行驶停止车辆统计 - 使用轨迹数据统计每个小时的车辆数量
// 获取园区围栏坐标(使用与 vehicle_log 相同的围栏)
$max_path_json = '[{"lng":"107.01527","lat":"29.895601"},{"lng":"106.997876","lat":"29.89281"},{"lng":"106.991391","lat":"29.887939"},{"lng":"106.985853","lat":"29.880405"},{"lng":"106.981835","lat":"29.871026"},{"lng":"106.960543","lat":"29.849142"},{"lng":"106.950737","lat":"29.83564"},{"lng":"106.945862","lat":"29.824089"},{"lng":"106.943563","lat":"29.814148"},{"lng":"106.945054","lat":"29.799754"},{"lng":"106.942764","lat":"29.783607"},{"lng":"106.970332","lat":"29.766519"},{"lng":"106.983273","lat":"29.751322"},{"lng":"107.017612","lat":"29.737776"},{"lng":"107.050425","lat":"29.734115"},{"lng":"107.064458","lat":"29.737683"},{"lng":"107.064744","lat":"29.739318"},{"lng":"107.058942","lat":"29.748447"},{"lng":"107.055205","lat":"29.762378"},{"lng":"107.071581","lat":"29.77504"},{"lng":"107.086248","lat":"29.789923"},{"lng":"107.088111","lat":"29.796775"},{"lng":"107.084106","lat":"29.807387"},{"lng":"107.080959","lat":"29.81142"},{"lng":"107.068298","lat":"29.813474"},{"lng":"107.06422","lat":"29.813406"},{"lng":"107.060435","lat":"29.82112"},{"lng":"107.050628","lat":"29.827207"},{"lng":"107.04726","lat":"29.828623"},{"lng":"107.046543","lat":"29.829966"},{"lng":"107.04701","lat":"29.830878"},{"lng":"107.04712","lat":"29.834263"},{"lng":"107.044827","lat":"29.838826"},{"lng":"107.053757","lat":"29.842875"},{"lng":"107.056479","lat":"29.849047"},{"lng":"107.057124","lat":"29.85153"},{"lng":"107.056802","lat":"29.853468"},{"lng":"107.060415","lat":"29.86047"},{"lng":"107.061665","lat":"29.864841"},{"lng":"107.054919","lat":"29.881592"},{"lng":"107.052508","lat":"29.894119"},{"lng":"107.040451","lat":"29.898403"},{"lng":"107.028579","lat":"29.89536"},{"lng":"107.014946","lat":"29.895825"},{"lng":"106.997804","lat":"29.89278"}]';
$max_path_arr = json_decode($max_path_json, true);
// 获取当前时间
$currentTime = time();
$currentHour = (int)date('H', $currentTime);
$todayDate = date('Y-m-d', $currentTime);
// 初始化今天24小时的数据数组00:00-23:59
$fullStats = [];
$fullStats2 = [];
for ($i = 0; $i < 24; $i++) {
$hourTimeShort = sprintf('%02d:00', $i);
$fullStats[] = [
'hour_time' => $hourTimeShort,
'order_count' => 0
];
$fullStats2[] = [
'hour_time' => $hourTimeShort,
'order_count' => 0
];
}
// 查询当天的轨迹数据
$todayStart = date('Y-m-d 00:00:00');
$todayEnd = date('Y-m-d 23:59:59');
$vehicleLines = Db::name('vehicle_line')
->where('create_time', '>=', $todayStart)
->where('create_time', '<=', $todayEnd)
->where('is_del', 1)
->field('vehicleNo,line')
->select();
// 批量获取所有车辆的速度信息(用于旧数据备选,如果轨迹点没有保存速度)
$allVehicleNos = [];
foreach ($vehicleLines as $lineRecord) {
$allVehicleNos[] = $lineRecord['vehicleNo'];
}
$allVehicleNos = array_unique($allVehicleNos);
$vehicleSpeedMap = [];
if (!empty($allVehicleNos)) {
$vehicles = Db::name('vehicle')
->where('vehicleNo', 'in', $allVehicleNos)
->where($map)
->field('vehicleNo,vec1')
->select();
foreach ($vehicles as $v) {
$vehicleSpeedMap[$v['vehicleNo']] = $v['vec1'];
}
}
// 统计每个小时的车辆数量(使用集合去重,避免同一车辆在同一小时被重复计算)
$hourVehicleMap = []; // [小时索引 => [vehicleNo => true]]
$speedStats = ['has_speed_in_point' => 0, 'no_speed_in_point' => 0, 'driving_count' => 0, 'stop_count' => 0];
foreach ($vehicleLines as $lineRecord) {
$lineData = json_decode($lineRecord['line'], true);
if (!is_array($lineData) || empty($lineData)) {
continue;
}
$vehicleNo = $lineRecord['vehicleNo'];
$defaultSpeed = isset($vehicleSpeedMap[$vehicleNo]) ? $vehicleSpeedMap[$vehicleNo] : 0;
// 遍历轨迹点,按小时统计
foreach ($lineData as $point) {
if (empty($point['coordinate']) || empty($point['create_time'])) {
continue;
}
// 解析坐标
$coords = explode(',', $point['coordinate']);
if (count($coords) < 2) {
continue;
}
$point_coord = [
'lng' => floatval($coords[0]),
'lat' => floatval($coords[1])
];
// 判断是否在园区内
if (!$this->is_point_in_polygon($point_coord, $max_path_arr)) {
continue;
}
// 获取轨迹点的时间
$pointTime = strtotime($point['create_time']);
if ($pointTime === false) {
continue;
}
// 计算轨迹点属于哪个小时(今天的小时)
$pointHour = (int)date('H', $pointTime);
$pointDate = date('Y-m-d', $pointTime);
// 只统计今天的数据
if ($pointDate !== $todayDate) {
continue;
}
// 直接使用小时作为索引0-23
$index = $pointHour;
// 初始化小时数据
if (!isset($hourVehicleMap[$index])) {
$hourVehicleMap[$index] = ['stop' => [], 'driving' => []];
}
// 优先使用轨迹点保存的速度信息
// 如果轨迹点没有speed字段旧数据跳过该点不进行统计
// 因为使用车辆当前速度来判断历史轨迹点是不准确的
if (!isset($point['speed'])) {
// 旧数据没有speed字段跳过统计
$speedStats['no_speed_in_point']++;
continue;
}
$pointSpeed = floatval($point['speed']);
$speedStats['has_speed_in_point']++;
// 根据速度判断是停止还是行驶速度大于0.1km/h视为行驶避免浮点数精度问题
// 使用集合去重,同一车辆在同一小时只统计一次
if ($pointSpeed > 0.1) {
$hourVehicleMap[$index]['driving'][$vehicleNo] = true;
$speedStats['driving_count']++;
} else {
$hourVehicleMap[$index]['stop'][$vehicleNo] = true;
$speedStats['stop_count']++;
}
}
}
// 调试信息(可以注释掉,或记录到日志)
// \think\Log::info('车辆行驶停止统计调试信息: ' . json_encode($speedStats, JSON_UNESCAPED_UNICODE));
// 将统计结果填充到数组中24小时
for ($i = 0; $i < 24; $i++) {
if (isset($hourVehicleMap[$i])) {
$fullStats[$i]['order_count'] = count($hourVehicleMap[$i]['stop']);
$fullStats2[$i]['order_count'] = count($hourVehicleMap[$i]['driving']);
}
}
// 只返回过去12小时的数据用于前端显示
// 从当前小时往前推12小时
$resultStats = [];
$resultStats2 = [];
for ($i = 11; $i >= 0; $i--) {
$targetTime = $currentTime - $i * 3600;
$targetHour = (int)date('H', $targetTime);
$hourTimeShort = date('H:00', $targetTime);
$resultStats[] = [
'hour_time' => $hourTimeShort,
'order_count' => isset($fullStats[$targetHour]) ? $fullStats[$targetHour]['order_count'] : 0
];
$resultStats2[] = [
'hour_time' => $hourTimeShort,
'order_count' => isset($fullStats2[$targetHour]) ? $fullStats2[$targetHour]['order_count'] : 0
];
}
$info['stop_count'] = $resultStats;
$info['driving_count'] = $resultStats2;
// 园区内车辆重载和空载
$info['heavy'] = [];
$info['empty'] = [];
$this->success('请求成功!', $info);
}
//大屏-运单
public function waybill_trend()
{
//运单周数量
// 获取当前日期(包含时间,用于精确查询)
$endDate = date('Y-m-d 23:59:59');
// 计算7天前的日期从当天0点开始
$startDate = date('Y-m-d 00:00:00', strtotime('-6 days'));
// 查询实际数据(只统计未删除的运单)
$map_waybill['is_del'] = 1;
$actualStats = Db::name('waybill')
->where($map_waybill)
->where('create_time', '>=', $startDate)
->where('create_time', '<=', $endDate)
->field("DATE_FORMAT(create_time, '%Y-%m-%d') as date,
COUNT(*) as count")
->group("DATE_FORMAT(create_time, '%Y-%m-%d')")
->select();
// 转换为以日期为键的数组方便查找
$week = ['周日','周一','周二','周三','周四','周五','周六'];
$dailyData = [];
foreach ($actualStats as $stat) {
$weekdayNum = date('w', strtotime($stat['date'])); // 0-6表示周日到周六
$dailyData[$stat['date']] = [
'date' => $stat['date'],
'weekday' => $week[$weekdayNum], // 转换为中文星期
'count' => intval($stat['count']),
];
}
// 构建完整的7天数据包含空日期
$fullWeekStats = [];
for ($i = 6; $i >= 0; $i--) {
$currentDate = date('Y-m-d', strtotime("-{$i} days"));
$weekdayNum = date('w', strtotime($currentDate)); // 0-6表示周日到周六
$weekday = $week[$weekdayNum];
if (isset($dailyData[$currentDate])) {
// 有数据的日期
$fullWeekStats[] = $dailyData[$currentDate];
} else {
// 无数据的日期补零
$fullWeekStats[] = [
'date' => $currentDate,
'weekday' => $weekday,
'count' => 0,
];
}
}
$info['waybill'] = $fullWeekStats;
//企业货物前10 - 按装货单位统计货物总量
$map_enterprise['is_del'] = 1;
$map_enterprise['waybill_loading'] = ['<>', '']; // 装货单位不为空
// 按装货单位分组统计每个企业的货物总量cargocount的SUM
$enterpriseList = Db::name('waybill')
->where($map_enterprise)
->field('waybill_loading, SUM(cargocount) as total_count')
->group('waybill_loading')
->order('total_count desc')
->limit(10)
->select();
$enterpriseNames = [];
$enterpriseNums = [];
if ($enterpriseList) {
foreach ($enterpriseList as $item) {
$enterpriseNames[] = $item['waybill_loading'] ? $item['waybill_loading'] : '未知企业';
$enterpriseNums[] = floatval($item['total_count']); // 货物总量(吨)
}
}
// 如果数据不足10条用空数据补齐前端需要固定10条
$count = count($enterpriseNames);
if ($count < 10) {
for ($i = $count; $i < 10; $i++) {
$enterpriseNames[] = '';
$enterpriseNums[] = 0;
}
}
$info['enterprise'] = [
'name' => $enterpriseNames,
'num' => $enterpriseNums
];
//当日片区货物输入 - 统计装货的运单
$info['in_area'] = $this->getAreaCargoStatistics('in');
//当日片区货物输出 - 统计卸货的运单
$info['out_area'] = $this->getAreaCargoStatistics('out');
$this->success('请求成功!', $info);
}
//大屏-未查验运单列表
public function uncheck_waybill_list()
{
$limit = input('limit', 20); // 默认20条
$map['is_del'] = 1; // 未删除
$map['check_status'] = 0; // 未查验状态
$field = 'id,waybill_order,tow_license,sale_product,waybill_loading,waybill_unloading,waybill_date,create_time';
$list = Waybill::where($map)
->field($field)
->order('create_time desc')
->limit($limit)
->select();
$result = [];
if ($list) {
foreach ($list as $k => $v) {
// 转换为数组格式
$item = [
'id' => $v['id'],
'waybill_order' => $v['waybill_order'] ? $v['waybill_order'] : '',
'tow_license' => $v['tow_license'] ? $v['tow_license'] : '',
'sale_product' => $v['sale_product'] ? $v['sale_product'] : '',
'waybill_loading' => $v['waybill_loading'] ? $v['waybill_loading'] : '',
'waybill_unloading' => $v['waybill_unloading'] ? $v['waybill_unloading'] : '',
];
// 格式化时间
if ($v['waybill_date']) {
$item['waybill_date'] = date('Y-m-d H:i:s', strtotime($v['waybill_date']));
} else {
$item['waybill_date'] = $v['create_time'] ? date('Y-m-d H:i:s', strtotime($v['create_time'])) : '';
}
$result[] = $item;
}
}
$this->success('请求成功!', $result);
}
//大屏-每日企业车辆进出记录
public function enterprise_vehicle($param)
{
if (empty($param['id'])) {
$this->error('异常操作!');
}
$data['vehicle_info'] = Db::name('perimeter')->where('id', $param['id'])->field('stop_vehicle,vehicle_count,enter_vehicle,out_vehicle')->find();
$this->error('请求成功!', $data);
}
//大屏-车辆报警列表
public function vehicle_alarm_list()
{
$page = input('page', 1);
$limit = input('limit', 10);
if (input('key')) {
$map['name'] = array('like', "%" . input('key') . "%");
}
//获取车牌号 - 支持通过车辆ID或车牌号查询
$map['is_del'] = array('neq', -1);
$vehicleNo = input('vehicleNo');
if (is_numeric($vehicleNo)) {
// 如果是数字当作车辆ID处理
$map['license'] = Vehicle::where('id', $vehicleNo)->value('license');
} else {
// 如果是字符串,当作车牌号处理
$map['license'] = $vehicleNo;
}
$list = Alarm::where($map)->order('create_time desc')->page($page)->limit($limit)->select();
$count = Alarm::where($map)->count();
$data['pages'] = ceil($count / $limit);
$data['list'] = $list;
$this->success('请求成功!', $data);
}
//大屏按钮-车辆详情
public function vehicle_detail()
{
$param = input();
if (empty($param['id'])) {
$this->error('异常操作!');
}
$field = 'vehicleNo,plateColor,ownerName,businessScopeName,transdCertificateCode,certificateEffdate,certificateExpdate,licenseIssueOrganCode';
// 支持通过车辆ID或车牌号查询
if (is_numeric($param['id'])) {
// 如果是数字当作车辆ID处理
$vehicle_info = Vehicle::where('id', $param['id'])->field($field)->find();
} else {
// 如果是字符串当作车牌号处理先查询车辆ID
$vehicleId = Vehicle::where('vehicleNo', $param['id'])->value('id');
if (empty($vehicleId)) {
$this->error('未找到该车辆信息!');
}
$vehicle_info = Vehicle::where('id', $vehicleId)->field($field)->find();
}
$url = 'http://47.108.219.88:5000/api/httpserver/vehicleInfo?vehicleNo=' . rawurlencode($vehicle_info['vehicleNo']) . '&plateColor=' . $vehicle_info['plateColor'];
$header = array();
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
$json = json_decode($output, true);
if (empty($json['body'])) {
$list = [
'vehicleNo' => '',
'ownerName' => '',
'licenseIssueOrganCode' => '',
'transCertificateCode' => '',
'transCertificateWord' => '',
'businessScopeName' => '',
'certificateEffdate' => '',
'certificateExpdate' => '',
];
} else {
$list = $json['body'];
}
$vehicle_info = array_merge($vehicle_info, $list);
$this->success('操作成功!', $vehicle_info);
}
//大屏按钮-车辆入园记录列表
public function vehicle_park_list()
{
// 支持通过车辆ID或车牌号查询
$vehicleNoInput = input('vehicleNo');
if (is_numeric($vehicleNoInput)) {
// 如果是数字当作车辆ID处理
$param['vehicleNo'] = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo');
} else {
// 如果是字符串,当作车牌号处理
$param['vehicleNo'] = $vehicleNoInput;
}
if (empty($param['vehicleNo'])) {
$this->error('异常操作!');
}
$page = input('page', 1);
$limit = input('limit', 10);
$map['is_del'] = array('neq', -1);
$map['tractor_license'] = $param['vehicleNo'];
$field = 'tractor_license,mission,status,create_time';
$list = Park::where($map)->order('create_time desc')->field($field)->page($page)->limit($limit)->select();
$count = Park::where($map)->count();
$data['pages'] = ceil($count / $limit);
$data['list'] = $list;
$this->success('请求成功!', $data);
}
//大屏按钮-车辆运单记录列表
public function vehicle_waybill_list()
{
// 支持通过车辆ID或车牌号查询
$vehicleNoInput = input('vehicleNo');
if (is_numeric($vehicleNoInput)) {
// 如果是数字当作车辆ID处理
$param['vehicleNo'] = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo');
} else {
// 如果是字符串,当作车牌号处理
$param['vehicleNo'] = $vehicleNoInput;
}
if (empty($param['vehicleNo'])) {
$this->error('异常操作!');
}
$page = input('page', 1);
$limit = input('limit', 10);
if (isset($param['mission']) && !empty($param['mission'])) {
$map['w_mission'] = $param['mission'];
}
$map['is_del'] = array('neq', -1);
$map['tow_license'] = $param['vehicleNo'];
$list = Waybill::where($map)->order('create_time desc')->page($page)->limit($limit)->select();
$count = Waybill::where($map)->count();
if ($list) {
foreach ($list as $k => &$v) {
$v->check_name = '';
$v->check_status_record = '';
$record_list = EnterpriseCheck::where('waybill_id', $v['id'])->field('check_uid,check_status')->find();
if ($record_list) {
$v->check_name = Perimeter::where('id', $record_list['check_uid'])->value('name');
$v->check_status_record = $record_list['check_status'];
}
}
}
$data['pages'] = ceil($count / $limit);
$data['list'] = $list;
$this->success('请求成功!', $data);
}
//大屏按钮-车辆行驶轨迹
public function vehicle_history()
{
$param = input();
// 支持通过车辆ID或车牌号查询
$vehicleNoInput = input('vehicleNo');
if (is_numeric($vehicleNoInput)) {
// 如果是数字当作车辆ID处理
$vehicleNo = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo');
} else {
// 如果是字符串,当作车牌号处理
$vehicleNo = $vehicleNoInput;
}
if (empty($vehicleNo)) {
$this->error('异常操作!');
}
// 处理日期参数:如果提供了 reservation日期使用该日期否则使用今天
$reservation = input('reservation', '');
if ($reservation) {
// 验证日期格式
$date = strtotime($reservation);
if ($date === false) {
$this->error('日期格式错误!');
}
$dateStr = date('Y-m-d', $date);
} else {
$dateStr = date('Y-m-d');
}
// 查询指定日期的轨迹数据
$table = 'vehicle_log';
$map['vehicleNo'] = $vehicleNo;
// 添加日期过滤:查询指定日期的数据
// vehicle_log 表使用 positionTime 或 V_carettime 字段存储时间
$dateStart = $dateStr . ' 00:00:00';
$dateEnd = $dateStr . ' 23:59:59';
// 查询指定日期的轨迹数据
// 优先使用 positionTime 字段,如果为空则使用 V_carettime 字段
$list = Db::name($table)
->where($map)
->where(function($query) use ($dateStart, $dateEnd) {
// 使用 positionTime 字段进行日期过滤(优先)
$query->where('positionTime', '>=', $dateStart)
->where('positionTime', '<=', $dateEnd);
})
->whereOr(function($query) use ($map, $dateStart, $dateEnd) {
// 如果 positionTime 为空或无效,使用 V_carettime 字段
$query->where($map)
->where(function($q) {
$q->where('positionTime', '')
->whereOr('positionTime', 'null')
->whereOr('positionTime', null);
})
->where('V_carettime', '>=', $dateStart)
->where('V_carettime', '<=', $dateEnd);
})
->field('longitude,latitude,positionTime,V_carettime')
->order('id asc')
->select();
// 如果还是没有结果,尝试只使用 V_carettime 字段(兼容旧数据)
if (empty($list)) {
$list = Db::name($table)
->where($map)
->where('V_carettime', '>=', $dateStart)
->where('V_carettime', '<=', $dateEnd)
->field('longitude,latitude')
->order('id asc')
->select();
}
// 如果仍然没有结果,尝试查询 vehicle_line 表(轨迹数据可能存储在这里)
if (empty($list)) {
// vehicle_line 表存储的是 JSON 格式的轨迹数据
$lineRecord = Db::name('vehicle_line')
->where('vehicleNo', $vehicleNo)
->where('create_time', '>=', $dateStart)
->where('create_time', '<=', $dateEnd)
->where('is_del', 1)
->field('line')
->order('id asc')
->select();
if ($lineRecord && count($lineRecord) > 0) {
// 解析 JSON 格式的轨迹数据
$list = [];
foreach ($lineRecord as $record) {
$lineData = json_decode($record['line'], true);
if (is_array($lineData) && count($lineData) > 0) {
foreach ($lineData as $point) {
if (isset($point['coordinate']) && !empty($point['coordinate'])) {
$coords = explode(',', $point['coordinate']);
if (count($coords) >= 2) {
$list[] = [
'longitude' => floatval($coords[0]),
'latitude' => floatval($coords[1])
];
}
}
}
}
}
}
}
$coordinate = '';
if ($list && count($list) > 0) {
foreach ($list as $k => $v) {
if (empty($v['longitude']) || empty($v['latitude'])) {
continue;
}
$str = $this->getWgs84($v['longitude'] . ',' . $v['latitude']);
$coordinate .= $str['lon'] . ',' . $str['lat'] . ';';
}
if (!empty($coordinate)) {
$coordinate = substr($coordinate, 0, strlen($coordinate) - 1);
}
}
// 如果没有查询到数据,返回默认值
if (empty($coordinate)) {
$coordinate = '106.964108,29.793287';//默认初始值
}
$data['coordinate'] = $coordinate;
$this->success('请求成功!', $data);
}
//车辆码 一小时请求一次
public function getHourData()
{
//园区当月车辆进出列表
$month_arr = [];
$m = date('m');
$y = date('Y');
$month = Date::days_in_month(date($m), date($y));
for ($i = 0; $i < $month; $i++) {
// 格式化日期确保是两位数2025-11-01 而不是 2025-11-1
$day = str_pad($i + 1, 2, '0', STR_PAD_LEFT);
$month_arr[$i]['month'] = $y . '-' . $m . '-' . $day;
$month_arr[$i]['green_count'] = 0;
$month_arr[$i]['yellow_count'] = 0;
$month_arr[$i]['red_count'] = 0;
}
// 方案1优先使用 vehicle_park_line_log 表(如果表存在且有数据)
// 方案2如果 vehicle_park_line_log 表没有数据,则使用 vehicle 表统计每天首次出现的车辆
foreach ($month_arr as $k => $v) {
$dateStr = $v['month'];
$dateStart = $dateStr . ' 00:00:00';
$dateEnd = $dateStr . ' 23:59:59';
// 先尝试从 vehicle_park_line_log 表查询
$park_log = ParkLineLog::where('is_del', 1)
->where('type', 1)
->whereTime('create_time', 'between', [$dateStart, $dateEnd])
->field('vehicleNo,create_time')
->select();
if ($park_log && count($park_log) > 0) {
// 使用 vehicle_park_line_log 表的数据
foreach ($park_log as $a => $b) {
$code = Vehicle::where('vehicleNo', $b['vehicleNo'])
->where('is_del', 'in', '1,2')
->value('qr_color');
if ($code == 1) {
$month_arr[$k]['green_count'] += 1;
} elseif ($code == 2) {
$month_arr[$k]['yellow_count'] += 1;
} elseif ($code == 3) {
$month_arr[$k]['red_count'] += 1;
}
}
} else {
// 如果 vehicle_park_line_log 表没有数据,使用 vehicle 表统计
// 统计每天首次出现的车辆(按车牌号去重)
$vehicles = Db::name('vehicle')
->where('is_del', 'in', '1,2')
->where('create_time', '>=', $dateStart)
->where('create_time', '<=', $dateEnd)
->field('vehicleNo, qr_color')
->group('vehicleNo')
->select();
if ($vehicles) {
foreach ($vehicles as $vehicle) {
$code = $vehicle['qr_color'];
if ($code == 1) {
$month_arr[$k]['green_count'] += 1;
} elseif ($code == 2) {
$month_arr[$k]['yellow_count'] += 1;
} elseif ($code == 3) {
$month_arr[$k]['red_count'] += 1;
}
}
}
}
}
$info['month_arr'] = $month_arr;//车辆码 一个小时自动请求
//当日园区进入车辆数
$park_same_day_count = ParkLineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'type' => 1])->count();
//当日入园上报数
$apply_count = Park::whereTime('create_time', 'd')->where('status', 1)->count();
//当日运单上报数
$waybill_count = Waybill::whereTime('create_time', 'd')->where('is_del', 1)->count();
$report_count = $apply_count + $waybill_count;
$info['park_same_day_count'] = $park_same_day_count;
$info['report_count'] = $report_count;
//当日入园未上报数
$info['no_report_count'] = $park_same_day_count - $report_count;
$this->success('请求成功!', $info);
}
public function getWgs84($par)
{
$result = array(); // 转换后的结果
$tokens = preg_split('/[\r\n]+/', $par);
foreach ($tokens as $token) {
if (false !== strpos($token, '=')) {
list($key, $value) = explode('=', $token, 2);
$result[$key] = $value;
} else
$result[] = $token;
}
foreach ($result as $k => $v) {
if ($v) {
$Varr = explode(',', $v);
$res = $this->bd_decrypt($Varr[0], $Varr[1]);
$arr = [
'lon' => number_format($res['gg_lon'], 6),
'lat' => number_format($res['gg_lat'], 6),
];
}
}
return $arr;
}
//BD-09(百度) 坐标转换成 GCJ-02(火星,高德) 坐标
//@param bd_lon 百度经度
//@param bd_lat 百度纬度
public function bd_decrypt($bd_lon, $bd_lat)
{
$x_pi = 3.14159265358979324 * 3000.0 / 180.0;
$x = $bd_lon - 0.0065;
$y = $bd_lat - 0.006;
$z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
$theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
$data['gg_lon'] = $z * cos($theta);
$data['gg_lat'] = $z * sin($theta);
return $data;
}
/**
* 统计每个片区的车辆数量
* @return array
*/
private function getAreaVehicleStatistics()
{
// 查询所有企业区域region_type=6作为片区
$areas = Perimeter::where('region_type', 6)
->where('is_del', 1)
->where('info', '<>', '')
->field('id,name,info')
->select();
if (empty($areas)) {
// 如果没有配置片区,返回空数组
return [];
}
// 查询所有车辆
$vehicles = Vehicle::where('is_del', 'in', '1,2')
->field('id,vehicleNo,longitude,latitude')
->select();
$areaStats = [];
// 遍历每个片区
foreach ($areas as $area) {
// 解析片区坐标
$polygon = $this->parsePerimeterCoords($area['info']);
if (empty($polygon)) {
continue;
}
$vehicleCount = 0;
// 统计在该片区内的车辆数量
foreach ($vehicles as $vehicle) {
if (empty($vehicle['longitude']) || empty($vehicle['latitude'])) {
continue;
}
$point = [
'lng' => floatval($vehicle['longitude']),
'lat' => floatval($vehicle['latitude'])
];
if ($this->is_point_in_polygon($point, $polygon)) {
$vehicleCount++;
}
}
$areaStats[] = [
'name' => $area['name'],
'value' => $vehicleCount
];
}
// 如果没有统计数据,返回空数组
return $areaStats;
}
/**
* 统计每条道路上的车辆数量
* @return array
*/
private function getRoadVehicleStatistics()
{
// 查询所有道路region_type=5
$roads = Perimeter::where('region_type', 5)
->where('is_del', 1)
->where('info', '<>', '')
->field('id,name,info')
->select();
if (empty($roads)) {
// 如果没有配置道路,返回空数组
return [];
}
// 查询所有车辆
$vehicles = Vehicle::where('is_del', 'in', '1,2')
->field('id,vehicleNo,longitude,latitude')
->select();
$roadStats = [];
// 遍历每条道路
foreach ($roads as $road) {
// 解析道路坐标
$polygon = $this->parsePerimeterCoords($road['info']);
if (empty($polygon)) {
// 如果没有坐标信息返回0
$roadStats[] = [
'id' => $road['id'],
'name' => $road['name'],
'count' => 0
];
continue;
}
$vehicleCount = 0;
// 统计在该道路上的车辆数量
foreach ($vehicles as $vehicle) {
if (empty($vehicle['longitude']) || empty($vehicle['latitude'])) {
continue;
}
$point = [
'lng' => floatval($vehicle['longitude']),
'lat' => floatval($vehicle['latitude'])
];
if ($this->is_point_in_polygon($point, $polygon)) {
$vehicleCount++;
}
}
$roadStats[] = [
'id' => $road['id'],
'name' => $road['name'],
'count' => $vehicleCount
];
}
// 如果没有统计数据,返回空数组
return $roadStats;
}
/**
* 统计片区货物输入/输出
* @param string $type 'in' 表示货物输入(装货),'out' 表示货物输出(卸货)
* @return array
*/
private function getAreaCargoStatistics($type = 'in')
{
// 查询所有企业区域region_type=6作为片区
$areas = Perimeter::where('region_type', 6)
->where('is_del', 1)
->field('id,name')
->select();
if (empty($areas)) {
// 如果没有配置片区,返回空数组
return [];
}
// 获取当天的日期使用waybill_date字段表示提货日期
$todayDate = date('Y-m-d');
// 根据类型确定查询字段和条件
if ($type === 'in') {
// 货物输入:统计装货的运单
$enterpriseField = 'waybill_loading';
$missionCondition = '装货';
} else {
// 货物输出:统计卸货的运单
$enterpriseField = 'waybill_unloading';
$missionCondition = '卸货';
}
// 查询当日的运单数据(按提货日期统计)
$waybillMap = [
'is_del' => 1,
'w_mission' => $missionCondition,
$enterpriseField => ['<>', ''], // 装货/卸货单位不为空
];
$waybills = Db::name('waybill')
->where($waybillMap)
->whereTime('waybill_date', 'd') // 使用waybill_date字段统计当天的运单
->field("{$enterpriseField} as enterprise_name, SUM(cargocount) as total_count")
->group($enterpriseField)
->select();
// 构建企业名称到货物数量的映射
$enterpriseCargoMap = [];
if ($waybills) {
foreach ($waybills as $waybill) {
$enterpriseName = $waybill['enterprise_name'];
$enterpriseCargoMap[$enterpriseName] = floatval($waybill['total_count']);
}
}
// 统计每个片区的货物总量
$areaStats = [];
foreach ($areas as $area) {
$areaName = $area['name'];
$totalCargo = 0;
// 遍历运单数据,匹配装货/卸货单位与片区名称
// 这里使用名称匹配,如果装货/卸货单位包含片区名称,则计入该片区
foreach ($enterpriseCargoMap as $enterpriseName => $cargoCount) {
// 如果企业名称与片区名称匹配(完全匹配或包含关系)
if ($enterpriseName == $areaName || strpos($enterpriseName, $areaName) !== false || strpos($areaName, $enterpriseName) !== false) {
$totalCargo += $cargoCount;
}
}
$areaStats[] = [
'name' => $areaName,
'value' => round($totalCargo, 2) // 保留2位小数
];
}
// 如果没有统计数据至少返回片区名称值为0
if (empty($areaStats)) {
foreach ($areas as $area) {
$areaStats[] = [
'name' => $area['name'],
'value' => 0
];
}
}
return $areaStats;
}
/**
* 解析周界坐标字符串为多边形数组
* @param string $info 坐标字符串格式lng,lat;lng,lat;...
* @return array
*/
private function parsePerimeterCoords($info)
{
if (empty($info)) {
return [];
}
$polygon = [];
$points = explode(';', $info);
foreach ($points as $point) {
$coords = explode(',', $point);
if (count($coords) >= 2) {
$polygon[] = [
'lng' => floatval(trim($coords[0])),
'lat' => floatval(trim($coords[1]))
];
}
}
return $polygon;
}
/**
* 判断一个坐标是否在一个多边形内
* @param array $point 点坐标 ['lng' => x, 'lat' => y]
* @param array $pts 多边形坐标数组
* @return bool
*/
private function is_point_in_polygon($point, $pts)
{
$N = count($pts);
if ($N < 3) {
return false;
}
$boundOrVertex = true;
$intersectCount = 0;
$precision = 2e-10;
$p1 = 0;
$p2 = 0;
$p = $point;
$p1 = $pts[0];
for ($i = 1; $i <= $N; ++$i) {
if ($p['lng'] == $p1['lng'] && $p['lat'] == $p1['lat']) {
return $boundOrVertex;
}
$p2 = $pts[$i % $N];
if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) {
$p1 = $p2;
continue;
}
if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) {
if($p['lng'] <= max($p1['lng'], $p2['lng'])){
if ($p1['lat'] == $p2['lat'] && $p['lng'] >= min($p1['lng'], $p2['lng'])) {
return $boundOrVertex;
}
if ($p1['lng'] == $p2['lng']) {
if ($p1['lng'] == $p['lng']) {
return $boundOrVertex;
} else {
++$intersectCount;
}
} else {
$xinters = ($p['lat'] - $p1['lat']) * ($p2['lng'] - $p1['lng']) / ($p2['lat'] - $p1['lat']) + $p1['lng'];
if (abs($p['lng'] - $xinters) < $precision) {
return $boundOrVertex;
}
if ($p['lng'] < $xinters) {
++$intersectCount;
}
}
}
} else {
if ($p['lat'] == $p2['lat'] && $p['lng'] <= $p2['lng']) {
$p3 = $pts[($i+1) % $N];
if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) {
++$intersectCount;
} else {
$intersectCount += 2;
}
}
}
$p1 = $p2;
}
if ($intersectCount % 2 == 0) {
return false;
} else {
return true;
}
}
}