gisviewer-vue3-arcgis
Version:
在应用中引入私服的包, 项目根目录下 新建文件 .npmrc 在 .npmrc 中对包源进行配置:
384 lines (383 loc) • 12.4 kB
JavaScript
import l from "ol/Feature";
import { LineString as f, Point as d } from "ol/geom";
import C from "ol/layer/Vector";
import w from "ol/source/Vector";
import { Style as u, Stroke as m, Circle as S, Fill as y, Icon as x } from "ol/style";
import { toRaw as M } from "vue";
import N from "../../../stores/index.mjs";
import { getLineStyle as b } from "../style/line-style.mjs";
import { getPointStyle as I } from "../style/point-style.mjs";
const g = 13, p = 17;
class T {
/**
* 构造函数
* @param map - OpenLayers 地图实例
*/
constructor(e) {
this.subSystemDataMap = /* @__PURE__ */ new Map(), this.zoomChangeListener = null, this.grayLineStyle = new u({
stroke: new m({
color: "rgba(200, 200, 200, 0.8)",
width: 2
})
}), this.grayPointStyle = new u({
image: new S({
radius: 4,
fill: new y({ color: "rgba(200, 200, 200, 0.8)" }),
stroke: new m({ color: "rgba(150, 150, 150, 0.8)", width: 1.5 })
})
}), this.highlightState = null;
const i = N.useAppDataStore;
M(
i.olMapInitializer
).subscribeClick(
(r, s, o, n) => {
this.mapClick(r, o);
},
{ triggerOnBlankClick: !0 }
), this.map = e, this.source = new w(), this.signalSystemLayer = new C({
source: this.source
}), this.signalSystemLayer.set("id", "signal-system-layer"), this.map.addLayer(this.signalSystemLayer), this.setupZoomListener();
}
mapClick(e, i) {
if (e === "map-blank") {
this.resetHighlight();
return;
}
if (e !== "sub-district-road" && e !== "sub-district-node")
return;
const { areaCode: t, subCode: r } = i;
this.focusSubSystem(t, r), this.highlight({
areaCode: t,
subAreaCode: r,
nonHighlightMode: "gray"
});
}
/**
* 设置 zoom 变化监听器
*/
setupZoomListener() {
let e = null;
this.zoomChangeListener = () => {
const i = this.map.getView().getZoom() ?? 0;
if (e === null) {
e = i;
return;
}
(this.hasCrossedThreshold(e, i, g + 1) || this.hasCrossedThreshold(
e,
i,
p
)) && this.reRenderAllSubSystems(), e = i;
}, this.map.getView().on("change:resolution", this.zoomChangeListener);
}
/**
* 判断 zoom 是否跨越了指定阈值
* @param oldZoom - 旧的 zoom 值
* @param newZoom - 新的 zoom 值
* @param threshold - 阈值
* @returns 是否跨越阈值
*/
hasCrossedThreshold(e, i, t) {
const r = Math.floor(e), s = Math.floor(i);
return r < t && s >= t || r >= t && s < t;
}
/**
* 重新渲染所有已存储的子系统
*/
reRenderAllSubSystems() {
this.source.getFeatures().filter((i) => i.get("subCode") !== void 0).forEach((i) => {
this.source.removeFeature(i);
}), this.subSystemDataMap.forEach(({ subSystem: i, areaCode: t }) => {
this.renderSubSystem(i, t);
});
}
/**
* 计算点集的重心坐标
* @param points - 点坐标数组 [{x, y}, ...]
* @returns 重心坐标 [x, y]
*/
calculateCentroid(e) {
if (e.length === 0)
return [0, 0];
const i = e.reduce((r, s) => r + s.x, 0), t = e.reduce((r, s) => r + s.y, 0);
return [i / e.length, t / e.length];
}
/** 根据区控和子区编码查找子区 */
findSubSystem(e, i) {
if (!i)
return null;
for (const {
subSystem: t,
areaCode: r
} of this.subSystemDataMap.values())
if (t.subAreaCode === i && (!e || (r || "") === e))
return t;
return null;
}
/** 聚焦子区:优先保证可见所有路口 */
focusSubSystem(e, i) {
const t = this.findSubSystem(e, i);
if (!t)
return;
const r = this.getSubSystemCrossExtent(t);
if (r) {
this.map.getView().fit(r, {
padding: [80, 80, 80, 80],
maxZoom: 17,
duration: 350
});
return;
}
const s = this.getSubSystemCenter(t);
s && this.map.getView().animate({
center: s,
zoom: 17,
duration: 350
});
}
/** 计算子区路口范围,确保地图能看到所有路口 */
getSubSystemCrossExtent(e) {
if (!e.children || e.children.length === 0)
return null;
let i = 1 / 0, t = 1 / 0, r = -1 / 0, s = -1 / 0;
return e.children.forEach((o) => {
const n = Number(o.x || o.lon), a = Number(o.y || o.lat);
Number.isNaN(n) || Number.isNaN(a) || (i = Math.min(i, n), t = Math.min(t, a), r = Math.max(r, n), s = Math.max(s, a));
}), i === 1 / 0 || t === 1 / 0 || r === -1 / 0 || s === -1 / 0 ? null : [i, t, r, s];
}
/** 根据子区数据计算中心点 */
getSubSystemCenter(e) {
if (e.children && e.children.length > 0) {
const i = e.children.map((t) => {
const r = Number(t.x || t.lon), s = Number(t.y || t.lat);
return { x: r, y: s };
}).filter((t) => !Number.isNaN(t.x) && !Number.isNaN(t.y));
if (i.length > 0)
return this.calculateCentroid(i);
}
if (e.subShape) {
let i;
typeof e.subShape == "string" ? i = JSON.parse(e.subShape) : i = e.subShape;
let t = 1 / 0, r = 1 / 0, s = -1 / 0, o = -1 / 0;
if (i.forEach((n) => {
n.forEach((a) => {
const [h, c] = a;
typeof h != "number" || typeof c != "number" || (t = Math.min(t, h), r = Math.min(r, c), s = Math.max(s, h), o = Math.max(o, c));
});
}), t !== 1 / 0 && r !== 1 / 0 && s !== -1 / 0 && o !== -1 / 0)
return [(t + s) / 2, (r + o) / 2];
}
return null;
}
showSignalSystem(e) {
e.areaList.forEach((t) => {
this.showSystem(t);
});
const i = this.source.getExtent();
return i && i[0] !== 1 / 0 ? new Promise((t) => {
this.map.getView().fit(i, {
padding: [50, 50, 50, 50],
callback: () => {
t({ status: 0, message: "success" });
}
});
}) : { status: 1, message: "No features to display" };
}
removeSignalSystem(e) {
if (!e) {
this.source.clear(), this.subSystemDataMap.clear(), this.zoomChangeListener && (this.map.getView().un("change:resolution", this.zoomChangeListener), this.zoomChangeListener = null);
return;
}
this.source.getFeatures().filter((t) => t.get("areaCode") === e).forEach((t) => {
this.source.removeFeature(t);
}), this.subSystemDataMap.forEach(({ subSystem: t, areaCode: r }) => {
r === r && this.subSystemDataMap.delete(r + "-" + t.subAreaCode);
});
}
showSystem(e) {
if (e.shape) {
let i;
typeof e.shape == "string" ? i = JSON.parse(e.shape) : i = e.shape;
const t = b(e.lineSymbol, e), r = new l({
geometry: new f(i),
areaCode: e.areaCode,
type: "signal-system-boundary"
});
r.setStyle(t), r.set("_originalStyle", t), this.source.addFeature(r);
}
e.children && e.children.forEach((i) => {
this.showSubSystem(i, e.areaCode);
});
}
/**
* 展示多个信号子区系统
* 遍历所有子区并渲染,完成后自动缩放地图视角以显示所有子区
*
* @param params - 展示参数,包含子区列表
* @returns 操作结果
*/
async showSubSignalSystem(e) {
e.areaList.forEach((t) => {
this.showSubSystem(t);
});
const i = this.source.getExtent();
return i && i[0] !== 1 / 0 ? new Promise((t) => {
this.map.getView().fit(i, {
padding: [50, 50, 50, 50],
callback: () => {
t({ status: 0, message: "success" });
}
});
}) : { status: 1, message: "No features to display" };
}
/**
* 移除信号子区系统
* 若不传 subCode,则清除所有子区;否则只清除指定子区
*
* @param subCode - 可选,子区编码。不传则清除全部
*/
removeSubSignalSystem(e) {
if (!e) {
this.source.clear();
return;
}
this.source.getFeatures().filter((t) => t.get("subCode") === e).forEach((t) => {
this.source.removeFeature(t);
});
}
highlight(e) {
if (e.areaCode)
this.highlightState = {
areaCode: e.areaCode,
subAreaCode: e.subAreaCode,
nonHighlightMode: e.nonHighlightMode || "hide",
needZoom: e.needZoom || !1
};
else
return { status: -1, message: "No areaCode provided" };
return this.reRenderAllSubSystems(), this.highlightState.needZoom && this.focusSubSystem(e.areaCode, e.subAreaCode), { status: 0, message: "ok" };
}
resetHighlight() {
this.highlightState = null, this.source.getFeatures().forEach((e) => {
const i = e.get("_originalStyle");
e.setStyle(i);
}), this.reRenderAllSubSystems();
}
/**
* 渲染单个信号子区系统
* 包括子区道路线和路口节点的渲染
*
* @param subSystem - 信号子区系统数据
* @param areaCode - 区域编码,用于关联要素
*/
showSubSystem(e, i) {
const t = i + "-" + e.subAreaCode;
this.subSystemDataMap.set(t, { subSystem: e, areaCode: i }), this.renderSubSystem(e, i);
}
/**
* 根据当前 zoom 级别渲染子系统
* zoom <= 13: 显示重心图标(不显示道路和路口)
* zoom >= 14: 显示 subShape 和 children
*
* @param subSystem - 子区系统数据
* @param areaCode - 区控编码
*/
renderSubSystem(e, i) {
const t = Math.floor(this.map.getView().getZoom() ?? 0);
let r = !1;
if (this.highlightState) {
const s = this.highlightState.areaCode === i, o = !this.highlightState.subAreaCode || e.subAreaCode === this.highlightState.subAreaCode;
if (!(s && o)) {
if (this.highlightState.nonHighlightMode === "hide")
return;
r = !0;
}
}
r && t <= g || (t <= g ? this.renderCentroidIcon(e, i) : this.renderSubSystemDetail(e, i, r));
}
/**
* 渲染子系统重心图标
* @param subSystem - 信号子区系统数据
* @param areaCode - 区域编码
*/
renderCentroidIcon(e, i) {
if (!e.children || e.children.length === 0)
return;
e.children.forEach((o) => {
o.x = Number(o.x || o.lon), o.y = Number(o.y || o.lat);
});
const t = this.calculateCentroid(e.children), r = new u({
image: new x({
src: "/GisViewerAssets/Images/gis/ic_zixitong.png",
scale: 0.5,
anchor: [0.5, 1]
})
}), s = new l({
subCode: e.subAreaCode,
areaCode: i || "",
isCentroid: !0,
// 标记为重心图标
geometry: new d(t),
type: "signal-subsystem-centroid"
});
s.setStyle(r), s.set("_originalStyle", r), this.source.addFeature(s);
}
/**
* 渲染子系统详细信息(道路线和节点)
* @param subSystem - 信号子区系统数据
* @param areaCode - 区域编码
*/
renderSubSystemDetail(e, i, t = !1) {
if (e.subShape) {
let r;
typeof e.subShape == "string" ? r = JSON.parse(e.subShape) : r = e.subShape;
const s = t ? this.grayLineStyle : b(e.roadSymbol), o = r.map((n) => {
const a = new l({
geometry: new f(n),
subCode: e.subAreaCode,
areaCode: i || "",
type: "sub-district-road",
selected: !t
});
return a.setStyle(s), a.set("_originalStyle", s), a;
});
this.source.addFeatures(o);
}
if (e.children) {
const r = Math.floor(this.map.getView().getZoom() ?? 0), s = e.children.map((o) => {
const n = o.symbol || e.nodeSymbol, a = t ? this.grayPointStyle : this.getNodeStyleByZoom(n, o, r), h = new l({
...o,
subCode: e.subAreaCode,
areaCode: i || "",
geometry: new d([o.x, o.y]),
type: "sub-district-node",
selected: !t
});
return h.setStyle(a), h.set("_originalStyle", a), h;
});
this.source.addFeatures(s);
}
}
/**
* 根据缩放层级获取路口节点样式
* 14 <= zoom < 17: 显示蓝色圆点
* zoom >= 17: 显示传入参数配置的 symbol
*
* @param symbol - 节点符号配置
* @param node - 节点数据
* @param zoom - 当前缩放层级
* @returns 样式对象
*/
getNodeStyleByZoom(e, i, t) {
return t >= p ? I(e, i) : new u({
image: new S({
radius: 4,
fill: new y({ color: "#2563eb" }),
stroke: new m({ color: "#ffffff", width: 1.5 })
})
});
}
}
export {
T as default
};