|
|
|
|
@ -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
|
|
|
|
|
}
|
|
|
|
|
|