index.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  6. <title>地块地图</title>
  7. <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/leaflet/1.9.4/leaflet.css">
  8. <link rel="stylesheet" href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css">
  9. <script src="https://cdn.bootcdn.net/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
  10. <script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.min.js"></script>
  11. <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
  12. <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  13. <style>
  14. html,
  15. body,
  16. #map,
  17. .map_box {
  18. width: 100%;
  19. height: 100%;
  20. margin: 0;
  21. padding: 0;
  22. }
  23. .btn-group {
  24. position: fixed;
  25. top: 10px;
  26. left: 10px;
  27. z-index: 999999;
  28. display: flex;
  29. flex-direction: column;
  30. gap: 6px;
  31. }
  32. .btn-group button {
  33. padding: 8px 12px;
  34. background: #007aff;
  35. color: #fff;
  36. border: none;
  37. border-radius: 6px;
  38. font-size: 14px;
  39. white-space: nowrap;
  40. cursor: pointer;
  41. }
  42. #clearDraw {
  43. background: #ff3b30;
  44. }
  45. #uploadLand {
  46. background: #28a745;
  47. display: none;
  48. }
  49. #areaInfo {
  50. background: #fff;
  51. padding: 6px 10px;
  52. border-radius: 6px;
  53. font-size: 13px;
  54. color: #333;
  55. font-weight: bold;
  56. box-shadow: 0 1px 3px #00000033;
  57. }
  58. .draw-area-label {
  59. background: #007aff;
  60. color: #fff;
  61. font-weight: bold;
  62. padding: 5px 10px;
  63. border-radius: 6px;
  64. font-size: 14px;
  65. white-space: nowrap;
  66. box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  67. pointer-events: none;
  68. }
  69. .land-label {
  70. background: #ffffff;
  71. padding: 4px 8px;
  72. border-radius: 6px;
  73. font-size: 12px;
  74. color: #d32f2f;
  75. text-align: center;
  76. white-space: nowrap;
  77. box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  78. pointer-events: none !important;
  79. font-weight: bold;
  80. border: 1px solid #eee;
  81. line-height: 1.4;
  82. display: inline-block;
  83. }
  84. /* 加载动画样式 */
  85. .loading-overlay {
  86. position: fixed;
  87. top: 0;
  88. left: 0;
  89. width: 100%;
  90. height: 100%;
  91. background-color: rgba(255, 255, 255, 0.9);
  92. display: flex;
  93. align-items: center;
  94. justify-content: center;
  95. z-index: 9999999;
  96. flex-direction: column;
  97. gap: 15px;
  98. }
  99. .loading-spinner {
  100. width: 40px;
  101. height: 40px;
  102. border: 4px solid #e0e0e0;
  103. border-top: 4px solid #007aff;
  104. border-radius: 50%;
  105. animation: spin 1s linear infinite;
  106. }
  107. .loading-text {
  108. font-size: 15px;
  109. color: #333;
  110. }
  111. @keyframes spin {
  112. 0% {
  113. transform: rotate(0deg);
  114. }
  115. 100% {
  116. transform: rotate(360deg);
  117. }
  118. }
  119. </style>
  120. </head>
  121. <body>
  122. <!-- 加载动画 -->
  123. <div class="loading-overlay" id="loadingOverlay">
  124. <div class="loading-spinner"></div>
  125. <div class="loading-text">正在加载地块数据...</div>
  126. </div>
  127. <div class="map_box">
  128. <div id="map"></div>
  129. <div class="btn-group">
  130. <!-- <button id="sendToMini">选中地块并回传</button> -->
  131. <button id="startDraw">开启打点绘图</button>
  132. <button id="clearDraw">清除绘图</button>
  133. <button id="uploadLand">✅ 上传当前绘制地块</button>
  134. <div id="areaInfo">绘图面积:0 亩 / 0 ㎡</div>
  135. </div>
  136. </div>
  137. <script>
  138. window.onload = function() {
  139. // 工具函数:获取URL参数
  140. function getUrlParams() {
  141. var params = {};
  142. var url = window.location.href;
  143. var queryStr = url.split('?')[1];
  144. if (!queryStr) return params;
  145. var queryArr = queryStr.split('&');
  146. for (var i = 0; i < queryArr.length; i++) {
  147. var item = queryArr[i];
  148. if (!item) continue;
  149. var eqIndex = item.indexOf('=');
  150. var key = eqIndex > -1 ? item.substring(0, eqIndex) : item;
  151. var val = eqIndex > -1 ? decodeURIComponent(item.substring(eqIndex + 1)) : '';
  152. params[key] = val;
  153. }
  154. return params;
  155. }
  156. const params = getUrlParams();
  157. // 🔥 接收参数
  158. const cbfbm = params.data || '';
  159. const pageType = params.type || '';
  160. const dsType = params.displayType || '';
  161. const TIANDITU_TK = '0f31ca9a7e9a0b80f5e5d53df0a02925';
  162. // 初始化地图(先不设置中心点,加载数据后自动定位)
  163. var map = L.map('map', {
  164. zoomControl: true,
  165. maxZoom: 28,
  166. minZoom: 1,
  167. zoomSnap: 0.5,
  168. zoomDelta: 0.5
  169. }).setView([30, 114], 5); // 初始小视野,加载后自动调整
  170. // 天地图底图
  171. L.tileLayer(
  172. `https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TK}`, {
  173. maxNativeZoom: 18,
  174. maxZoom: 28,
  175. minZoom: 1
  176. }).addTo(map);
  177. L.tileLayer(
  178. `https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TK}`, {
  179. maxNativeZoom: 18,
  180. maxZoom: 28,
  181. minZoom: 1
  182. }).addTo(map);
  183. // 初始化绘图工具
  184. map.pm.setLang('zh');
  185. // 类型为 farmer 时隐藏绘图功能
  186. if (pageType === 'farmer') {
  187. $('#startDraw').hide();
  188. $('#clearDraw').hide();
  189. $('#areaInfo').hide();
  190. }
  191. var landLayerGroup = L.layerGroup().addTo(map);
  192. var currentSelectedPolygon = null;
  193. const SHOW_LABEL_MIN_ZOOM = 18;
  194. let allLandItems = [];
  195. let drawnPolygon = null;
  196. let areaLabelMarker = null;
  197. // 点击边线添加顶点
  198. function enableClickAddVertex(layer) {
  199. layer.off('click');
  200. layer.on('click', function(e) {
  201. if (!layer.pm.enabled()) return;
  202. const latlng = e.latlng;
  203. const latlngs = layer.getLatLngs()[0];
  204. let closestIndex = -1;
  205. let minDist = Infinity;
  206. for (let i = 0; i < latlngs.length - 1; i++) {
  207. const p1 = latlngs[i];
  208. const p2 = latlngs[i + 1];
  209. const dist = L.LineUtil.pointToSegmentDistance(
  210. map.latLngToContainerPoint(latlng),
  211. map.latLngToContainerPoint(p1),
  212. map.latLngToContainerPoint(p2)
  213. );
  214. if (dist < minDist) {
  215. minDist = dist;
  216. closestIndex = i + 1;
  217. }
  218. }
  219. if (closestIndex !== -1) {
  220. latlngs.splice(closestIndex, 0, latlng);
  221. layer.setLatLngs(latlngs);
  222. layer.fire('pm:edit');
  223. }
  224. });
  225. }
  226. // 计算面积(亩 + 平方米)
  227. function calcAreaMu(latLngs) {
  228. if (!latLngs || latLngs.length < 3) return { mu: 0, m2: 0 };
  229. let m2 = 0;
  230. if (L.PM && L.PM.Utils && L.PM.Utils.calculatePolygonArea) {
  231. m2 = L.PM.Utils.calculatePolygonArea(latLngs);
  232. } else {
  233. const R = 6378137;
  234. const rad = Math.PI / 180;
  235. for (let i = 0; i < latLngs.length; i++) {
  236. const p1 = latLngs[i];
  237. const p2 = latLngs[(i + 1) % latLngs.length];
  238. m2 += (p2.lng - p1.lng) * rad * (2 + Math.sin(p1.lat * rad) + Math.sin(p2.lat * rad));
  239. }
  240. m2 = Math.abs(m2 * R * R / 2);
  241. }
  242. const mu = (m2 / 666.67).toFixed(2);
  243. return {
  244. mu: mu,
  245. m2: m2.toFixed(2)
  246. };
  247. }
  248. // 打印地块信息
  249. function printPolygonInfo(layer) {
  250. if (!layer) return;
  251. const latlngs = layer.getLatLngs()[0];
  252. const { mu, m2 } = calcAreaMu(latlngs);
  253. console.log('====================================');
  254. console.log('✅ 地块点位:');
  255. latlngs.forEach((p, i) => console.log(`第${i+1}点:${p.lat.toFixed(8)}, ${p.lng.toFixed(8)}`));
  256. console.log(`✅ 面积:${mu} 亩 / ${m2} 平方米`);
  257. console.log('====================================');
  258. }
  259. // 更新面积标签
  260. function updateAreaLabel() {
  261. if (!drawnPolygon) return;
  262. let latLngs = drawnPolygon.getLatLngs()[0];
  263. const { mu, m2 } = calcAreaMu(latLngs);
  264. $('#areaInfo').text(`绘图面积:${mu} 亩 / ${m2} ㎡`);
  265. let center = drawnPolygon.getCenter();
  266. if (areaLabelMarker) map.removeLayer(areaLabelMarker);
  267. areaLabelMarker = L.marker(center, {
  268. icon: L.divIcon({
  269. className: 'draw-area-label',
  270. html: `${mu} 亩 / ${m2} ㎡`,
  271. iconSize: null,
  272. iconAnchor: [50, 50]
  273. })
  274. }).addTo(map);
  275. }
  276. // 上传绘制地块
  277. function uploadDrawLand() {
  278. if (!drawnPolygon) {
  279. alert("请先绘制地块!");
  280. return;
  281. }
  282. const latlngs = drawnPolygon.getLatLngs()[0];
  283. const { mu, m2 } = calcAreaMu(latlngs);
  284. const points = latlngs.map(p => [p.lng, p.lat]);
  285. const confirmUpload = confirm(`确认上传当前地块?\n\n面积:${mu} 亩 / ${m2} ㎡\n\n确认后将自动返回小程序`);
  286. if (!confirmUpload) {
  287. console.log("用户取消上传");
  288. return;
  289. }
  290. const postData = {
  291. type: "drawLandFinished",
  292. points: points,
  293. areaMu: mu,
  294. areaM2: m2
  295. };
  296. console.log("H5 发送给小程序:", postData);
  297. wx.miniProgram.postMessage({
  298. data: postData
  299. });
  300. wx.miniProgram.navigateBack();
  301. }
  302. // 按钮事件:开启/关闭绘图
  303. $('#startDraw').click(function() {
  304. if (map.pm.Draw.Polygon._enabled) {
  305. map.pm.Draw.Polygon.disable();
  306. $(this).text('开启打点绘图').css('background', '#007aff');
  307. return;
  308. }
  309. $(this).text('关闭打点绘图').css('background', '#ff3b30');
  310. $('#uploadLand').hide();
  311. map.pm.Draw.Polygon.enable({
  312. allowSelfIntersection: false,
  313. markerStyle: {
  314. radius: 5
  315. }
  316. });
  317. });
  318. // 清除绘图
  319. $('#clearDraw').click(function() {
  320. if (drawnPolygon) {
  321. map.removeLayer(drawnPolygon);
  322. drawnPolygon = null;
  323. }
  324. if (areaLabelMarker) {
  325. map.removeLayer(areaLabelMarker);
  326. areaLabelMarker = null;
  327. }
  328. $('#areaInfo').text('绘图面积:0 亩 / 0 ㎡');
  329. $('#startDraw').text('开启打点绘图').css('background', '#007aff');
  330. $('#uploadLand').hide();
  331. });
  332. $('#uploadLand').click(uploadDrawLand);
  333. // 绘图完成事件
  334. map.on('pm:create', function(e) {
  335. let layer = e.layer;
  336. if (drawnPolygon) map.removeLayer(drawnPolygon);
  337. drawnPolygon = layer;
  338. layer.pm.enable({
  339. allowAddVertex: true,
  340. allowDeleteVertex: true,
  341. markerEditable: true,
  342. hideMiddleMarkers: false
  343. });
  344. enableClickAddVertex(layer);
  345. map.pm.Draw.Polygon.disable();
  346. $('#startDraw').text('开启打点绘图').css('background', '#007aff');
  347. $('#uploadLand').show();
  348. updateAreaLabel();
  349. printPolygonInfo(layer);
  350. layer.on('pm:edit pm:vertexadded pm:vertexremoved', function() {
  351. updateAreaLabel();
  352. printPolygonInfo(layer);
  353. });
  354. });
  355. // 获取多边形中心点
  356. function getPolygonCenter(points) {
  357. let latSum = 0,
  358. lngSum = 0,
  359. len = points.length;
  360. points.forEach(p => {
  361. latSum += p[1];
  362. lngSum += p[0];
  363. });
  364. return [latSum / len, lngSum / len];
  365. }
  366. // 自适应缩放到所有地块
  367. function fitMapToAllLands(allLandPoints) {
  368. if (!allLandPoints.length) return;
  369. let bounds = [];
  370. allLandPoints.forEach(points => {
  371. points.forEach(p => bounds.push([p[1], p[0]]));
  372. });
  373. map.fitBounds(L.latLngBounds(bounds), {
  374. padding: [30, 30]
  375. });
  376. }
  377. // 更新标签显示
  378. function updateVisibleLabels() {
  379. const currentZoom = map.getZoom();
  380. const bounds = map.getBounds();
  381. allLandItems.forEach(item => {
  382. const {
  383. label,
  384. center
  385. } = item;
  386. if (currentZoom < SHOW_LABEL_MIN_ZOOM || !bounds.contains(center)) {
  387. if (landLayerGroup.hasLayer(label)) landLayerGroup.removeLayer(label);
  388. } else {
  389. if (!landLayerGroup.hasLayer(label)) landLayerGroup.addLayer(label);
  390. }
  391. });
  392. }
  393. // 加载地块数据
  394. function loadLandData() {
  395. console.log(123,dsType)
  396. let ctData = {}
  397. if(dsType === 'all'){
  398. ctData = {
  399. cbfbms: cbfbm,
  400. }
  401. }else if(dsType === 'single'){
  402. ctData = {
  403. dkbms: cbfbm,
  404. }
  405. }
  406. $.ajax({
  407. url: "http://192.168.110.235:1860/miniProgram/dk/arcgisGeojson",
  408. type: "GET",
  409. data: ctData, //
  410. dataType: "json",
  411. success: function(res) {
  412. // 加载完成 → 隐藏加载动画
  413. $('#loadingOverlay').hide();
  414. landLayerGroup.clearLayers();
  415. allLandItems = [];
  416. let allLandPoints = [];
  417. if (res.code === 200 && res.data?.features?.length) {
  418. res.data.features.forEach(feature => {
  419. let rings = feature.geometry?.rings?.[0];
  420. if (!rings) return;
  421. let points = rings.map(p => [p[1], p[0]]);
  422. allLandPoints.push(rings);
  423. let center = getPolygonCenter(rings);
  424. let attr = feature.attributes;
  425. let polygon = L.polygon(points, {
  426. color: '#ffaa00',
  427. weight: 2,
  428. fillColor: '#ffaa00',
  429. fillOpacity: 0.3,
  430. attributes: attr
  431. }).addTo(landLayerGroup);
  432. let text =
  433. `${attr.cbfmc||'地块'} | ${(attr.scmjm||0).toFixed(2)}亩 | ${attr.scmj||0}㎡`;
  434. let label = L.marker(center, {
  435. icon: L.divIcon({
  436. className: 'land-label',
  437. html: text,
  438. iconSize: null,
  439. iconAnchor: [50, 50]
  440. })
  441. });
  442. allLandItems.push({
  443. label,
  444. center,
  445. polygon
  446. });
  447. // 点击选中地块
  448. polygon.on('click', e => {
  449. landLayerGroup.eachLayer(l => {
  450. if (l.options?.attributes) l.setStyle({
  451. color: '#ffaa00',
  452. weight: 2,
  453. fillOpacity: 0.3
  454. });
  455. });
  456. e.target.setStyle({
  457. color: '#ffff00',
  458. weight: 4,
  459. fillOpacity: 0.5
  460. });
  461. currentSelectedPolygon = e.target;
  462. });
  463. });
  464. // 自动定位到所有地块中心
  465. fitMapToAllLands(allLandPoints);
  466. updateVisibleLabels();
  467. }
  468. },
  469. error: () => {
  470. $('#loadingOverlay').hide();
  471. alert("获取地块失败");
  472. }
  473. });
  474. }
  475. // 地图缩放/移动时更新标签
  476. map.on('zoomend moveend', updateVisibleLabels);
  477. // 选中地块回传给小程序
  478. $("#sendToMini").click(function() {
  479. if (!currentSelectedPolygon) return alert("请先选中地块");
  480. let data = {
  481. points: currentSelectedPolygon.getLatLngs()[0].map(i => [i.lat, i.lng]),
  482. attr: currentSelectedPolygon.options.attributes
  483. };
  484. console.log("提交数据:", data);
  485. wx.miniProgram.postMessage({
  486. data: {
  487. type: "landSelected",
  488. data: data
  489. }
  490. });
  491. alert("已回传给小程序");
  492. });
  493. // 开始加载数据
  494. loadLandData();
  495. };
  496. </script>
  497. </body>
  498. </html>