@caoguo/maplibre-pipeline 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var maplibre = require('@caoguo/maplibre');
4
+
3
5
  // src/types/index.ts
4
6
  var PIPELINE_TYPES = [
5
7
  "gas",
@@ -721,8 +723,6 @@ function dijkstra(adj, start, end, opts = {}) {
721
723
  }
722
724
  return { distance: dist.get(end) ?? 0, path, found: true };
723
725
  }
724
-
725
- // src/topology/PipelineTopology.ts
726
726
  var PipelineTopology = class {
727
727
  map;
728
728
  dataset;
@@ -733,6 +733,12 @@ var PipelineTopology = class {
733
733
  nodeListeners = /* @__PURE__ */ new Set();
734
734
  pipeListeners = /* @__PURE__ */ new Set();
735
735
  drillListeners = /* @__PURE__ */ new Set();
736
+ /** 当前钻取区域(null = 全量) */
737
+ currentRegion = null;
738
+ /** 当前分层过滤器 */
739
+ layerFilter = null;
740
+ /** click 事件是否已绑定 */
741
+ clickBound = false;
736
742
  constructor(options) {
737
743
  this.map = options.map;
738
744
  this.dataset = options.dataset;
@@ -747,12 +753,7 @@ var PipelineTopology = class {
747
753
  const prefix = this.layerPrefix;
748
754
  const pipeGeoJSON = {
749
755
  type: "FeatureCollection",
750
- features: this.dataset.pipes.filter((p) => {
751
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType)) {
752
- return false;
753
- }
754
- return true;
755
- }).flatMap((p) => {
756
+ features: this.dataset.pipes.filter((p) => this.isPipeVisible(p)).flatMap((p) => {
756
757
  const from = this.dataset.nodes.find((n) => n.id === p.fromNode);
757
758
  const to = this.dataset.nodes.find((n) => n.id === p.toNode);
758
759
  if (!from || !to) return [];
@@ -777,19 +778,14 @@ var PipelineTopology = class {
777
778
  };
778
779
  const nodeGeoJSON = {
779
780
  type: "FeatureCollection",
780
- features: this.dataset.nodes.filter((n) => {
781
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType)) {
782
- return false;
783
- }
784
- return true;
785
- }).map((n) => ({
781
+ features: this.dataset.nodes.filter((n) => this.isNodeVisible(n)).map((n) => ({
786
782
  type: "Feature",
787
783
  geometry: { type: "Point", coordinates: [n.lng, n.lat] },
788
784
  properties: { nodeId: n.id, kind: n.kind, pipelineType: n.pipelineType ?? "" }
789
785
  }))
790
786
  };
791
- mlMap.addSource(`${prefix}-pipes-src`, pipeGeoJSON);
792
- mlMap.addSource(`${prefix}-nodes-src`, nodeGeoJSON);
787
+ maplibre.upsertSource(mlMap, `${prefix}-pipes-src`, pipeGeoJSON);
788
+ maplibre.upsertSource(mlMap, `${prefix}-nodes-src`, nodeGeoJSON);
793
789
  mlMap.addLayer({
794
790
  id: `${prefix}-pipes-line`,
795
791
  type: "line",
@@ -812,6 +808,33 @@ var PipelineTopology = class {
812
808
  }
813
809
  });
814
810
  this.layerIds.push(`${prefix}-pipes-line`, `${prefix}-nodes-pt`);
811
+ this.bindClickEvents(mlMap, prefix);
812
+ }
813
+ /**
814
+ * 绑定点击事件(设备卡片:点击节点/管段触发订阅回调)。
815
+ * 只绑定一次(render 重建图层后事件仍指向固定 layerId)。
816
+ */
817
+ bindClickEvents(mlMap, prefix) {
818
+ if (this.clickBound) return;
819
+ this.clickBound = true;
820
+ const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n]));
821
+ const pipeById = new Map(this.dataset.pipes.map((p) => [p.id, p]));
822
+ mlMap.on("click", `${prefix}-pipes-line`, (e) => {
823
+ const features = e.features ?? [];
824
+ const f = features[0];
825
+ const pipe = f?.properties?.pipeId ? pipeById.get(f.properties.pipeId) : void 0;
826
+ if (pipe) {
827
+ for (const l of this.pipeListeners) l({ pipe });
828
+ }
829
+ });
830
+ mlMap.on("click", `${prefix}-nodes-pt`, (e) => {
831
+ const features = e.features ?? [];
832
+ const f = features[0];
833
+ const node = f?.properties?.nodeId ? nodeById.get(f.properties.nodeId) : void 0;
834
+ if (node) {
835
+ for (const l of this.nodeListeners) l({ node });
836
+ }
837
+ });
815
838
  }
816
839
  /** 切换着色模式 */
817
840
  setColorBy(mode) {
@@ -857,6 +880,78 @@ var PipelineTopology = class {
857
880
  this.drillListeners.add(fn);
858
881
  return () => this.drillListeners.delete(fn);
859
882
  }
883
+ /** 管段是否可见(管线类型 + 区域钻取 + 分层过滤) */
884
+ isPipeVisible(p) {
885
+ if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType)) {
886
+ return false;
887
+ }
888
+ if (this.currentRegion && p.region !== this.currentRegion) return false;
889
+ const f = this.layerFilter;
890
+ if (f) {
891
+ const props = p.properties ?? {};
892
+ if (f.minDiameter !== void 0 && (props.diameter ?? 0) < f.minDiameter) return false;
893
+ if (f.maxDiameter !== void 0 && (props.diameter ?? Infinity) > f.maxDiameter) return false;
894
+ if (f.material && props.material !== f.material) return false;
895
+ if (f.status && props.status !== f.status) return false;
896
+ if (f.minAgeYears !== void 0 && props.installDate) {
897
+ if (this.ageInYears(props.installDate) < f.minAgeYears) return false;
898
+ }
899
+ }
900
+ return true;
901
+ }
902
+ /** 节点是否可见(管线类型 + 区域钻取) */
903
+ isNodeVisible(n) {
904
+ if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType)) {
905
+ return false;
906
+ }
907
+ if (this.currentRegion && n.region !== this.currentRegion) return false;
908
+ return true;
909
+ }
910
+ /** 计算管段使用年限(年) */
911
+ ageInYears(installDate) {
912
+ const d = new Date(installDate).getTime();
913
+ if (Number.isNaN(d)) return 0;
914
+ return (Date.now() - d) / (365.25 * 24 * 3600 * 1e3);
915
+ }
916
+ /**
917
+ * 搜索定位(按编号/地址/区域/类型)。
918
+ * 返回匹配的节点与管段,供上层 flyTo 定位。
919
+ */
920
+ search(query) {
921
+ const q = query.trim().toLowerCase();
922
+ if (!q) return { nodes: [], pipes: [] };
923
+ const nodes = this.dataset.nodes.filter(
924
+ (n) => n.id.toLowerCase().includes(q) || (n.properties?.code ?? "").toLowerCase().includes(q) || (n.region ?? "").toLowerCase().includes(q) || n.kind.toLowerCase().includes(q)
925
+ );
926
+ const pipes = this.dataset.pipes.filter(
927
+ (p) => p.id.toLowerCase().includes(q) || (p.region ?? "").toLowerCase().includes(q) || (p.properties?.material ?? "").toLowerCase().includes(q)
928
+ );
929
+ return { nodes, pipes };
930
+ }
931
+ /** 层级钻取:下钻到指定区域(过滤渲染 + 触发事件) */
932
+ drillDown(region) {
933
+ const from = this.currentRegion;
934
+ this.currentRegion = region;
935
+ this.render();
936
+ for (const l of this.drillListeners) l({ from, to: region });
937
+ }
938
+ /** 层级钻取:返回上一级(清空区域过滤) */
939
+ drillUp() {
940
+ const from = this.currentRegion;
941
+ this.currentRegion = null;
942
+ this.render();
943
+ for (const l of this.drillListeners) l({ from, to: "" });
944
+ }
945
+ /** 分层控制:按管径/材质/状态/年代过滤 */
946
+ setLayerFilter(filter) {
947
+ this.layerFilter = filter;
948
+ this.render();
949
+ }
950
+ /** 清空分层过滤器 */
951
+ clearLayerFilter() {
952
+ this.layerFilter = null;
953
+ this.render();
954
+ }
860
955
  /** 高亮连通路径(基于节点 ID) */
861
956
  highlightConnectivity(centerId) {
862
957
  const adj = /* @__PURE__ */ new Map();
@@ -1601,8 +1696,6 @@ function convexHullLocal(points) {
1601
1696
  lower.pop();
1602
1697
  return lower.concat(upper);
1603
1698
  }
1604
-
1605
- // src/leakage/LeakagePlume.ts
1606
1699
  var LeakagePlume = class {
1607
1700
  map;
1608
1701
  thresholds;
@@ -1676,7 +1769,7 @@ var LeakagePlume = class {
1676
1769
  properties: { threshold: 0 }
1677
1770
  };
1678
1771
  const id = `${this.layerPrefix}-flood-fill`;
1679
- if (!mlMap.getSource(id)) mlMap.addSource(id, data);
1772
+ maplibre.upsertSource(mlMap, id, data);
1680
1773
  try {
1681
1774
  mlMap.addLayer({
1682
1775
  id,
@@ -1704,7 +1797,7 @@ var LeakagePlume = class {
1704
1797
  };
1705
1798
  const sourceId = `${this.layerPrefix}-gas-src`;
1706
1799
  const layerId = `${this.layerPrefix}-gas-fill`;
1707
- if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);
1800
+ maplibre.upsertSource(mlMap, sourceId, data);
1708
1801
  try {
1709
1802
  mlMap.addLayer({
1710
1803
  id: layerId,
@@ -1728,7 +1821,7 @@ var LeakagePlume = class {
1728
1821
  } catch {
1729
1822
  }
1730
1823
  const ptId = `${this.layerPrefix}-source`;
1731
- mlMap.addSource(ptId, {
1824
+ const ptData = {
1732
1825
  type: "FeatureCollection",
1733
1826
  features: [
1734
1827
  {
@@ -1737,7 +1830,8 @@ var LeakagePlume = class {
1737
1830
  properties: {}
1738
1831
  }
1739
1832
  ]
1740
- });
1833
+ };
1834
+ maplibre.upsertSource(mlMap, ptId, ptData);
1741
1835
  try {
1742
1836
  mlMap.addLayer({
1743
1837
  id: ptId,