381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			HTML
		
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			HTML
		
	
	
| <!DOCTYPE html>
 | ||
| <html lang="zh-CN">
 | ||
| <head>
 | ||
|   <meta charset="utf-8">
 | ||
|   <title>特殊作业扎点</title>
 | ||
|   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 | ||
|   <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
 | ||
|   <meta http-equiv="X-UA-Compatible" content="IE=Edge">
 | ||
|   <style>
 | ||
|     body, html, #container {
 | ||
|         overflow: hidden;
 | ||
|         width: 100%;
 | ||
|         height: 100%;
 | ||
|         margin: 0;
 | ||
|         font-family: "微软雅黑";
 | ||
|     }
 | ||
|     #hint {
 | ||
|         position: absolute;
 | ||
|         z-index: 9999;
 | ||
|         left: 10px;
 | ||
|         top: 10px;
 | ||
|         padding: 6px 10px;
 | ||
|         background: rgba(255,255,255,0.9);
 | ||
|         border-radius: 4px;
 | ||
|         font-size: 12px;
 | ||
|     }
 | ||
|   </style>
 | ||
| 
 | ||
|   <!-- Baidu Map WebGL (保留你的 key) -->
 | ||
|   <script type="text/javascript"
 | ||
|           src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=OElqFYoKiAH8KFtph8ftLKF5NlNrbCUr"></script>
 | ||
|   <script src="./uni.webview.1.5.4.js"></script>
 | ||
| </head>
 | ||
| <body onload="onLoad()">
 | ||
| <div id="container"></div>
 | ||
| <div id="hint" style="display:none"></div>
 | ||
| </body>
 | ||
| 
 | ||
| <script>
 | ||
|   // 地图与数据容器
 | ||
|   var map = null;
 | ||
|   var marker = null;
 | ||
|   var GSON_LON_LAT_WSG84 = [];       // WGS84 多点 [[lon,lat,alt], ...]
 | ||
|   var GSON_LON_LAT_BD09 = [];        // 转换后的 BD09 二维点 [[lng,lat],...]
 | ||
|   var convertor = null;
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 通用通知宿主(Flutter / 原生 / web)
 | ||
|   // 会尝试多种桥接方式
 | ||
|   // data 可以是对象或字符串
 | ||
|   // ---------------------------
 | ||
