map_view.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. import os
  2. import requests
  3. from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
  4. QLabel, QComboBox, QGroupBox, QToolBar, QAction, QGridLayout)
  5. from PyQt5.QtCore import Qt, QUrl, pyqtSlot, QSize
  6. from PyQt5.QtGui import QIcon
  7. from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile, QWebEngineSettings
  8. from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
  9. class RequestInterceptor(QWebEngineUrlRequestInterceptor):
  10. """网络请求拦截器,用于调试地图加载问题"""
  11. def interceptRequest(self, info):
  12. print(f"请求URL: {info.requestUrl().toString()}")
  13. print(f"请求方法: {info.requestMethod()}")
  14. print(f"请求头: {info.requestHeaders()}")
  15. class CustomWebEnginePage(QWebEnginePage):
  16. def javaScriptConsoleMessage(self, level, message, line, source):
  17. level_str = {
  18. 0: "INFO",
  19. 1: "WARNING",
  20. 2: "ERROR"
  21. }.get(level, "UNKNOWN")
  22. print(f"JS控制台 [{level_str}] {message} (第{line}行, 来源: {source})")
  23. class MapView(QWidget):
  24. """地图视图组件,用于在地理信息系统上显示监测区域和告警位置"""
  25. def __init__(self, config):
  26. super().__init__()
  27. self.config = config
  28. self.init_ui()
  29. def init_ui(self):
  30. """初始化UI"""
  31. # 创建主布局
  32. layout = QVBoxLayout(self)
  33. layout.setContentsMargins(0, 0, 0, 0)
  34. # 创建地图容器
  35. map_container = QWidget()
  36. map_layout = QVBoxLayout(map_container)
  37. map_layout.setContentsMargins(0, 0, 0, 0)
  38. # 创建工具栏
  39. toolbar = QToolBar()
  40. toolbar.setIconSize(QSize(16, 16))
  41. # 添加地图类型选择下拉框
  42. self.map_type_combo = QComboBox()
  43. self.map_type_combo.addItems(["卫星图", "地形图", "道路图", "混合图"])
  44. self.map_type_combo.setCurrentIndex(0) # 设置默认选中卫星图
  45. self.map_type_combo.currentIndexChanged.connect(self.change_map_type)
  46. toolbar.addWidget(QLabel("地图类型: "))
  47. toolbar.addWidget(self.map_type_combo)
  48. toolbar.addSeparator()
  49. # 添加区域选择下拉框
  50. self.region_combo = QComboBox()
  51. for region in self.config.get('monitor_regions', []):
  52. self.region_combo.addItem(region['name'], region)
  53. self.region_combo.currentIndexChanged.connect(self.change_region)
  54. toolbar.addWidget(QLabel("监测区域: "))
  55. toolbar.addWidget(self.region_combo)
  56. toolbar.addSeparator()
  57. # 添加缩放按钮
  58. self.zoom_in_btn = QPushButton("放大")
  59. self.zoom_in_btn.setIcon(QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'assets', 'zoom_in.png')))
  60. self.zoom_in_btn.clicked.connect(self.zoom_in)
  61. toolbar.addWidget(self.zoom_in_btn)
  62. self.zoom_out_btn = QPushButton("缩小")
  63. self.zoom_out_btn.setIcon(QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'assets', 'zoom_out.png')))
  64. self.zoom_out_btn.clicked.connect(self.zoom_out)
  65. toolbar.addWidget(self.zoom_out_btn)
  66. toolbar.addSeparator()
  67. # 显示/隐藏告警点
  68. self.show_alerts_btn = QPushButton("显示告警")
  69. self.show_alerts_btn.setCheckable(True)
  70. self.show_alerts_btn.setChecked(True)
  71. self.show_alerts_btn.clicked.connect(self.toggle_alerts)
  72. toolbar.addWidget(self.show_alerts_btn)
  73. toolbar.addSeparator()
  74. # 添加定位按钮
  75. self.location_btn = QPushButton("定位")
  76. self.location_btn.setIcon(QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'assets', 'location.png')))
  77. self.location_btn.clicked.connect(self.locate_current_position)
  78. toolbar.addWidget(self.location_btn)
  79. # 添加工具栏到地图布局
  80. map_layout.addWidget(toolbar)
  81. # 创建Web视图用于显示地图
  82. self.web_view = QWebEngineView()
  83. # 配置Edge浏览器引擎
  84. profile = QWebEngineProfile.defaultProfile()
  85. profile.setHttpUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59")
  86. # 添加请求拦截器
  87. interceptor = RequestInterceptor()
  88. profile.setUrlRequestInterceptor(interceptor)
  89. # 启用必要的Web功能
  90. settings = profile.settings()
  91. settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
  92. settings.setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
  93. settings.setAttribute(QWebEngineSettings.WebGLEnabled, True)
  94. settings.setAttribute(QWebEngineSettings.PluginsEnabled, True)
  95. settings.setAttribute(QWebEngineSettings.FullScreenSupportEnabled, True)
  96. # 启用跨域访问
  97. profile.setHttpCacheType(QWebEngineProfile.DiskHttpCache)
  98. profile.setPersistentCookiesPolicy(QWebEngineProfile.AllowPersistentCookies)
  99. # 设置页面
  100. page = CustomWebEnginePage(self.web_view)
  101. self.web_view.setPage(page)
  102. map_layout.addWidget(self.web_view)
  103. # 将地图容器添加到主布局
  104. layout.addWidget(map_container)
  105. # 加载初始地图
  106. self.load_map()
  107. def load_map(self):
  108. """加载百度地图"""
  109. try:
  110. # 获取地图配置
  111. center = self.config.get('map_center', [39.915, 116.404])
  112. zoom = self.config.get('map_zoom', 15)
  113. # 使用临时文件加载地图
  114. import tempfile
  115. temp_path = os.path.join(tempfile.gettempdir(), "map_temp.html")
  116. # 构建HTML
  117. html = f"""<!DOCTYPE html>
  118. <html>
  119. <head>
  120. <meta charset="utf-8" />
  121. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  122. <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
  123. <title>森林监测地图</title>
  124. <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak={self.config.get('baidu_map_key', '')}&type=webgl"></script>
  125. <style type="text/css">
  126. html, body, #map {{
  127. height: 100%;
  128. width: 100%;
  129. margin: 0;
  130. padding: 0;
  131. }}
  132. .loading {{
  133. position: absolute;
  134. top: 50%;
  135. left: 50%;
  136. transform: translate(-50%, -50%);
  137. text-align: center;
  138. font-family: Arial, sans-serif;
  139. }}
  140. .offline-map {{
  141. width: 100%;
  142. height: 100%;
  143. background-color: #eee;
  144. display: flex;
  145. flex-direction: column;
  146. align-items: center;
  147. justify-content: center;
  148. font-family: Arial, sans-serif;
  149. }}
  150. .info-window {{
  151. padding: 5px;
  152. min-width: 150px;
  153. }}
  154. .info-window .title {{
  155. font-weight: bold;
  156. margin-bottom: 5px;
  157. color: #3a8ee6;
  158. }}
  159. </style>
  160. </head>
  161. <body>
  162. <div id="map">
  163. <div class="loading">加载百度地图中...</div>
  164. </div>
  165. <script type="text/javascript">
  166. // 全局变量
  167. var map;
  168. var alertMarkers = [];
  169. var isMapLoaded = false;
  170. // 初始化函数
  171. function initMap() {{
  172. try {{
  173. console.log("开始初始化地图...");
  174. // 检查BMap对象是否存在
  175. if (typeof BMapGL === "undefined") {{
  176. throw new Error("BMapGL未定义,百度地图API未正确加载");
  177. }}
  178. console.log("创建地图实例...");
  179. // 创建地图实例
  180. map = new BMapGL.Map("map");
  181. console.log("设置地图中心点...");
  182. // 创建点坐标(注意经纬度顺序:经度在前,纬度在后)
  183. var point = new BMapGL.Point({center[1]}, {center[0]});
  184. console.log("初始化地图视图...");
  185. // 初始化地图
  186. map.centerAndZoom(point, {zoom});
  187. // 设置默认地图类型为卫星图
  188. map.setMapType(BMAP_SATELLITE_MAP);
  189. // 设置默认显示选项,隐藏POI图标
  190. map.setDisplayOptions({{
  191. poi: false, // 隐藏POI图标
  192. poiText: false, // 隐藏POI文字
  193. building: false // 隐藏3D建筑物
  194. }});
  195. // 开启鼠标滚轮缩放
  196. map.enableScrollWheelZoom(true);
  197. console.log("添加地图控件...");
  198. // 添加地图控件
  199. map.addControl(new BMapGL.NavigationControl()); // 导航控件
  200. map.addControl(new BMapGL.ScaleControl()); // 比例尺控件
  201. map.addControl(new BMapGL.ZoomControl()); // 缩放控件
  202. map.addControl(new BMapGL.LocationControl()); // 定位控件
  203. // 地图初始化成功标记
  204. isMapLoaded = true;
  205. console.log("地图初始化完成");
  206. // 自动定位到当前位置
  207. setTimeout(function() {{
  208. // 定义错误状态处理函数
  209. function handleLocationError(status) {{
  210. var errorMsg = "";
  211. switch(status) {{
  212. case 6:
  213. errorMsg = "定位权限被拒绝,请在浏览器设置中允许获取位置信息";
  214. break;
  215. case 2:
  216. case 8:
  217. errorMsg = "定位不可用或超时,尝试使用IP定位";
  218. break;
  219. default:
  220. errorMsg = "定位失败(错误码:" + status + "),尝试使用IP定位";
  221. }}
  222. console.error(errorMsg);
  223. return errorMsg;
  224. }}
  225. // 显示定位结果的函数
  226. function showLocationResult(point, accuracy, address, locationType, errorMsg) {{
  227. var marker = new BMapGL.Marker(point);
  228. map.addOverlay(marker);
  229. map.centerAndZoom(point, locationType === 'ip' ? 12 : 18);
  230. // 如果是精确定位,显示精度圈
  231. if (accuracy && locationType !== 'ip') {{
  232. var circle = new BMapGL.Circle(point, accuracy, {{
  233. strokeColor: "#1E90FF",
  234. strokeWeight: 1,
  235. strokeOpacity: 0.5,
  236. fillColor: "#1E90FF",
  237. fillOpacity: 0.1
  238. }});
  239. map.addOverlay(circle);
  240. }}
  241. var locationTypeText = {{
  242. 'sdk': '手机GPS',
  243. 'h5': '浏览器定位',
  244. 'ip': 'IP定位'
  245. }}[locationType] || '未知方式';
  246. var infoWindow = new BMapGL.InfoWindow(
  247. '<div class="info-window">' +
  248. '<div class="title">当前位置</div>' +
  249. '<div>定位方式: ' + locationTypeText + '</div>' +
  250. '<div>经度: ' + point.lng.toFixed(6) + '</div>' +
  251. '<div>纬度: ' + point.lat.toFixed(6) + '</div>' +
  252. '<div>地址: ' + address + '</div>' +
  253. (accuracy && locationType !== 'ip' ? '<div>定位精度: ' + accuracy.toFixed(1) + '米</div>' : '') +
  254. (errorMsg ? '<div style="color: #ff9900;">' + errorMsg + '</div>' : '') +
  255. '</div>'
  256. );
  257. marker.openInfoWindow(infoWindow);
  258. }}
  259. // 先尝试SDK定位
  260. var geolocation = new BMapGL.Geolocation();
  261. geolocation.enableSDKLocation();
  262. geolocation.getCurrentPosition(function(r) {{
  263. if(this.getStatus() == BMAP_STATUS_SUCCESS) {{
  264. // SDK定位成功
  265. var geoc = new BMapGL.Geocoder();
  266. geoc.getLocation(r.point, function(rs) {{
  267. var addComp = rs.addressComponents;
  268. var address = addComp.province + addComp.city +
  269. addComp.district + addComp.street +
  270. addComp.streetNumber;
  271. showLocationResult(r.point, r.accuracy, address, 'sdk');
  272. }});
  273. }} else {{
  274. // SDK定位失败,尝试浏览器H5定位
  275. var h5geolocation = new BMapGL.Geolocation();
  276. h5geolocation.getCurrentPosition(function(r) {{
  277. if(this.getStatus() == BMAP_STATUS_SUCCESS) {{
  278. // H5定位成功
  279. var geoc = new BMapGL.Geocoder();
  280. geoc.getLocation(r.point, function(rs) {{
  281. var addComp = rs.addressComponents;
  282. var address = addComp.province + addComp.city +
  283. addComp.district + addComp.street +
  284. addComp.streetNumber;
  285. showLocationResult(r.point, r.accuracy, address, 'h5');
  286. }});
  287. }} else {{
  288. // H5定位也失败,使用IP定位
  289. var errorMsg = handleLocationError(this.getStatus());
  290. var myCity = new BMapGL.LocalCity();
  291. myCity.get(function(result) {{
  292. var geoc = new BMapGL.Geocoder();
  293. geoc.getLocation(result.center, function(rs) {{
  294. var addComp = rs.addressComponents;
  295. var address = addComp.province + addComp.city +
  296. addComp.district + addComp.street +
  297. addComp.streetNumber;
  298. showLocationResult(result.center, null, address, 'ip',
  299. '注意:由于无法获取精确位置,已切换到IP定位(精度较低)');
  300. }});
  301. }});
  302. }}
  303. }}, {{
  304. enableHighAccuracy: true,
  305. timeout: 5000,
  306. maximumAge: 0
  307. }});
  308. }}
  309. }}, {{
  310. enableHighAccuracy: true,
  311. timeout: 5000,
  312. maximumAge: 0,
  313. SDKLocation: true,
  314. coordType: 'bd09ll',
  315. poiDistance: true,
  316. poiNumber: 1
  317. }});
  318. }}, 1000);
  319. // 定义对外接口
  320. window.mapFunctions = {{
  321. setMapType: function(type) {{
  322. if (!isMapLoaded) return false;
  323. try {{
  324. switch(type) {{
  325. case 0: // 卫星图
  326. map.setMapType(BMAP_SATELLITE_MAP);
  327. map.setDisplayOptions({{
  328. poi: false,
  329. poiText: false,
  330. building: false
  331. }});
  332. map.setTilt(0);
  333. break;
  334. case 1: // 地形图
  335. map.setMapType(BMAP_NORMAL_MAP);
  336. map.setDisplayOptions({{
  337. poi: false,
  338. poiText: false,
  339. building: false
  340. }});
  341. break;
  342. case 2: // 道路图
  343. map.setMapType(BMAP_NORMAL_MAP);
  344. map.setDisplayOptions({{
  345. poi: false,
  346. poiText: false,
  347. building: false
  348. }});
  349. break;
  350. case 3: // 混合图
  351. map.setMapType(BMAP_SATELLITE_MAP);
  352. map.setDisplayOptions({{
  353. poi: false,
  354. poiText: false,
  355. building: true
  356. }});
  357. break;
  358. }}
  359. return true;
  360. }} catch(e) {{
  361. console.error("切换地图类型出错:", e);
  362. return false;
  363. }}
  364. }},
  365. zoomIn: function() {{
  366. if (!isMapLoaded) return false;
  367. try {{
  368. map.zoomIn();
  369. return true;
  370. }} catch(e) {{
  371. console.error("地图放大出错:", e);
  372. return false;
  373. }}
  374. }},
  375. zoomOut: function() {{
  376. if (!isMapLoaded) return false;
  377. try {{
  378. map.zoomOut();
  379. return true;
  380. }} catch(e) {{
  381. console.error("地图缩小出错:", e);
  382. return false;
  383. }}
  384. }},
  385. panTo: function(lat, lng) {{
  386. if (!isMapLoaded) return false;
  387. try {{
  388. var point = new BMapGL.Point(lng, lat);
  389. map.panTo(point);
  390. return true;
  391. }} catch(e) {{
  392. console.error("地图平移出错:", e);
  393. return false;
  394. }}
  395. }},
  396. toggleAlerts: function(show) {{
  397. if (!isMapLoaded) return false;
  398. try {{
  399. if (alertMarkers.length > 0) {{
  400. alertMarkers.forEach(function(marker) {{
  401. if (show) marker.show();
  402. else marker.hide();
  403. }});
  404. }}
  405. return true;
  406. }} catch(e) {{
  407. console.error("切换告警显示出错:", e);
  408. return false;
  409. }}
  410. }},
  411. locateCurrentPosition: function() {{
  412. if (!isMapLoaded) {{
  413. console.error('地图未加载完成');
  414. return false;
  415. }}
  416. // 创建定位控件
  417. var locationControl = new BMapGL.LocationControl();
  418. locationControl.addEventListener("locationSuccess", function(e){{
  419. var address = '';
  420. address += e.addressComponent.province;
  421. address += e.addressComponent.city;
  422. address += e.addressComponent.district;
  423. address += e.addressComponent.street;
  424. address += e.addressComponent.streetNumber;
  425. // 在marker上显示信息窗口
  426. var infoWindow = new BMapGL.InfoWindow(
  427. '<div class="info-window">' +
  428. '<div class="title">当前位置</div>' +
  429. '<div>经度: ' + e.point.lng + '</div>' +
  430. '<div>纬度: ' + e.point.lat + '</div>' +
  431. '<div>地址: ' + address + '</div>' +
  432. '</div>'
  433. );
  434. var marker = new BMapGL.Marker(e.point);
  435. map.addOverlay(marker);
  436. marker.openInfoWindow(infoWindow);
  437. console.log('定位成功');
  438. }});
  439. locationControl.addEventListener("locationError", function(e){{
  440. console.error('定位失败:' + e.message);
  441. }});
  442. locationControl.location();
  443. return true;
  444. }}
  445. }};
  446. }} catch (e) {{
  447. console.error("地图初始化失败:", e);
  448. document.getElementById("map").innerHTML =
  449. '<div class="offline-map"><h2>百度地图初始化失败</h2><p>错误信息: ' + e.message + '</p></div>';
  450. }}
  451. }}
  452. // 添加示例告警点
  453. function addExampleAlerts() {{
  454. // 清空告警点数组
  455. alertMarkers = [];
  456. // 暂时不添加示例告警点
  457. }}
  458. // 页面加载完成后初始化地图
  459. window.onload = initMap;
  460. </script>
  461. </body>
  462. </html>"""
  463. # 将HTML保存到临时文件
  464. with open(temp_path, "w", encoding="utf-8") as f:
  465. f.write(html)
  466. # 从本地文件加载
  467. self.web_view.load(QUrl.fromLocalFile(temp_path))
  468. print("正在从本地文件加载百度地图...")
  469. except Exception as e:
  470. print(f"地图加载失败: {e}")
  471. # 显示错误信息
  472. error_html = f"""
  473. <html>
  474. <body style="background-color: #f0f0f0; color: #333; font-family: Arial, sans-serif; text-align: center; padding: 50px;">
  475. <h2>地图加载失败</h2>
  476. <p>错误信息: {str(e)}</p>
  477. <p>请检查以下内容:</p>
  478. <ol style="text-align: left; max-width: 500px; margin: 0 auto;">
  479. <li>确认网络连接正常</li>
  480. <li>确认百度地图API密钥有效</li>
  481. <li>检查PyQt WebEngine设置</li>
  482. <li>查看控制台输出的详细错误信息</li>
  483. </ol>
  484. </body>
  485. </html>
  486. """
  487. self.web_view.setHtml(error_html)
  488. @pyqtSlot(int)
  489. def change_map_type(self, index):
  490. """改变地图类型"""
  491. # 通过JS调用地图函数
  492. script = f"""
  493. try {{
  494. console.log('调用地图类型切换,类型索引:', {index});
  495. if (window.mapFunctions && typeof window.mapFunctions.setMapType === 'function') {{
  496. window.mapFunctions.setMapType({index});
  497. return true;
  498. }} else {{
  499. console.error('地图类型切换函数不可用');
  500. return false;
  501. }}
  502. }} catch(e) {{
  503. console.error('执行地图类型切换时出错:', e);
  504. return false;
  505. }}
  506. """
  507. self.web_view.page().runJavaScript(script)
  508. @pyqtSlot(int)
  509. def change_region(self, index):
  510. """改变监测区域"""
  511. if index >= 0 and index < len(self.config.get('monitor_regions', [])):
  512. region = self.config['monitor_regions'][index]
  513. # 通过JS调用地图函数
  514. script = """
  515. try {
  516. if (window.mapFunctions && typeof window.mapFunctions.panTo === 'function') {
  517. window.mapFunctions.panTo(0, 0);
  518. return true;
  519. } else {
  520. console.error('地图平移函数不可用');
  521. return false;
  522. }
  523. } catch(e) {
  524. console.error('平移到区域时出错:', e);
  525. return false;
  526. }
  527. """
  528. self.web_view.page().runJavaScript(script)
  529. @pyqtSlot()
  530. def zoom_in(self):
  531. """地图放大"""
  532. script = """
  533. try {
  534. if (window.mapFunctions && typeof window.mapFunctions.zoomIn === 'function') {
  535. window.mapFunctions.zoomIn();
  536. return true;
  537. } else {
  538. console.error('地图放大函数不可用');
  539. return false;
  540. }
  541. } catch(e) {
  542. console.error('地图放大时出错:', e);
  543. return false;
  544. }
  545. """
  546. self.web_view.page().runJavaScript(script)
  547. @pyqtSlot()
  548. def zoom_out(self):
  549. """地图缩小"""
  550. script = """
  551. try {
  552. if (window.mapFunctions && typeof window.mapFunctions.zoomOut === 'function') {
  553. window.mapFunctions.zoomOut();
  554. return true;
  555. } else {
  556. console.error('地图缩小函数不可用');
  557. return false;
  558. }
  559. } catch(e) {
  560. console.error('地图缩小时出错:', e);
  561. return false;
  562. }
  563. """
  564. self.web_view.page().runJavaScript(script)
  565. @pyqtSlot(bool)
  566. def toggle_alerts(self, checked):
  567. """切换告警点显示状态"""
  568. script = f"""
  569. try {{
  570. if (window.mapFunctions && typeof window.mapFunctions.toggleAlerts === 'function') {{
  571. window.mapFunctions.toggleAlerts({str(checked).lower()});
  572. return true;
  573. }} else {{
  574. console.error('告警点切换函数不可用');
  575. return false;
  576. }}
  577. }} catch(e) {{
  578. console.error('切换告警点时出错:', e);
  579. return false;
  580. }}
  581. """
  582. self.web_view.page().runJavaScript(script)
  583. def locate_current_position(self):
  584. """定位当前位置"""
  585. script = """
  586. try {
  587. if (!isMapLoaded) {
  588. console.error('地图未加载完成');
  589. return false;
  590. }
  591. // 创建定位控件
  592. var locationControl = new BMapGL.LocationControl();
  593. locationControl.addEventListener("locationSuccess", function(e){{
  594. var address = '';
  595. address += e.addressComponent.province;
  596. address += e.addressComponent.city;
  597. address += e.addressComponent.district;
  598. address += e.addressComponent.street;
  599. address += e.addressComponent.streetNumber;
  600. // 在marker上显示信息窗口
  601. var infoWindow = new BMapGL.InfoWindow(
  602. '<div class="info-window">' +
  603. '<div class="title">当前位置</div>' +
  604. '<div>经度: ' + e.point.lng + '</div>' +
  605. '<div>纬度: ' + e.point.lat + '</div>' +
  606. '<div>地址: ' + address + '</div>' +
  607. '</div>'
  608. );
  609. var marker = new BMapGL.Marker(e.point);
  610. map.addOverlay(marker);
  611. marker.openInfoWindow(infoWindow);
  612. console.log('定位成功');
  613. }});
  614. locationControl.addEventListener("locationError", function(e){{
  615. console.error('定位失败:' + e.message);
  616. }});
  617. locationControl.location();
  618. return true;
  619. } catch(e) {
  620. console.error('执行定位时出错:', e);
  621. return false;
  622. }
  623. """
  624. self.web_view.page().runJavaScript(script)
  625. def update_view(self):
  626. """更新地图视图"""
  627. # 实际项目中可以在这里添加刷新告警点等逻辑
  628. pass
  629. def get_accurate_ip_location(self):
  630. """获取更精确的IP定位信息"""
  631. try:
  632. # 使用腾讯位置服务
  633. response = requests.get(
  634. 'https://apis.map.qq.com/ws/location/v1/ip',
  635. params={
  636. 'key': self.config.get('qq_map_key', ''), # 需要在配置中添加腾讯地图密钥
  637. 'output': 'json'
  638. },
  639. timeout=5
  640. )
  641. if response.status_code == 200:
  642. data = response.json()
  643. if data['status'] == 0:
  644. location = data['result']['location']
  645. return {
  646. 'lng': location['lng'],
  647. 'lat': location['lat'],
  648. 'accuracy': data['result'].get('accuracy', 0)
  649. }
  650. except Exception as e:
  651. print(f"腾讯地图IP定位失败: {e}")
  652. return None