设置威胁区域 #12

Merged
pg9nrcf7t merged 1 commits from lcf_branch into main 3 months ago

@ -25,12 +25,39 @@
#include <cmath>
#include "mapbridge.h"
MapPage::MapPage(QWidget *parent) : QWidget(parent), mapArea_(nullptr), heightCombo_(nullptr), downloadMapBtn_(nullptr), mapView_(nullptr), setThreatBtn_(nullptr), areaSearchBtn_(nullptr), pathPlanningBtn_(nullptr), areaCoverageBtn_(nullptr), coordInput_(nullptr), locateBtn_(nullptr), searchMapBtn_(nullptr), threatDialog_(nullptr), searchDialog_(nullptr), planningDialog_(nullptr), coverageDialog_(nullptr), locateDialog_(nullptr), baseFontSize_(10), pathOverlayId_("pathOverlay"), startMarkerId_("startMarker"), endMarkerId_("endMarker"), currentPathData_(""), bridge_(nullptr), channel_(nullptr), coverageOverlayId_("coverageOverlay"), areaOverlayId_("areaOverlay"), currentCoveragePathData_("") {
MapPage::MapPage(QWidget *parent) : QWidget(parent),
mapArea_(nullptr),
heightCombo_(nullptr),
downloadMapBtn_(nullptr),
mapView_(nullptr),
setThreatBtn_(nullptr),
pathPlanningBtn_(nullptr),
areaCoverageBtn_(nullptr),
coordInput_(nullptr),
locateBtn_(nullptr),
searchMapBtn_(nullptr),
threatDialog_(nullptr),
planningDialog_(nullptr),
coverageDialog_(nullptr),
locateDialog_(nullptr),
baseFontSize_(10),
pathOverlayId_("pathOverlay"),
startMarkerId_("startMarker"),
endMarkerId_("endMarker"),
currentPathData_(""),
bridge_(nullptr),
channel_(nullptr),
coverageOverlayId_("coverageOverlay"),
areaOverlayId_("areaOverlay"),
currentCoveragePathData_(""),
isDrawing_(false),
drawingShape_(""),
drawingPoints_(),
threatAreas_() {
setupUI();
enableGeolocation();
qDebug() << "MapPage 构造函数完成";
threatDialog_ = new ThreatAreaDialog(this);
searchDialog_ = new AreaSearchDialog(this);
threatDialog_ = new ThreatAreaDialog(this, this);
planningDialog_ = new PathPlanningDialog(this, this);
coverageDialog_ = new AreaCoverageDialog(this, this);
locateDialog_ = new LocateDialog(this);
@ -94,7 +121,27 @@ void MapPage::setupMapArea() {
window.onerror = function(msg, url, line) { console.log("JS 错误: " + msg + " at line " + line); };
</script>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="https://webapi.amap.com/maps?v=2.0&key=492dc9daf4eae7cab678c0f3efed8198&callback=initMap" async defer></script>
<script>
// Wait for WebChannel before loading AMap SDK
function loadAMap() {
var script = document.createElement('script');
script.src = 'https://webapi.amap.com/maps?v=2.0&key=492dc9daf4eae7cab678c0f3efed8198&callback=initMap';
script.async = true;
script.defer = true;
document.head.appendChild(script);
}
if (typeof QWebChannel !== 'undefined') {
new QWebChannel(qt.webChannelTransport, function(channel) {
window.bridge = channel.objects.bridge;
console.log('Bridge connected, loading AMap SDK');
loadAMap();
});
} else {
console.error('QWebChannel not loaded, cannot connect bridge');
loadAMap(); // 尝试加载,但可能失败
}
</script>
<script>
function initMap() {
console.log('amap js ok');
@ -178,49 +225,77 @@ void MapPage::setupMapArea() {
extensions: 'all'
});
// 保留geolocation控件用于手动定位但不自动调用getCurrentPosition
// 添加 geolocation 控件并自动尝试 GPS 定位
window.map.addControl(geolocation);
// 使用IP定位作为首要方式
console.log("开始IP定位...");
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
console.log("IP API响应:", JSON.stringify(data));
if (data && data.city && data.latitude && data.longitude) {
var lnglat = new AMap.LngLat(data.longitude, data.latitude);
window.map.setCenter(lnglat);
window.map.setZoom(12);
console.log("IP定位成功: " + data.city + " at " + lnglat);
// 添加定位成功标记
var marker = new AMap.Marker({
position: lnglat,
title: ' (' + data.city + ')',
content: '<div style="background:#4CAF50; color:white; padding:4px 8px; border-radius:50%%; font-size:12px; font-weight:bold;"></div>',
animation: 'AMAP_ANIMATION_DROP'
});
marker.setMap(window.map);
// 添加定位信息窗口
var infoWindow = new AMap.InfoWindow({
content: '<div>IP<br>: ' + data.city + '<br>: ' + data.longitude + '<br>: ' + data.latitude + '</div>',
offset: new AMap.Pixel(0, -30)
// 自动调用 GPS 定位
geolocation.getCurrentPosition(function(status, result) {
if (status == 'complete') {
var position = result.position;
var lnglat = new AMap.LngLat(position.lng, position.lat);
window.map.setCenter(lnglat);
window.map.setZoom(15); // 更高缩放级别以显示街道
console.log("GPS定位成功: 经度 " + position.lng + ", 纬度 " + position.lat + ", 精度 " + result.accuracy);
// 添加 GPS 定位成功标记 (蓝色)
var marker = new AMap.Marker({
position: lnglat,
title: 'GPS Position',
content: '<div style="background:#2196F3; color:white; padding:4px 8px; border-radius:50%%; font-size:12px;">GPS </div>',
animation: 'AMAP_ANIMATION_DROP'
});
marker.setMap(window.map);
// 添加 GPS 信息窗口
var infoContent = '<div>GPS<br>: ' + position.lng + '<br>: ' + position.lat;
if (result.accuracy) infoContent += '<br>: ' + result.accuracy + '';
if (result.info) infoContent += '<br>: ' + result.info;
infoContent += '</div>';
var infoWindow = new AMap.InfoWindow({
content: infoContent,
offset: new AMap.Pixel(0, -30)
});
infoWindow.open(window.map, lnglat);
} else {
console.log("GPS定位失败: " + result.message);
// GPS 失败,回退到 IP 定位
console.log("开始IP定位...");
fetch('https://ipapi.co/json/')
.then(response => response.json())
.then(data => {
console.log("IP API响应:", JSON.stringify(data));
if (data && data.latitude && data.longitude) {
var lnglat = new AMap.LngLat(data.longitude, data.latitude);
window.map.setCenter(lnglat);
window.map.setZoom(12);
console.log("IP定位成功: " + (data.city || '') + " at " + lnglat);
// 添加 IP 定位标记
var marker = new AMap.Marker({
position: lnglat,
title: 'IP Position (' + (data.city || '') + ')',
animation: 'AMAP_ANIMATION_DROP'
});
marker.setMap(window.map);
// 添加 IP 信息窗口
var infoWindow = new AMap.InfoWindow({
content: '<div>IP<br>: ' + (data.city || '') + '<br>: ' + data.longitude + '<br>: ' + data.latitude + '</div>',
offset: new AMap.Pixel(0, -30)
});
infoWindow.open(window.map, lnglat);
} else {
console.log("IP定位数据无效使用默认位置");
useDefaultPosition();
}
})
.catch(err => {
console.log("IP API请求失败: " + err);
console.log("使用默认位置");
useDefaultPosition();
});
infoWindow.open(window.map, lnglat);
} else {
console.log("IP定位数据无效使用默认位置");
useDefaultPosition();
}
})
.catch(err => {
console.log("IP API请求失败: " + err);
console.log("使用默认位置");
useDefaultPosition();
});
// 可选:添加持续监听位置变化(如果需要实时更新)
// geolocation.watchPosition();
}
});
// 辅助函数:使用默认位置
function useDefaultPosition() {
@ -281,11 +356,6 @@ QWidget* MapPage::createControlBarWidget() {
connect(searchMapBtn_, &QPushButton::clicked, this, &MapPage::onSearchMapClicked);
layout->addWidget(searchMapBtn_);
// 添加区域搜索按钮
areaSearchBtn_ = new QPushButton("区域搜索");
areaSearchBtn_->setProperty("primary", true);
connect(areaSearchBtn_, &QPushButton::clicked, this, &MapPage::onAreaSearchClicked);
layout->addWidget(areaSearchBtn_);
downloadMapBtn_ = new QPushButton("地图下载");
downloadMapBtn_->setProperty("primary", true);
@ -372,9 +442,6 @@ void MapPage::onAreaCoverageClicked() {
}
}
void MapPage::onAreaSearchClicked() {
searchDialog_->exec();
}
void MapPage::runMapJavaScript(const QString& js) {
@ -472,7 +539,7 @@ void MapPage::visualizeCoveragePath(const QString& pathData) {
for (var i = 0; i < pathCoords.length; i += 5) { // Every 5th point for visibility
var marker = new AMap.Marker({
position: pathCoords[i],
content: '<div style="background:#4CAF50; color:white; padding:2px 6px; border-radius:50%; font-size:10px; font-weight:bold;">C' + (i/5 + 1) + '</div>',
content: '<div style="background:#4CAF50; color:white; padding:2px 6px; border-radius:50%%; font-size:10px; font-weight:bold;">C' + (i/5 + 1) + '</div>',
zIndex: 15
});
marker.setMap(window.map);
@ -519,7 +586,6 @@ void MapPage::clearCoverageOverlays() {
for (var i = 0; i < window.coverageIntermediates.length; i++) {
if (window.coverageIntermediates[i]) {
window.map.remove(window.coverageIntermediates[i]);
console.log('Removed coverage intermediate marker ' + i);
}
}
window.coverageIntermediates = [];
@ -724,7 +790,7 @@ void MapPage::onSearchMapClicked() {
}
// Implement ThreatAreaDialog methods
ThreatAreaDialog::ThreatAreaDialog(QWidget *parent) : QDialog(parent) {
ThreatAreaDialog::ThreatAreaDialog(QWidget *parent, MapPage* mapPage) : QDialog(parent), mapPage_(mapPage), drawingPoints_() {
setWindowTitle("设置威胁区域");
setMinimumSize(600, 500);
auto* layout = new QVBoxLayout(this);
@ -746,115 +812,93 @@ ThreatAreaDialog::ThreatAreaDialog(QWidget *parent) : QDialog(parent) {
// 威胁等级
basicLayout->addWidget(new QLabel("威胁等级:"), 0, 2);
auto* levelCombo = new QComboBox();
levelCombo->addItems({"", "", ""});
levelCombo->setStyleSheet("QComboBox { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
basicLayout->addWidget(levelCombo, 0, 3);
levelCombo_ = new QComboBox();
levelCombo_->addItems({"", "", ""});
levelCombo_->setStyleSheet("QComboBox { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
basicLayout->addWidget(levelCombo_, 0, 3);
layout->addWidget(basicGroup);
// 添加形状选择
basicLayout->addWidget(new QLabel("形状:"), 1, 0);
shapeCombo_ = new QComboBox();
shapeCombo_->addItems({"圆形", "矩形"});
shapeCombo_->setStyleSheet("QComboBox { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
connect(shapeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThreatAreaDialog::onShapeChanged);
basicLayout->addWidget(shapeCombo_, 1, 1);
// 添加颜色选择(假设用于 overlay 颜色)
basicLayout->addWidget(new QLabel("颜色:"), 1, 2);
colorCombo_ = new QComboBox();
colorCombo_->addItems({"红色", "黄色", "蓝色"});
basicLayout->addWidget(colorCombo_, 1, 3);
layout->addWidget(basicGroup);
// 时间设置组
// 时间设置组 (保持不变)
auto* timeGroup = new QGroupBox("威胁区域存在时间");
timeGroup->setStyleSheet("QGroupBox { font-weight: bold; margin-top: 10px; }");
auto* timeLayout = new QVBoxLayout(timeGroup);
// 时间模式选择
auto* timeModeLayout = new QHBoxLayout();
auto* singleTimeMode = new QRadioButton("单时间点");
auto* timeRangeMode = new QRadioButton("时间段");
singleTimeMode->setChecked(true);
timeModeLayout->addWidget(singleTimeMode);
timeModeLayout->addWidget(timeRangeMode);
timeModeLayout->addStretch();
timeLayout->addLayout(timeModeLayout);
// 单时间点设置
auto* singleTimeWidget = new QWidget();
auto* singleTimeLayout = new QHBoxLayout(singleTimeWidget);
singleTimeLayout->setContentsMargins(0, 0, 0, 0);
singleTimeLayout->addWidget(new QLabel("时间:"));
timeEdit_ = new QDateTimeEdit(QDateTime::currentDateTime());
timeEdit_->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
timeEdit_->setCalendarPopup(true);
timeEdit_->setStyleSheet("QDateTimeEdit { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
singleTimeLayout->addWidget(timeEdit_);
auto* setCurrentBtn = new QPushButton("设为当前时间");
setCurrentBtn->setStyleSheet("QPushButton { padding: 5px 10px; background: #e3f2fd; border: 1px solid #2196f3; border-radius: 4px; color: #1976d2; }");
singleTimeLayout->addWidget(setCurrentBtn);
singleTimeLayout->addStretch();
timeLayout->addWidget(singleTimeWidget);
// 时间段设置
auto* timeRangeWidget = new QWidget();
auto* timeRangeLayout = new QVBoxLayout(timeRangeWidget);
timeRangeLayout->setContentsMargins(0, 0, 0, 0);
// 开始时间
auto* startTimeLayout = new QHBoxLayout();
startTimeLayout->addWidget(new QLabel("开始时间:"));
auto* startTimeEdit = new QDateTimeEdit(QDateTime::currentDateTime());
startTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
startTimeEdit->setCalendarPopup(true);
startTimeEdit->setStyleSheet("QDateTimeEdit { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
startTimeLayout->addWidget(startTimeEdit);
auto* setStartCurrentBtn = new QPushButton("设为当前");
setStartCurrentBtn->setStyleSheet("QPushButton { padding: 5px 10px; background: #e8f5e8; border: 1px solid #4caf50; border-radius: 4px; color: #2e7d32; }");
startTimeLayout->addWidget(setStartCurrentBtn);
startTimeEdit_ = new QDateTimeEdit(QDateTime::currentDateTime());
startTimeEdit_->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
startTimeEdit_->setCalendarPopup(true);
startTimeEdit_->setStyleSheet("QDateTimeEdit { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
startTimeLayout->addWidget(startTimeEdit_);
startTimeLayout->addStretch();
timeRangeLayout->addLayout(startTimeLayout);
timeLayout->addLayout(startTimeLayout);
// 结束时间
auto* endTimeLayout = new QHBoxLayout();
endTimeLayout->addWidget(new QLabel("结束时间:"));
auto* endTimeEdit = new QDateTimeEdit(QDateTime::currentDateTime().addSecs(3600)); // 默认1小时后
endTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
endTimeEdit->setCalendarPopup(true);
endTimeEdit->setStyleSheet("QDateTimeEdit { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
endTimeLayout->addWidget(endTimeEdit);
auto* setEndCurrentBtn = new QPushButton("设为当前");
setEndCurrentBtn->setStyleSheet("QPushButton { padding: 5px 10px; background: #e8f5e8; border: 1px solid #4caf50; border-radius: 4px; color: #2e7d32; }");
endTimeLayout->addWidget(setEndCurrentBtn);
endTimeEdit_ = new QDateTimeEdit(QDateTime::currentDateTime().addSecs(3600)); // 默认1小时后
endTimeEdit_->setDisplayFormat("yyyy-MM-dd hh:mm:ss");
endTimeEdit_->setCalendarPopup(true);
endTimeEdit_->setStyleSheet("QDateTimeEdit { padding: 5px; border: 1px solid #ccc; border-radius: 4px; }");
endTimeLayout->addWidget(endTimeEdit_);
endTimeLayout->addStretch();
timeRangeLayout->addLayout(endTimeLayout);
// 持续时间显示和快速设置
auto* durationLayout = new QHBoxLayout();
auto* durationLabel = new QLabel("持续时间: 1小时");
durationLabel->setStyleSheet("QLabel { color: #666; font-weight: bold; }");
durationLayout->addWidget(durationLabel);
durationLayout->addStretch();
// 快速时间设置按钮
auto* quickTimeLayout = new QHBoxLayout();
auto* quick1Hour = new QPushButton("1小时");
auto* quick4Hour = new QPushButton("4小时");
auto* quick1Day = new QPushButton("1天");
auto* quick1Week = new QPushButton("1周");
QString quickBtnStyle = "QPushButton { padding: 4px 8px; background: #f5f5f5; border: 1px solid #ddd; border-radius: 3px; font-size: 11px; }";
quick1Hour->setStyleSheet(quickBtnStyle);
quick4Hour->setStyleSheet(quickBtnStyle);
quick1Day->setStyleSheet(quickBtnStyle);
quick1Week->setStyleSheet(quickBtnStyle);
quickTimeLayout->addWidget(quick1Hour);
quickTimeLayout->addWidget(quick4Hour);
quickTimeLayout->addWidget(quick1Day);
quickTimeLayout->addWidget(quick1Week);
durationLayout->addLayout(quickTimeLayout);
timeRangeLayout->addLayout(durationLayout);
timeLayout->addWidget(timeRangeWidget);
timeLayout->addLayout(endTimeLayout);
layout->addWidget(timeGroup);
// 按钮区域
// 动态输入 widget
circleInputWidget_ = new QWidget();
auto* circleLayout = new QFormLayout(circleInputWidget_);
centerLngInput_ = new QLineEdit();
centerLngInput_->setPlaceholderText("中心经度");
circleLayout->addRow("中心经度:", centerLngInput_);
centerLatInput_ = new QLineEdit();
centerLatInput_->setPlaceholderText("中心纬度");
circleLayout->addRow("中心纬度:", centerLatInput_);
radiusInput_ = new QLineEdit();
radiusInput_->setPlaceholderText("半径 (米)");
circleLayout->addRow("半径 (米):", radiusInput_);
layout->addWidget(circleInputWidget_);
rectangleInputWidget_ = new QWidget();
auto* rectLayout = new QVBoxLayout(rectangleInputWidget_);
rectPointsTable_ = new QTableWidget(4, 2);
rectPointsTable_->setHorizontalHeaderLabels({"经度", "纬度"});
for (int i = 0; i < 4; ++i) {
rectPointsTable_->setItem(i, 0, new QTableWidgetItem(""));
rectPointsTable_->setItem(i, 1, new QTableWidgetItem(""));
}
rectLayout->addWidget(rectPointsTable_);
layout->addWidget(rectangleInputWidget_);
rectangleInputWidget_->setVisible(false);
// 按钮 (保持 drawOnMapBtn_ 等)
auto* buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
drawOnMapBtn_ = new QPushButton("在地图上绘制");
drawOnMapBtn_->setStyleSheet("QPushButton { padding: 8px 20px; background: #4CAF50; color: white; border-radius: 4px; }");
connect(drawOnMapBtn_, &QPushButton::clicked, this, &ThreatAreaDialog::startDrawingThreatArea);
buttonLayout->addWidget(drawOnMapBtn_);
auto* addBtn = new QPushButton("添加区域");
addBtn->setProperty("primary", true);
addBtn->setStyleSheet("QPushButton { padding: 8px 20px; background: #2196f3; color: white; border-radius: 4px; }");
@ -869,20 +913,232 @@ ThreatAreaDialog::ThreatAreaDialog(QWidget *parent) : QDialog(parent) {
// 连接信号槽
connect(addBtn, &QPushButton::clicked, this, &ThreatAreaDialog::addArea);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
// 初始化 areaTable_
auto* tableGroup = new QGroupBox("已添加威胁区域");
auto* tableLayout = new QVBoxLayout(tableGroup);
areaTable_ = new QTableWidget();
areaTable_->setColumnCount(6);
areaTable_->setHorizontalHeaderLabels({"类型", "等级", "开始时间", "结束时间", "形状", "细节"});
areaTable_->setSelectionBehavior(QAbstractItemView::SelectRows); // 整行选择
connect(areaTable_, &QTableWidget::itemSelectionChanged, this, [this]() {
auto selected = areaTable_->selectedItems();
if (!selected.isEmpty()) {
selectedRow_ = selected.first()->row();
} else {
selectedRow_ = -1;
}
});
tableLayout->addWidget(areaTable_);
// 添加编辑/删除按钮
auto* buttonLayoutTable = new QHBoxLayout();
editBtn_ = new QPushButton("编辑选中区域");
connect(editBtn_, &QPushButton::clicked, this, &ThreatAreaDialog::editArea);
buttonLayoutTable->addWidget(editBtn_);
deleteBtn_ = new QPushButton("删除选中区域");
connect(deleteBtn_, &QPushButton::clicked, this, &ThreatAreaDialog::deleteArea);
buttonLayoutTable->addWidget(deleteBtn_);
tableLayout->addLayout(buttonLayoutTable);
layout->addWidget(tableGroup);
// 填充现有区域
const auto& areas = mapPage_->getThreatAreas();
for (const auto& area : areas) {
int row = areaTable_->rowCount();
areaTable_->insertRow(row);
areaTable_->setItem(row, 0, new QTableWidgetItem(area["type"].toString()));
areaTable_->setItem(row, 1, new QTableWidgetItem(area["level"].toString()));
areaTable_->setItem(row, 2, new QTableWidgetItem(area["startTime"].toString()));
areaTable_->setItem(row, 3, new QTableWidgetItem(area["endTime"].toString()));
areaTable_->setItem(row, 4, new QTableWidgetItem(area["shape"].toString()));
QString details;
if (area["shape"].toString() == "circle") {
details = QString("中心: %1,%2 半径: %3m").arg(area["centerLng"].toDouble()).arg(area["centerLat"].toDouble()).arg(area["radius"].toDouble());
} else {
details = "矩形点..."; // 简化
}
areaTable_->setItem(row, 5, new QTableWidgetItem(details));
}
}
void ThreatAreaDialog::onShapeChanged(int index) {
currentShape_ = shapeCombo_->currentText();
circleInputWidget_->setVisible(index == 0);
rectangleInputWidget_->setVisible(index == 1);
}
void ThreatAreaDialog::startDrawingThreatArea() {
if (!mapPage_) return;
currentShape_ = shapeCombo_->currentText();
QMessageBox::information(this, "提示", QString("现在在地图上绘制%1。点击添加点。").arg(currentShape_));
hide(); // 隐藏对话框以允许地图交互
mapPage_->enableDrawingMode(currentShape_ == "圆形" ? "circle" : "rectangle");
connect(mapPage_, &MapPage::mapClicked, this, &ThreatAreaDialog::handleDrawingClick); // 临时连接
}
void ThreatAreaDialog::handleDrawingClick(double lng, double lat) {
drawingPoints_.append(qMakePair(lng, lat));
if (currentShape_ == "圆形" && drawingPoints_.size() == 2) {
// 计算中心和半径
double centerLng = drawingPoints_[0].first;
double centerLat = drawingPoints_[0].second;
double dx = drawingPoints_[1].first - centerLng;
double dy = drawingPoints_[1].second - centerLat;
double radius = sqrt(dx*dx + dy*dy) * 111000; // 粗略转换为米 (1度 ~ 111km)
centerLngInput_->setText(QString::number(centerLng));
centerLatInput_->setText(QString::number(centerLat));
radiusInput_->setText(QString::number(radius));
finishDrawing();
} else if (currentShape_ == "矩形" && drawingPoints_.size() == 2) {
// 计算矩形四个点 (假设两个对角点)
double minLng = qMin(drawingPoints_[0].first, drawingPoints_[1].first);
double maxLng = qMax(drawingPoints_[0].first, drawingPoints_[1].first);
double minLat = qMin(drawingPoints_[0].second, drawingPoints_[1].second);
double maxLat = qMax(drawingPoints_[0].second, drawingPoints_[1].second);
rectPointsTable_->setItem(0, 0, new QTableWidgetItem(QString::number(minLng)));
rectPointsTable_->setItem(0, 1, new QTableWidgetItem(QString::number(minLat)));
rectPointsTable_->setItem(1, 0, new QTableWidgetItem(QString::number(maxLng)));
rectPointsTable_->setItem(1, 1, new QTableWidgetItem(QString::number(minLat)));
rectPointsTable_->setItem(2, 0, new QTableWidgetItem(QString::number(maxLng)));
rectPointsTable_->setItem(2, 1, new QTableWidgetItem(QString::number(maxLat)));
rectPointsTable_->setItem(3, 0, new QTableWidgetItem(QString::number(minLng)));
rectPointsTable_->setItem(3, 1, new QTableWidgetItem(QString::number(maxLat)));
finishDrawing();
}
}
void ThreatAreaDialog::finishDrawing() {
mapPage_->disableDrawingMode();
disconnect(mapPage_, &MapPage::mapClicked, this, &ThreatAreaDialog::handleDrawingClick);
drawingPoints_.clear();
show();
addArea(); // 自动添加绘制结果到表格和地图
QMessageBox::information(this, "完成", "绘制完成,已自动添加区域。");
}
// Implement missing methods
void ThreatAreaDialog::addArea() {
// 验证输入并添加 overlay
QVariantMap params;
params["type"] = typeCombo_->currentText();
params["level"] = levelCombo_->currentText();
params["startTime"] = startTimeEdit_->dateTime().toString();
params["endTime"] = endTimeEdit_->dateTime().toString();
// 颜色映射
QString colorStr = colorCombo_->currentText();
QString hexColor;
if (colorStr == "红色") hexColor = "#FF0000";
else if (colorStr == "黄色") hexColor = "#FFFF00";
else if (colorStr == "蓝色") hexColor = "#0000FF";
else hexColor = "#FF0000"; // 默认红色
params["color"] = hexColor;
params["shape"] = currentShape_;
bool valid = true;
if (currentShape_ == "圆形") {
bool ok;
params["centerLng"] = centerLngInput_->text().toDouble(&ok);
valid &= ok;
params["centerLat"] = centerLatInput_->text().toDouble(&ok);
valid &= ok;
double radius = radiusInput_->text().toDouble(&ok);
valid &= ok && radius > 0;
params["radius"] = radius;
if (!valid) {
QMessageBox::warning(this, "错误", "无效的圆形参数(经纬度/半径必须是正数)");
return;
}
mapPage_->addThreatOverlay("circle", params);
} else {
QList<QVariant> points;
for (int i = 0; i < 4; ++i) {
QVariantMap point;
point["lng"] = rectPointsTable_->item(i, 0)->text().toDouble();
point["lat"] = rectPointsTable_->item(i, 1)->text().toDouble();
points.append(point);
}
params["points"] = points;
mapPage_->addThreatOverlay("rectangle", params);
}
qDebug() << "添加威胁区域";
QMessageBox::information(this, "提示", "威胁区域已添加");
// 插入到表格
int row = areaTable_->rowCount();
areaTable_->insertRow(row);
areaTable_->setItem(row, 0, new QTableWidgetItem(params["type"].toString()));
areaTable_->setItem(row, 1, new QTableWidgetItem(params["level"].toString()));
areaTable_->setItem(row, 2, new QTableWidgetItem(params["startTime"].toString()));
areaTable_->setItem(row, 3, new QTableWidgetItem(params["endTime"].toString()));
areaTable_->setItem(row, 4, new QTableWidgetItem(params["shape"].toString()));
QString details;
if (params["shape"].toString() == "圆形") {
details = QString("中心: %1,%2 半径: %3m").arg(params["centerLng"].toDouble()).arg(params["centerLat"].toDouble()).arg(params["radius"].toDouble());
} else {
details = "矩形点: ..."; // 可以扩展
}
areaTable_->setItem(row, 5, new QTableWidgetItem(details));
accept();
}
// Implement missing methods
void ThreatAreaDialog::updateThreatStats() {
qDebug() << "更新威胁统计";
}
void ThreatAreaDialog::editArea() {
if (selectedRow_ < 0 || selectedRow_ >= mapPage_->getThreatAreas().size()) {
QMessageBox::warning(this, "错误", "请选择一个区域编辑");
return;
}
auto& area = mapPage_->getThreatAreas()[selectedRow_];
typeCombo_->setCurrentText(area["type"].toString());
levelCombo_->setCurrentText(area["level"].toString());
startTimeEdit_->setDateTime(QDateTime::fromString(area["startTime"].toString()));
endTimeEdit_->setDateTime(QDateTime::fromString(area["endTime"].toString()));
colorCombo_->setCurrentText(area["color"].toString());
shapeCombo_->setCurrentText(area["shape"].toString());
if (area["shape"].toString() == "circle") {
centerLngInput_->setText(QString::number(area["centerLng"].toDouble()));
centerLatInput_->setText(QString::number(area["centerLat"].toDouble()));
radiusInput_->setText(QString::number(area["radius"].toDouble()));
} else {
// 填充矩形表格
QList<QVariant> points = area["points"].toList();
for (int i = 0; i < 4 && i < points.size(); ++i) {
QVariantMap p = points[i].toMap();
rectPointsTable_->setItem(i, 0, new QTableWidgetItem(QString::number(p["lng"].toDouble())));
rectPointsTable_->setItem(i, 1, new QTableWidgetItem(QString::number(p["lat"].toDouble())));
}
}
// 更新后,用户可以修改并点击"添加区域"来覆盖(或实现更新逻辑)
QMessageBox::information(this, "提示", "已加载选中区域数据,请修改后点击添加更新。");
}
void ThreatAreaDialog::deleteArea() {
if (selectedRow_ < 0 || selectedRow_ >= mapPage_->getThreatAreas().size()) {
QMessageBox::warning(this, "错误", "请选择一个区域删除");
return;
}
mapPage_->removeThreatOverlay(selectedRow_);
areaTable_->removeRow(selectedRow_);
selectedRow_ = -1;
QMessageBox::information(this, "成功", "已删除选中区域");
}
// In MapPage class
void MapPage::removeThreatOverlay(int index) {
if (index < 0 || index >= threatAreas_.size()) return;
QVariantMap area = threatAreas_.at(index);
QString overlayId = area["id"].toString();
runMapJavaScript(QString("if (window.%1) { window.map.remove(window.%1); window.%1 = null; }").arg(overlayId));
threatAreas_.removeAt(index);
// 无需 clearMap 和重绘
}
PathPlanningDialog::PathPlanningDialog(QWidget *parent, MapPage* mapPage) : QDialog(parent), mapPage_(mapPage), pathData_("") {
setWindowTitle("路径规划");
@ -1464,63 +1720,6 @@ LocateDialog::LocateDialog(QWidget *parent) : QDialog(parent), lng_(0.0), lat_(0
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
}
// Implement AreaSearchDialog methods
AreaSearchDialog::AreaSearchDialog(QWidget *parent) : QDialog(parent) {
setWindowTitle("区域搜索");
setMinimumSize(400, 300);
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(20, 20, 20, 20);
auto* formLayout = new QFormLayout();
minLngInput_ = new QLineEdit();
minLngInput_->setPlaceholderText("最小经度");
formLayout->addRow("最小经度:", minLngInput_);
minLatInput_ = new QLineEdit();
minLatInput_->setPlaceholderText("最小纬度");
formLayout->addRow("最小纬度:", minLatInput_);
maxLngInput_ = new QLineEdit();
maxLngInput_->setPlaceholderText("最大经度");
formLayout->addRow("最大经度:", maxLngInput_);
maxLatInput_ = new QLineEdit();
maxLatInput_->setPlaceholderText("最大纬度");
formLayout->addRow("最大纬度:", maxLatInput_);
layout->addLayout(formLayout);
auto* buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
auto* confirmBtn = new QPushButton("确认");
buttonLayout->addWidget(confirmBtn);
auto* cancelBtn = new QPushButton("取消");
buttonLayout->addWidget(cancelBtn);
layout->addLayout(buttonLayout);
connect(confirmBtn, &QPushButton::clicked, this, &AreaSearchDialog::onConfirm);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
}
void AreaSearchDialog::onConfirm() {
bool ok1, ok2, ok3, ok4;
double minLng = minLngInput_->text().toDouble(&ok1);
double minLat = minLatInput_->text().toDouble(&ok2);
double maxLng = maxLngInput_->text().toDouble(&ok3);
double maxLat = maxLatInput_->text().toDouble(&ok4);
if (ok1 && ok2 && ok3 && ok4 && minLng < maxLng && minLat < maxLat) {
accept();
qDebug() << "区域搜索边界:" << minLng << minLat << maxLng << maxLat;
// TODO: Implement search logic, e.g., adjust map view
} else {
QMessageBox::warning(this, "错误", "请输入有效的边界坐标 (min < max)");
}
}
void LocateDialog::onConfirm() {
bool ok1, ok2;
@ -1542,11 +1741,15 @@ double LocateDialog::getLatitude() const {
return lat_;
}
// 更新 handleMapClick 以支持绘制模式
void MapPage::handleMapClick(double lng, double lat) {
qDebug() << "Map clicked in C++: lng=" << lng << ", lat=" << lat;
emit mapClicked(lng, lat);
emit mapClicked(lng, lat); // 始终发出信号dialog 会根据模式处理
// 如果在绘制模式,可以在这里添加额外逻辑,但由于连接在 dialog 中,保持简单
}
// 移除不必要的 handleDrawingClick
// void MapPage::handleDrawingClick(double lng, double lat) { }
void MapPage::onConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) {
qDebug() << "Console message:" << level << message << "at line" << lineNumber << "source:" << sourceID;
@ -1559,3 +1762,63 @@ QWidget* MapPage::createMapControlsWidget() {
layout->setContentsMargins(0, 0, 0, 0);
return widget;
}
// 在 MapPage 中实现
void MapPage::enableDrawingMode(const QString& shape) {
isDrawing_ = true;
drawingShape_ = shape;
drawingPoints_.clear();
addClickListener();
}
void MapPage::disableDrawingMode() {
isDrawing_ = false;
removeClickListener();
}
void MapPage::addThreatOverlay(const QString& shape, const QVariantMap& params) {
QString overlayId = "threat_" + QString::number(threatAreas_.size()); // 唯一 ID
QVariantMap paramsWithId = params;
paramsWithId["id"] = overlayId;
QString js;
if (shape == "circle") {
js = QString(R"(
var circle = new AMap.Circle({
center: new AMap.LngLat(%1, %2),
radius: %3,
fillColor: '%4',
fillOpacity: 0.3,
strokeColor: '%4',
strokeWeight: 2
});
circle.setMap(window.map);
window.%5 = circle; // 存储以便移除
window.map.setFitView([circle]); // 缩放到 overlay
)").arg(params["centerLng"].toDouble()).arg(params["centerLat"].toDouble())
.arg(params["radius"].toDouble()).arg(params["color"].toString()).arg(overlayId);
} else if (shape == "rectangle") {
QString path = "[";
QList<QVariant> points = params["points"].toList();
for (int i = 0; i < points.size(); ++i) {
QVariantMap p = points[i].toMap();
if (i > 0) path += ",";
path += QString("[%1,%2]").arg(p["lng"].toDouble()).arg(p["lat"].toDouble());
}
path += "]";
js = QString(R"(
var polygon = new AMap.Polygon({
path: %1,
fillColor: '%2',
fillOpacity: 0.3,
strokeColor: '%2',
strokeWeight: 2
});
polygon.setMap(window.map);
window.%5 = polygon; // 存储以便移除
window.map.setFitView([polygon]); // 缩放到 overlay
)").arg(path).arg(params["color"].toString()).arg(overlayId);
}
runMapJavaScript(js + " console.log('Overlay added: " + overlayId + "'); ");
threatAreas_.append(paramsWithId); // 存储带 ID
}

@ -47,21 +47,48 @@ protected:
// class AreaSearchDialog;
// class PathPlanningDialog;
// 前向声明
class MapPage;
// 定义类
class ThreatAreaDialog : public QDialog {
Q_OBJECT
public:
ThreatAreaDialog(QWidget* parent = nullptr);
ThreatAreaDialog(QWidget* parent = nullptr, MapPage* mapPage = nullptr); // 添加 MapPage 参数
private slots:
void addArea();
void updateThreatStats();
void startDrawingThreatArea();
void onShapeChanged(int index); // 新增槽函数,处理形状变化
void handleDrawingClick(double lng, double lat); // 新增
void finishDrawing(); // 新增
void editArea(); // 新增:编辑选中区域
void deleteArea(); // 新增:删除选中区域
private:
QComboBox* typeCombo_;
QDateTimeEdit* timeEdit_;
QComboBox* shapeCombo_;
QDateTimeEdit* startTimeEdit_;
QDateTimeEdit* endTimeEdit_;
QComboBox* shapeCombo_; // 已存在,但确保用于圆形/矩形
QComboBox* colorCombo_;
QTableWidget* areaTable_;
QLineEdit* coordInput_;
QPushButton* drawOnMapBtn_;
// 新增输入字段
QWidget* circleInputWidget_;
QWidget* rectangleInputWidget_;
QLineEdit* centerLngInput_;
QLineEdit* centerLatInput_;
QLineEdit* radiusInput_;
QTableWidget* rectPointsTable_;
MapPage* mapPage_; // 添加 MapPage 引用
QString currentShape_; // 当前形状
QList<QPair<double, double>> drawingPoints_; // 新增临时绘制点
QComboBox* levelCombo_; // 新增,存储威胁等级
QPushButton* editBtn_; // 新增编辑按钮
QPushButton* deleteBtn_; // 新增删除按钮
int selectedRow_ = -1; // 跟踪选中行
};
@ -141,19 +168,6 @@ private:
QList<QPair<double, double>> vertices_;
};
// 添加AreaSearchDialog类定义
class AreaSearchDialog : public QDialog {
Q_OBJECT
public:
AreaSearchDialog(QWidget* parent = nullptr);
private slots:
void onConfirm();
private:
QLineEdit* minLngInput_;
QLineEdit* minLatInput_;
QLineEdit* maxLngInput_;
QLineEdit* maxLatInput_;
};
// 添加LocateDialog类定义
class LocateDialog : public QDialog {
@ -203,7 +217,6 @@ private slots:
void onSetThreatClicked();
void onPathPlanningClicked();
void onAreaCoverageClicked();
void onAreaSearchClicked();
// 添加新槽
void onSearchMapClicked(); // 新增声明
@ -222,6 +235,10 @@ public slots:
void showMarker(double lng, double lat, const QString& label, const QString& color, int index);
void visualizeCoveragePath(const QString& pathData);
void clearCoverageOverlays();
void enableDrawingMode(const QString& shape);
void disableDrawingMode();
void addThreatOverlay(const QString& shape, const QVariantMap& params); // 添加威胁 overlay
void removeThreatOverlay(int index); // 新增:移除特定 overlay
private:
void setupUI();
@ -244,7 +261,6 @@ private:
// 添加新按钮成员
QPushButton* setThreatBtn_;
QPushButton* areaSearchBtn_;
QPushButton* pathPlanningBtn_;
QPushButton* areaCoverageBtn_;
@ -255,7 +271,6 @@ private:
// 添加对话框成员
ThreatAreaDialog* threatDialog_;
AreaSearchDialog* searchDialog_;
PathPlanningDialog* planningDialog_;
AreaCoverageDialog* coverageDialog_;
LocateDialog* locateDialog_;
@ -277,6 +292,15 @@ private:
QString coverageOverlayId_ = "coverageOverlay";
QString areaOverlayId_ = "areaOverlay";
QString currentCoveragePathData_;
// 绘制模式
bool isDrawing_ = false;
QString drawingShape_; // "circle" or "rectangle"
QList<QPair<double, double>> drawingPoints_; // 临时绘制点
QList<QVariantMap> threatAreas_; // 新增:存储已添加的威胁区域
public:
const QList<QVariantMap>& getThreatAreas() const { return threatAreas_; } // 新增 getter
};
// 移除所有类定义从h文件移出

Loading…
Cancel
Save