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