|   function notifyHost(data) {
 | ||
|       try {
 | ||
|           // prefer passing object where supported
 | ||
|           if (window.JS && typeof window.JS.postMessage === 'function') {
 | ||
|               // webview_flutter JavaScriptChannel expects string
 | ||
|               var payload = (typeof data === 'string') ? data : JSON.stringify(data);
 | ||
|               window.JS.postMessage(payload);
 | ||
|               return;
 | ||
|           }
 | ||
|           if (window.flutter_inappwebview && typeof window.flutter_inappwebview.callHandler === 'function') {
 | ||
|               // flutter_inappwebview can accept objects
 | ||
|               window.flutter_inappwebview.callHandler('messageHandler', data);
 | ||
|               return;
 | ||
|           }
 | ||
|           if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.JS && typeof window.webkit.messageHandlers.JS.postMessage === 'function') {
 | ||
|               window.webkit.messageHandlers.JS.postMessage(data);
 | ||
|               return;
 | ||
|           }
 | ||
|           // fallback to uni (if present) to keep backward compatibility
 | ||
|           if (window.uni && typeof window.uni.postMessage === 'function') {
 | ||
|               window.uni.postMessage({data: data});
 | ||
|               return;
 | ||
|           }
 | ||
|           // last resort, console (useful for debugging)
 | ||
|           console.log('notifyHost:', data);
 | ||
|       } catch (e) {
 | ||
|           console.error('notifyHost error:', e);
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 尝试从 URL 初始化(如果 Flutter 把参数拼在 URL 上)
 | ||
|   // 支持参数: longitude, latitude, GSON (JSON string, 已 encodeURIComponent)
 | ||
|   // ---------------------------
 | ||
|   function tryInitFromUrl() {
 | ||
|       try {
 | ||
|           const params = new URLSearchParams(window.location.search);
 | ||
|           const lon = params.get('longitude');
 | ||
|           const lat = params.get('latitude');
 | ||
|           const gsonRaw = params.get('GSON'); // 预期是 encodeURIComponent(JSON.stringify([...]))
 | ||
|           var has = false;
 | ||
|           if (lon && lat) has = true;
 | ||
|           if (gsonRaw) has = true;
 | ||
|           if (!has) return false;
 | ||
| 
 | ||
|           var gson = null;
 | ||
|           if (gsonRaw) {
 | ||
|               try {
 | ||
|                   gson = JSON.parse(decodeURIComponent(gsonRaw));
 | ||
|               } catch (e) {
 | ||
|                   // 如果直接是未 encode 的 JSON 字符串,也尝试解析
 | ||
|                   try { gson = JSON.parse(gsonRaw); } catch (e2) { gson = null; }
 | ||
|               }
 | ||
|           }
 | ||
| 
 | ||
|           // 使用从 url 获取到的数据执行初始化
 | ||
|           initWithData({
 | ||
|               longitude: parseFloat(lon),
 | ||
|               latitude: parseFloat(lat),
 | ||
|               GSON: gson
 | ||
|           });
 | ||
|           return true;
 | ||
|       } catch (e) {
 | ||
|           console.warn('tryInitFromUrl error', e);
 | ||
|           return false;
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // Flutter/宿主可调用的初始化函数
 | ||
|   // 支持两种调用方式:
 | ||
|   //  1) initWithData({longitude: xx, latitude: yy, GSON: [...]})
 | ||
|   //  2) initWithData(longitude, latitude, GSONArray)
 | ||
|   // GSONArray 结构应与原来一致([[lon,lat,alt], ...])
 | ||
|   // ---------------------------
 | ||
|   window.initWithData = function() {
 | ||
|       var args = arguments;
 | ||
|       var payload = {};
 | ||
|       if (args.length === 1 && typeof args[0] === 'object') {
 | ||
|           payload = args[0];
 | ||
|       } else {
 | ||
|           // try positional
 | ||
|           payload.longitude = args[0];
 | ||
|           payload.latitude = args[1];
 | ||
|           payload.GSON = args[2];
 | ||
|       }
 | ||
| 
 | ||
|       try {
 | ||
|           if (!payload) payload = {};
 | ||
|           // default safe parse
 | ||
|           var lon = Number(payload.longitude) || 0;
 | ||
|           var lat = Number(payload.latitude) || 0;
 | ||
|           var gson = payload.GSON || [];
 | ||
| 
 | ||
|           // ensure convertor exists
 | ||
|           if (!convertor && window.BMapGL && typeof BMapGL.Convertor === 'function') {
 | ||
|               convertor = new BMapGL.Convertor();
 | ||
|           }
 | ||
| 
 | ||
|           // set global wgs84 list (if provided)
 | ||
|           if (Array.isArray(gson) && gson.length > 0) {
 | ||
|               GSON_LON_LAT_WSG84 = gson;
 | ||
|           }
 | ||
| 
 | ||
|           // init map & polygon
 | ||
|           fnInitMap(lon, lat);
 | ||
|           if (GSON_LON_LAT_WSG84 && GSON_LON_LAT_WSG84.length > 0) {
 | ||
|               fnInitConvertorData(GSON_LON_LAT_WSG84);
 | ||
|           }
 | ||
| 
 | ||
|           showHint('地图已初始化');
 | ||
|           return true;
 | ||
|       } catch (e) {
 | ||
|           console.error('initWithData error', e);
 | ||
|           return false;
 | ||
|       }
 | ||
|   };
 | ||
| 
 | ||
|   // 显示调试提示
 | ||
|   function showHint(text) {
 | ||
|       var el = document.getElementById('hint');
 | ||
|       if (!el) return;
 | ||
|       el.style.display = 'block';
 | ||
|       el.innerText = text;
 | ||
|       setTimeout(function(){ el.style.display = 'none'; }, 4000);
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 页面加载
 | ||
|   // ---------------------------
 | ||
|   function onLoad() {
 | ||
|       // create convertor if possible
 | ||
|       if (window.BMapGL && typeof BMapGL.Convertor === 'function') {
 | ||
|           convertor = new BMapGL.Convertor();
 | ||
|       } else {
 | ||
|           console.warn('BMapGL.Convertor not ready yet.');
 | ||
|       }
 | ||
| 
 | ||
|       // 创建地图对象(延迟绑定 center,等 init 时调用)
 | ||
|       map = new BMapGL.Map('container');
 | ||
|       map.enableScrollWheelZoom(true);
 | ||
|       map.setDisplayOptions({ building: false });
 | ||
|       map.addEventListener('click', MapClick);
 | ||
| 
 | ||
|       // 如果 URL 带参数则自动初始化,否则等待宿主调用 initWithData(...)
 | ||
|       var ok = tryInitFromUrl();
 | ||
|       if (!ok) {
 | ||
|           showHint('等待宿主调用 initWithData(...) 初始化地图');
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 点在多边形内判定(二维,只用 lon/lat)
 | ||
|   // polygon = [[lng,lat], ...]
 | ||
|   // point = [lng, lat]
 | ||
|   // ---------------------------
 | ||
|   function isPointInPolygon(point, polygon) {
 | ||
|       const x = point[0];
 | ||
|       const y = point[1];
 | ||
|       let inside = false;
 | ||
|       for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
 | ||
|           const xi = polygon[i][0], yi = polygon[i][1];
 | ||
|           const xj = polygon[j][0], yj = polygon[j][1];
 | ||
|           const intersect = ((yi > y) !== (yj > y))
 | ||
|               && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
 | ||
|           if (intersect) inside = !inside;
 | ||
|       }
 | ||
|       return inside;
 | ||
|   }
 | ||
| 
 | ||
|   // 地图点击处理
 | ||
|   function MapClick(e) {
 | ||
|       try {
 | ||
|           if (marker) map.removeOverlay(marker);
 | ||
|           marker = new BMapGL.Marker(new BMapGL.Point(e.latlng.lng, e.latlng.lat));
 | ||
|           const x = [e.latlng.lng, e.latlng.lat];
 | ||
|           let inside = (GSON_LON_LAT_BD09 && GSON_LON_LAT_BD09.length > 0) ? isPointInPolygon(x, GSON_LON_LAT_BD09) : true;
 | ||
|           if (!inside) {
 | ||
|               alert("当前选择点位不在区域中!");
 | ||
|               notifyHost({type:'point_selected', ok:false, reason:'out_of_polygon', lng:e.latlng.lng, lat:e.latlng.lat});
 | ||
|           } else {
 | ||
|               map.addOverlay(marker);
 | ||
|               // 把 BD09 -> WGS84 转换后的点回传给宿主
 | ||
|               fnConvertorBd09ToWgs84Data2(e.latlng.lng, e.latlng.lat);
 | ||
|               notifyHost({type:'point_selected', ok:true, lng:e.latlng.lng, lat:e.latlng.lat});
 | ||
|           }
 | ||
|       } catch (err) {
 | ||
|           console.error('MapClick error', err);
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 将 WGS84 多点转换为 BD09 并绘制多边形
 | ||
|   // arr: [[lon,lat,...], ...]  注意原 arr (GSON_LON_LAT_WSG84)顺序是 lon, lat
 | ||
|   // ---------------------------
 | ||
|   function fnInitConvertorData(arr) {
 | ||
|       try {
 | ||
|           if (!convertor) convertor = new BMapGL.Convertor();
 | ||
|           var points = [];
 | ||
|           for (let i = 0; i < arr.length; i++) {
 | ||
|               var xi = arr[i][0], yi = arr[i][1];
 | ||
|               points.push(new BMapGL.Point(xi, yi));
 | ||
|           }
 | ||
|           convertor.translate(points, 1, 5, function(res) {
 | ||
|               if (!res || !res.points) {
 | ||
|                   console.warn('convertor.translate returned no points', res);
 | ||
|                   return;
 | ||
|               }
 | ||
|               GSON_LON_LAT_BD09 = [];
 | ||
|               var list = [];
 | ||
|               for (var p = 0; p < res.points.length; p++) {
 | ||
|                   let item = res.points[p];
 | ||
|                   GSON_LON_LAT_BD09.push([item.lng, item.lat]);
 | ||
|                   list.push(new BMapGL.Point(item.lng, item.lat));
 | ||
|               }
 | ||
|               var polygon = new BMapGL.Polygon(list, {
 | ||
|                   zIndex: 999,
 | ||
|                   strokeColor: 'blue',
 | ||
|                   strokeWeight: 5,
 | ||
|                   strokeOpacity: 0.5
 | ||
|               });
 | ||
|               map.addOverlay(polygon);
 | ||
|           });
 | ||
|       } catch (e) {
 | ||
|           console.error('fnInitConvertorData error', e);
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 初始化地图中心(longitude, latitude 为 WGS84)
 | ||
|   // ---------------------------
 | ||
|   function fnInitMap(longitude, latitude) {
 | ||
|       try {
 | ||
|           if (!convertor) convertor = new BMapGL.Convertor();
 | ||
|           var ponits = [ new BMapGL.Point(longitude, latitude) ];
 | ||
|           convertor.translate(ponits, 1, 5, function(res) {
 | ||
|               if (!res || !res.points || res.points.length === 0) {
 | ||
|                   console.warn('fnInitMap: translate failed', res);
 | ||
|                   return;
 | ||
|               }
 | ||
|               var pt = res.points[0];
 | ||
|               map.centerAndZoom(new BMapGL.Point(pt.lng, pt.lat), 18);
 | ||
|           });
 | ||
|       } catch (e) {
 | ||
|           console.error('fnInitMap error', e);
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // BD09 -> WGS84 (JS 版算法),并把结果 post 给宿主
 | ||
|   // 注意: bdLat, bdLon 参数顺序(原函数输入顺序)
 | ||
|   // 返回 [wgsLat, wgsLon](跟原实现一致)
 | ||
|   // ---------------------------
 | ||
|   const bd09ToWgs84 = (bdLat, bdLon) => {
 | ||
|       const x_pi = (Math.PI * 3000.0) / 180.0;
 | ||
|       const x = bdLon - 0.0065;
 | ||
|       const y = bdLat - 0.006;
 | ||
|       const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
 | ||
|       const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
 | ||
|       const gcjLon = z * Math.cos(theta);
 | ||
|       const gcjLat = z * Math.sin(theta);
 | ||
| 
 | ||
|       let dlat = transformlat(gcjLon - 105.0, gcjLat - 35.0);
 | ||
|       let dlng = transformlng(gcjLon - 105.0, gcjLat - 35.0);
 | ||
|       const radlat = (gcjLat / 180.0) * Math.PI;
 | ||
|       let magic = Math.sin(radlat);
 | ||
|       magic = 1 - 0.006693421622965943 * magic * magic;
 | ||
|       const sqrtmagic = Math.sqrt(magic);
 | ||
|       dlat = (dlat * 180.0) / (((6378245.0 * (1 - 0.006693421622965943)) / (magic * sqrtmagic)) * Math.PI);
 | ||
|       dlng = (dlng * 180.0) / ((6378245.0 / sqrtmagic) * Math.cos(radlat) * Math.PI);
 | ||
|       const mglat = gcjLat + dlat;
 | ||
|       const mglng = gcjLon + dlng;
 | ||
|       const wgsLon = gcjLon * 2 - mglng;
 | ||
|       const wgsLat = gcjLat * 2 - mglat;
 | ||
|       return [wgsLat, wgsLon];
 | ||
|   };
 | ||
| 
 | ||
|   const transformlat = (lng, lat) => {
 | ||
|       let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
 | ||
|       ret += ((20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0) / 3.0;
 | ||
|       ret += ((20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin((lat / 3.0) * Math.PI)) * 2.0) / 3.0;
 | ||
|       ret += ((160.0 * Math.sin((lat / 12.0) * Math.PI) + 320 * Math.sin((lat * Math.PI) / 30.0)) * 2.0) / 3.0;
 | ||
|       return ret;
 | ||
|   };
 | ||
|   const transformlng = (lng, lat) => {
 | ||
|       let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
 | ||
|       ret += ((20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0) / 3.0;
 | ||
|       ret += ((20.0 * Math.sin(lng * Math.PI) + 40.0 * Math.sin((lng / 3.0) * Math.PI)) * 2.0) / 3.0;
 | ||
|       ret += ((150.0 * Math.sin((lng / 12.0) * Math.PI) + 300.0 * Math.sin((lng / 30.0) * Math.PI)) * 2.0) / 3.0;
 | ||
|       return ret;
 | ||
|   };
 | ||
| 
 | ||
|   // ---------------------------
 | ||
|   // 使用百度 convertor(如果可用)把 BD09 点转回 WGS84 并 postMessage 给宿主
 | ||
|   // 备用:也会调用 bd09ToWgs84 本地算法
 | ||
|   // ---------------------------
 | ||
|   function fnConvertorBd09ToWgs84Data2(lng, lat) {
 | ||
|       try {
 | ||
|           // first try convertor.translate from BMapGL
 | ||
|           if (convertor && typeof convertor.translate === 'function') {
 | ||
|               var pts = [ new BMapGL.Point(lng, lat) ];
 | ||
|               convertor.translate(pts, 5, 1, function(res) {
 | ||
|                   if (res && res.points && res.points.length > 0) {
 | ||
|                       var p = res.points[0];
 | ||
|                       // res.points are in WGS84? depends on convert params; keep compatibility:
 | ||
|                       notifyHost({type:'converted', longitue: p.lng, latitude: p.lat});
 | ||
|                       return;
 | ||
|                   }
 | ||
|                   // fallback to local algorithm if convertor result absent
 | ||
|                   var w = bd09ToWgs84(lat, lng);
 | ||
|                   notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
 | ||
|               });
 | ||
|           } else {
 | ||
|               var w = bd09ToWgs84(lat, lng);
 | ||
|               notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
 | ||
|           }
 | ||
|       } catch (e) {
 | ||
|           console.error('fnConvertorBd09ToWgs84Data2 error', e);
 | ||
|           var w = bd09ToWgs84(lat, lng);
 | ||
|           notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
 | ||
|       }
 | ||
|   }
 | ||
| 
 | ||
|   // 兼容老方法名
 | ||
|   window.fnInitMap = fnInitMap;
 | ||
|   window.fnInitConvertorData = fnInitConvertorData;
 | ||
|   window.fnConvertorBd09ToWgs84Data2 = fnConvertorBd09ToWgs84Data2;
 | ||
| </script>
 | ||
| </html>
 |