import * as KMap from "@/utils/ol-map/KMap"; import * as util from "@/common/ol_common.js"; import config from "@/api/config.js"; import { Vector as VectorSource } from "ol/source.js"; import { Point } from 'ol/geom'; import { newPoint, newAreaFeature } from "@/utils/map"; import { GeoJSON, WKT } from 'ol/format' import { Feature } from "ol"; import { getArea } from "ol/sphere" import * as turf from "@turf/turf" import Style from "ol/style/Style"; import Icon from "ol/style/Icon"; import { Fill, Text, Stroke } from "ol/style"; import Overlay from "ol/Overlay"; import * as proj from "ol/proj"; import { buffer as bufferExtent, getWidth, getHeight } from "ol/extent"; import proj4 from "proj4" import { register } from "ol/proj/proj4"; proj4.defs("EPSG:38572", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs"); register(proj4); /** * */ class AreaMap { constructor() { this.labelOverlays = []; this.gardenPolygonLayer = new KMap.VectorLayer("gardenPolygonLayer", 999, { minZoom: 8, maxZoom: 22, source: new VectorSource({}), style: (f) => this.getZoneStyle(f), }); // 位置图标 this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, { style: (f) => { let pointIcon = new Style({ image: new Icon({ src: require("@/assets/img/home/garden-point.png"), scale: 0.5, anchor: [0.5, 1], }), }); let nameText = new Style({ text: new Text({ font: "14px sans-serif", text: f.get("name"), offsetY: 10, fill: new Fill({ color: "#fff" }), // 字体颜色 stroke: new Stroke({ color: "#000", width: 0.5, }), }), }); return [pointIcon, nameText] }, }); } getZoneStyle(f) { const flySpeed = f.get("fly_speed"); const labelStyle = f.get("label_style") || (flySpeed ? "card" : "legacy"); const fillColor = f.get("fill_color") || "#00000080"; const strokeColor = f.get("stroke_color") || "#2199F8"; const zoneName = f.get("zone_name") || (f.get("mianji") ? `${f.get("mianji")}亩` : ""); const styles = [ new Style({ fill: new Fill({ color: fillColor }), stroke: new Stroke({ color: strokeColor, width: 2 }), }), ]; if (!zoneName) return styles; if (labelStyle === "legacy") { styles.push( new Style({ text: new Text({ font: "14px sans-serif", text: zoneName, offsetY: 10, fill: new Fill({ color: "#fff" }), stroke: new Stroke({ color: "#000", width: 0.5, }), }), }) ); return styles; } if (labelStyle === "simple") { styles.push( new Style({ text: new Text({ text: zoneName, font: "13px sans-serif", fill: new Fill({ color: "#ffffff" }), backgroundFill: new Fill({ color: "rgba(80, 80, 80, 0.85)" }), padding: [4, 8, 4, 8], }), }) ); return styles; } return styles; } createZoneCardLabelElement(zoneName, flySpeed) { const wrap = document.createElement("div"); Object.assign(wrap.style, { background: "#ffffff", borderRadius: "4px", padding: "6px 10px", boxShadow: "0 1px 4px rgba(0, 0, 0, 0.08)", textAlign: "center", pointerEvents: "none", whiteSpace: "nowrap", }); const title = document.createElement("div"); title.textContent = zoneName; Object.assign(title.style, { fontSize: "13px", fontWeight: "600", color: "#262626", lineHeight: "18px", }); wrap.appendChild(title); if (flySpeed) { const sub = document.createElement("div"); sub.textContent = `飞巡速度:${flySpeed}`; Object.assign(sub.style, { fontSize: "11px", color: "#999999", lineHeight: "16px", marginTop: "2px", }); wrap.appendChild(sub); } return wrap; } clearLabelOverlays() { if (!this.labelOverlays?.length || !this.kmap?.map) return; this.labelOverlays.forEach((overlay) => { this.kmap.map.removeOverlay(overlay); }); this.labelOverlays = []; } syncLabelOverlays() { this.clearLabelOverlays(); if (!this.kmap?.map || !this.gardenPolygonLayer?.source) return; this.gardenPolygonLayer.source.getFeatures().forEach((f) => { const flySpeed = f.get("fly_speed"); const labelStyle = f.get("label_style") || (flySpeed ? "card" : "legacy"); if (labelStyle !== "card") return; const zoneName = f.get("zone_name"); if (!zoneName) return; const geom = f.getGeometry(); if (!geom?.getInteriorPoint) return; const coord = geom.getInteriorPoint().getCoordinates(); const overlay = new Overlay({ element: this.createZoneCardLabelElement(zoneName, flySpeed), position: coord, positioning: "center-center", stopEvent: false, }); this.kmap.map.addOverlay(overlay); this.labelOverlays.push(overlay); }); } initMap(location, target, options = {}) { const { interactive = false } = options; let level = 16; let coordinate = util.wktCastGeom(location).getFirstCoordinate(); // dragPan / mouseWheelZoom 需在 Map 构造时传入,interactive 为 true 时才能拖拽与缩放 this.kmap = new KMap.Map( target, level, coordinate[0], coordinate[1], null, 8, 22, undefined, interactive, interactive ); if (!interactive && this.kmap?.map) { this.kmap.map.getInteractions().forEach((i) => i.setActive(false)); } let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png"; this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2); this.kmap.addLayer(this.gardenPolygonLayer.layer); this.kmap.addLayer(this.clickPointLayer.layer); } initLayer(rangeWkt) { if (!rangeWkt) { this.initZones([]); return; } this.initZones([{ zone_geometry: rangeWkt }]); } /** @param {{ zone_geometry?: string, zone_name?: string, fly_speed?: string, fill_color?: string, stroke_color?: string, label_style?: 'card'|'simple' }[]} zones */ initZones(zones) { this.clearLabelOverlays(); if (this.gardenPolygonLayer.source) { this.gardenPolygonLayer.source.clear(); } if (this.clickPointLayer.source) { this.clickPointLayer.source.clear(); } const list = Array.isArray(zones) ? zones : []; const seen = new Set(); list.forEach((zone) => { const wkt = typeof zone?.zone_geometry === "string" ? zone.zone_geometry.trim() : ""; if (!wkt || seen.has(wkt)) return; seen.add(wkt); try { const f = newAreaFeature({ geomWkt: wkt }, "geomWkt"); f.set("zone_name", zone.zone_name ?? ""); if (zone.fly_speed) f.set("fly_speed", zone.fly_speed); if (zone.fill_color) f.set("fill_color", zone.fill_color); if (zone.stroke_color) f.set("stroke_color", zone.stroke_color); if (zone.label_style) f.set("label_style", zone.label_style); this.gardenPolygonLayer.source.addFeature(f); } catch (e) { console.warn("[AreaMap] zone_geometry parse failed", e); } }); this.syncLabelOverlays(); this.fitView(); } /** * 调整地图视图以适应地块范围 */ fitView() { if (!this.kmap?.map) return; const map = this.kmap.map; map.updateSize(); let extent = this.gardenPolygonLayer.source.getExtent(); if (!extent || !Number.isFinite(extent[0])) return; const ew = getWidth(extent); const eh = getHeight(extent); if (ew <= 0 || eh <= 0) return; // 略外扩,避免描边贴边、视觉上像被裁切 extent = bufferExtent(extent, Math.max(ew, eh) * 0.04); const size = map.getSize(); if (!size || size[0] < 4 || size[1] < 4) return; const [sw, sh] = size; // padding 不能过大:小高度容器里固定 80 会导致内框高度为负,地块无法完整显示 let padX = Math.max(8, Math.min(48, Math.floor(sw * 0.1))); let padY = Math.max(8, Math.min(48, Math.floor(sh * 0.1))); let padTop = Math.min(sh - padY - 4, padY + 14); let padBottom = padY; let padLeft = padX; let padRight = padX; if (padTop + padBottom >= sh - 2) { const p = Math.max(4, Math.floor(sh / 6)); padTop = padBottom = p; } if (padLeft + padRight >= sw - 2) { const p = Math.max(4, Math.floor(sw / 6)); padLeft = padRight = p; } this.kmap.getView().fit(extent, { size, duration: 80, padding: [padTop, padRight, padBottom, padLeft], maxZoom: 22, }); } fitByGardenId(gardenId, hasMapAnimate) { this.gardenPolygonLayer.source.forEachFeature((f) => { if (f.get("organId") == gardenId) { const extent = f.getGeometry().getExtent() this.kmap.getView().fit(extent, { padding: [60, 60, 60, 60], duration: hasMapAnimate ? 1500 : 0 }); const currentZoom = this.kmap.getView().getZoom(); if (currentZoom > 16) { // this.kmap.getView().setZoom(16); this.kmap.getView().animate({ zoom: 16, duration: hasMapAnimate ? 1500 : 0 // 动画持续时间,单位为毫秒 }); } } }) } destroyMap() { this.clearLabelOverlays(); if (this.gardenPolygonLayer?.source) { this.gardenPolygonLayer.source.clear(); } if (this.clickPointLayer?.source) { this.clickPointLayer.source.clear(); } if (this.kmap && typeof this.kmap.destroy === "function") { this.kmap.destroy(); } this.kmap = null; } } export default AreaMap;