三十四、openlayers官网示例Dynamic clusters解析——动态的聚合图层

官网demo地址:

https://openlayers.org/en/latest/examples/clusters-dynamic.html

这篇绘制了多个聚合图层。

先初始化地图 ,设置了地图视角的边界extent,限制了地图缩放的范围

 initMap() {
      const raster = new TileLayer({
        source: new XYZ({
          attributions:
            'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',
          url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",
        }),
      });

      this.map = new Map({
        layers: [raster],
        target: "map",
        view: new View({
          center: [0, 0],
          zoom: 2,
          maxZoom: 19,
          extent: [
            ...fromLonLat([16.1793, 48.1124]),
            ...fromLonLat([16.5559, 48.313]),
          ],
          showFullExtent: true,
        }),
      });
      this.map.on("pointermove", this.moveEvent);
      this.map.on("click", this.clickEvent);
    },

创建一个聚合数据源,数据是geoJson格式的。

    const vectorSource = new VectorSource({
        format: new GeoJSON(),
        url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",
      });

      const clusterSource = new Cluster({
        attributions:
          'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',
        distance: 35,
        source: vectorSource,
      });

 然后创建一个聚合图层


      //聚合图层
      this.clustersLayer = new VectorLayer({
        source: clusterSource,
        style: this.clusterStyle,
      });
      this.map.addLayer(this.clustersLayer);

因为每个feature的样式不一样,所以样式这里都绑定了函数。

这里的outerCircle定义为全局变量而并非局部变量,主要是因为clusterStyle函数是个高频触发函数,将outerCircle写成全局的可以不用new那么多次。使用样式组的方法绘制出来了外发光效果,其实就是画了两次圆形。

//黄色圆圈 内外两层 发光效果
    clusterStyle(feature) {
      const size = feature.get("features").length;
      //还有下级
      if (size > 1) {
        return [
          new Style({
            image: this.outerCircle,
          }),
          new Style({
            image: this.innerCircle,
            text: new Text({
              text: size.toString(),
              fill: this.textFill,
              stroke: this.textStroke,
            }),
          }),
        ];
      }
      //没有下级
      const originalFeature = feature.get("features")[0];
      return this.clusterMemberStyle(originalFeature);
    },
 this.textFill = new Fill({
        color: "#fff",
      });
      this.textStroke = new Stroke({
        color: "rgba(0, 0, 0, 0.6)",
        width: 3,
      });
      this.innerCircle = new CircleStyle({
        radius: 14,
        fill: new Fill({
          color: "rgba(255, 165, 0, 0.7)",
        }),
      });
      this.outerCircle = new CircleStyle({
        radius: 20,
        fill: new Fill({
          color: "rgba(255, 153, 102, 0.3)",
        }),
      });

有下级的时候图形是橙色发光的样式,没有下级的时候。根据feature的LEISTUNG字段显示为不同的icon图形。

clusterMemberStyle(clusterMember) {
      return new Style({
        geometry: clusterMember.getGeometry(),
        image:
          clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,
      });
    },
 this.darkIcon = new Icon({
     src: "data/icons/emoticon-cool.svg",
 });
 this.lightIcon = new Icon({
     src: "data/icons/emoticon-cool-outline.svg",
 });

 创建一个凸包图层。

凸包是包含给定点集的最小凸多边形,在许多计算几何应用中非常重要,如图形学、地理信息系统(GIS)和形状分析。计算凸包的算法通常基于点的排序和几何性质,可以有效地处理大规模的数据。 

下载引入monotone-chain-convex-hull

npm i monotone-chain-convex-hull
import monotoneChainConvexHull from "monotone-chain-convex-hull";
      //凸包图层样式
      this.convexHullFill = new Fill({
        color: "rgba(255, 153, 0, 0.4)",
      });
      this.convexHullStroke = new Stroke({
        color: "rgba(204, 85, 0, 1)",
        width: 1.5,
      });
      //凸包图层
      this.clusterHulls = new VectorLayer({
        source: clusterSource,
        style: this.clusterHullStyle,
      });
 clusterHullStyle(cluster) {
      if (cluster !== this.hoverFeature) {
        return null;
      }
      const originalFeatures = cluster.get("features");
      const points = originalFeatures.map((feature) =>
        feature.getGeometry().getCoordinates()
      );
      return new Style({
        geometry: new Polygon([monotoneChainConvexHull(points)]),
        fill: this.convexHullFill,
        stroke: this.convexHullStroke,
      });
    },

当鼠标移动到点图层时,显示凸包效果。 

  moveEvent(event) {
      this.clustersLayer.getFeatures(event.pixel).then((features) => {
        if (features[0] !== this.hoverFeature) {
          this.hoverFeature = features[0];
          this.clusterHulls.setStyle(this.clusterHullStyle);
          this.map.getTargetElement().style.cursor =
            this.hoverFeature && this.hoverFeature.get("features").length > 1
              ? "pointer"
              : "";
        }
      });
    },

然后是点线图层

this.clusterCircles = new VectorLayer({
        source: clusterSource,
        style: this.clusterCircleStyle,
      });

当前视图的缩放级别达到了最大缩放级别或者范围的宽度和高度都小于当前视图的分辨率      

往点线图层的style数组中添加两个样式。。

clusterCircleStyle(cluster, resolution) {
      if (cluster !== this.clickFeature || resolution !== this.clickResolution) {
        return null;
      }
      const clusterMembers = cluster.get("features");
      const centerCoordinates = cluster.getGeometry().getCoordinates();
      return this.generatePointsCircle( 
        clusterMembers.length,
        cluster.getGeometry().getCoordinates(),
        resolution
      ).reduce((styles, coordinates, i) => {
        const point = new Point(coordinates);
        const line = new LineString([centerCoordinates, coordinates]);
        styles.unshift(
          new Style({
            geometry: line,
            stroke: this.convexHullStroke,
          })
        );
        styles.push(
          this.clusterMemberStyle(
            new Feature({
              ...clusterMembers[i].getProperties(),
              geometry: point,
            })
          )
        );
        return styles;
      }, []);
    },

generatePointsCircle 方法根据聚类成员的数量、中心坐标和当前分辨率,生成一个圆周上的点坐标数组。 

generatePointsCircle(count, clusterCenter, resolution) {
      //计算圆周长度和每个点的角度步长
      const circumference =
        this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);
      let legLength = circumference / (Math.PI * 2); 
      const angleStep = (Math.PI * 2) / count;
      const res = [];
      let angle;
      //调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。
      legLength = Math.max(legLength, 35) * resolution; 
      //生成圆周上的点坐标
      for (let i = 0; i < count; ++i) {
        angle = this.circleStartAngle + i * angleStep;
        res.push([
          clusterCenter[0] + legLength * Math.cos(angle),
          clusterCenter[1] + legLength * Math.sin(angle),
        ]);
      }
      return res;
    },

 点击事件时,获取当前点击的feature的边界值,定位到指定位置。

clickEvent(event) {
      this.clustersLayer.getFeatures(event.pixel).then((features) => {
        if (features.length > 0) {
          const clusterMembers = features[0].get("features");
          if (clusterMembers.length > 1) {
            const extent = createEmpty();
            clusterMembers.forEach((feature) =>
              extend(extent, feature.getGeometry().getExtent())
            );
            const view = this.map.getView();
            const resolution = this.map.getView().getResolution();
            //如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率
            if (
              view.getZoom() === view.getMaxZoom() ||
              (getWidth(extent) < resolution && getHeight(extent) < resolution)
            ) {
              this.clickFeature = features[0];
              this.clickResolution = resolution;
              this.clusterCircles.setStyle(this.clusterCircleStyle);
            } else {
              view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });
            }
          }
        }
      });
    },

完整代码:

<template>
  <div class="box">
    <h1>Dynamic clusters</h1>
    <div id="map"></div>
  </div>
</template>

<script>
import Feature from "ol/Feature.js";
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import {
  Circle as CircleStyle,
  Fill,
  Icon,
  Stroke,
  Style,
  Text,
} from "ol/style.js";
import { Cluster, Vector as VectorSource, XYZ } from "ol/source.js";
import { LineString, Point, Polygon } from "ol/geom.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { createEmpty, extend, getHeight, getWidth } from "ol/extent.js";
import { fromLonLat } from "ol/proj.js";
// import convexHull from "convex-hull";
import monotoneChainConvexHull from "monotone-chain-convex-hull";
export default {
  name: "",
  components: {},
  data() {
    return {
      map: null,
      hoverFeature: null,
      convexHullFill: null,
      convexHullStroke: null,
      textFill: null,
      textStroke: null,
      innerCircle: null,
      outerCircle: null,
      darkIcon: null,
      lightIcon: null,
      circleDistanceMultiplier: 1,
      circleFootSeparation: 28,
      circleStartAngle: Math.PI / 2,
      clickFeature: null,
      clustersLayer: null,
      clusterHulls: null,
      clusterCircles: null,
      clickResolution:null,
    };
  },
  computed: {},
  created() {},
  mounted() {
    this.initMap();
    this.initStyle();
    this.addClusterLayers();
  },
  methods: {
    initMap() {
      const raster = new TileLayer({
        source: new XYZ({
          attributions:
            'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',
          url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",
        }),
      });

      this.map = new Map({
        layers: [raster],
        target: "map",
        view: new View({
          center: [0, 0],
          zoom: 2,
          maxZoom: 19,
          extent: [
            ...fromLonLat([16.1793, 48.1124]),
            ...fromLonLat([16.5559, 48.313]),
          ],
          showFullExtent: true,
        }),
      });
      this.map.on("pointermove", this.moveEvent);
      this.map.on("click", this.clickEvent);
    },
    addClusterLayers() {
      const vectorSource = new VectorSource({
        format: new GeoJSON(),
        url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",
      });

      const clusterSource = new Cluster({
        attributions:
          'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',
        distance: 35,
        source: vectorSource,
      });
      //凸包图层
      this.clusterHulls = new VectorLayer({
        source: clusterSource,
        style: this.clusterHullStyle,
      });
      //聚合图层
      this.clustersLayer = new VectorLayer({
        source: clusterSource,
        style: this.clusterStyle,
      });
      //特定情况下的图层
      this.clusterCircles = new VectorLayer({
        source: clusterSource,
        style: this.clusterCircleStyle,
      });
      this.map.addLayer(this.clusterHulls);
      this.map.addLayer(this.clustersLayer);
      this.map.addLayer(this.clusterCircles);
    },
    initStyle() {
      //凸包图层样式
      this.convexHullFill = new Fill({
        color: "rgba(255, 153, 0, 0.4)",
      });
      this.convexHullStroke = new Stroke({
        color: "rgba(204, 85, 0, 1)",
        width: 1.5,
      });

      this.textFill = new Fill({
        color: "#fff",
      });
      this.textStroke = new Stroke({
        color: "rgba(0, 0, 0, 0.6)",
        width: 3,
      });
      this.innerCircle = new CircleStyle({
        radius: 14,
        fill: new Fill({
          color: "rgba(255, 165, 0, 0.7)",
        }),
      });
      this.outerCircle = new CircleStyle({
        radius: 20,
        fill: new Fill({
          color: "rgba(255, 153, 102, 0.3)",
        }),
      });
      this.darkIcon = new Icon({
        src: "data/icons/emoticon-cool.svg",
      });
      this.lightIcon = new Icon({
        src: "data/icons/emoticon-cool-outline.svg",
      });
    },
    clusterMemberStyle(clusterMember) {
      return new Style({
        geometry: clusterMember.getGeometry(),
        image:
          clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,
      });
    },
    clusterCircleStyle(cluster, resolution) {
      if (cluster !== this.clickFeature || resolution !== this.clickResolution) {
        return null;
      }
      const clusterMembers = cluster.get("features");
      const centerCoordinates = cluster.getGeometry().getCoordinates();
      return this.generatePointsCircle( 
        clusterMembers.length,
        cluster.getGeometry().getCoordinates(),
        resolution
      ).reduce((styles, coordinates, i) => {
        const point = new Point(coordinates);
        const line = new LineString([centerCoordinates, coordinates]);
        styles.unshift(
          new Style({
            geometry: line,
            stroke: this.convexHullStroke,
          })
        );
        styles.push(
          this.clusterMemberStyle(
            new Feature({
              ...clusterMembers[i].getProperties(),
              geometry: point,
            })
          )
        );
        return styles;
      }, []);
    },
    generatePointsCircle(count, clusterCenter, resolution) {
      //计算圆周长度和每个点的角度步长
      const circumference =
        this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);
      let legLength = circumference / (Math.PI * 2); 
      const angleStep = (Math.PI * 2) / count;
      const res = [];
      let angle;
      //调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。
      legLength = Math.max(legLength, 35) * resolution; 
      //生成圆周上的点坐标
      for (let i = 0; i < count; ++i) {
        angle = this.circleStartAngle + i * angleStep;
        res.push([
          clusterCenter[0] + legLength * Math.cos(angle),
          clusterCenter[1] + legLength * Math.sin(angle),
        ]);
      }
      return res;
    },
    clusterHullStyle(cluster) {
      if (cluster !== this.hoverFeature) {
        return null;
      }
      const originalFeatures = cluster.get("features");
      const points = originalFeatures.map((feature) =>
        feature.getGeometry().getCoordinates()
      );
      return new Style({
        geometry: new Polygon([monotoneChainConvexHull(points)]),
        fill: this.convexHullFill,
        stroke: this.convexHullStroke,
      });
    },
    //黄色圆圈 内外两层 发光效果
    clusterStyle(feature) {
      const size = feature.get("features").length;
      //还有下级
      if (size > 1) {
        return [
          new Style({
            image: this.outerCircle,
          }),
          new Style({
            image: this.innerCircle,
            text: new Text({
              text: size.toString(),
              fill: this.textFill,
              stroke: this.textStroke,
            }),
          }),
        ];
      }
      //没有下级
      const originalFeature = feature.get("features")[0];
      return this.clusterMemberStyle(originalFeature);
    },
    moveEvent(event) {
      this.clustersLayer.getFeatures(event.pixel).then((features) => {
        if (features[0] !== this.hoverFeature) {
          this.hoverFeature = features[0];
          this.clusterHulls.setStyle(this.clusterHullStyle);
          this.map.getTargetElement().style.cursor =
            this.hoverFeature && this.hoverFeature.get("features").length > 1
              ? "pointer"
              : "";
        }
      });
    },
    clickEvent(event) {
      this.clustersLayer.getFeatures(event.pixel).then((features) => {
        if (features.length > 0) {
          const clusterMembers = features[0].get("features");
          if (clusterMembers.length > 1) {
            const extent = createEmpty();
            clusterMembers.forEach((feature) =>
              extend(extent, feature.getGeometry().getExtent())
            );
            const view = this.map.getView();
            const resolution = this.map.getView().getResolution();
            //如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率
            if (
              view.getZoom() === view.getMaxZoom() ||
              (getWidth(extent) < resolution && getHeight(extent) < resolution)
            ) {
              this.clickFeature = features[0];
              this.clickResolution = resolution;
              this.clusterCircles.setStyle(this.clusterCircleStyle);
            } else {
              view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });
            }
          }
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
#map {
  width: 100%;
  height: 500px;
}
.box {
  height: 100%;
}

#info {
  width: 100%;
  height: 24rem;
  overflow: scroll;
  display: flex;
  align-items: baseline;
  border: 1px solid black;
  justify-content: flex-start;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/675912.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

导入和使用标准模块

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中&#xff0c;自带了很多实用的模块&#xff0c;称为标准模块&#xff08;也可以称为标准库&#xff09;&#xff0c;对于标准模块&#xf…

韩顺平0基础学java——第15天

p303-326 重写override 和重载做个对比 注&#xff1a;但子类可以扩大范围&#xff0c;比如父类是protected&#xff0c;子类可以是public 多态 方法或对象具有多种形态&#xff0c;是面向对象的第三大特征&#xff0c;多态是建立在封装和继承基础之上的。 多态的具体体现…

Ubuntu server 24 (Linux) 安装部署smartdns 搭建智能DNS服务器

SmartDNS是推荐本地运行的DNS服务器&#xff0c;SmartDNS接受本地客户端的DNS查询请求&#xff0c;从多个上游DNS服务器获取DNS查询结果&#xff0c;并将访问速度最快的结果返回给客户端&#xff0c;提高网络访问速度和准确性。 支持指定域名IP地址&#xff0c;达到禁止过滤的效…

【YOLOv5/v7改进系列】引入ODConv——即插即用的卷积块

一、导言 提出了一种称为全维度动态卷积(ODConv)的新颖设计&#xff0c;旨在克服当前动态卷积方法的局限性并提升卷积神经网络(CNN)的性能。以下是该论文提出的全维度动态卷积设计的优点和存在的缺点分析&#xff1a; 优点&#xff1a; 增强特征学习能力&#xff1a; ODConv通…

第十五届蓝桥杯物联网试题(省赛)

这个省赛题不算难&#xff0c;中规中矩&#xff0c;记得看清A板B板&#xff0c;还有ADC的获取要配合定时器

如何查看本地sql server数据库的ip地址

程序连线SQL数据库&#xff0c;需要SQL Server实例的名称或网络地址。 1.查询语句 DECLARE ipAddress VARCHAR(100) SELECT ipAddress local_net_address FROM sys.dm_exec_connections WHERE SESSION_ID SPID SELECT ipAddress As [IP Address]SELECT CONNECTIONPROPERTY(…

p10,idea开发工具

1 什么是ide 有编写&#xff0c;编译&#xff0c;运行三个功能合一的集成开发工具。

创建模拟器

修改模拟器默认路径 由于模拟器文件比较大&#xff0c;默认路径在C:\Users\用户名.android\avd&#xff0c;可修改默认路径 创建修改后的路径文件 D:\A-software\Android\AVD添加系统变量ANDROID_SDK_HOME&#xff1a;D:\A-software\Android\AVD重启Android Studio 创建模拟…

云端数据提取:安全、高效地利用无限资源

在当今的大数据时代&#xff0c;企业和组织越来越依赖于云平台存储和处理海量数据。然而&#xff0c;随着数据的指数级增长&#xff0c;数据的安全性和高效的数据处理成为了企业最为关心的议题之一。本文将探讨云端数据安全的重要性&#xff0c;并提出一套既高效又安全的数据提…

24年西藏事业单位报名详细流程

✨各位姐妹们注意啦&#xff01;24西藏事业单位公告已出&#xff0c;本次计划公开招聘8⃣9⃣9⃣人即日起开始报名&#xff0c;想要上岸的姐妹们要抓紧了哦✊趁着还有时间赶紧开卷&#xff01;&#xff01;&#xff01; &#x1f308;24西藏事业单位招聘考试&#xff1a; &…

Linux 内核之 mmap 内存映射触发的缺页异常 Page Fault

文章目录 前言一、简介1. MMU 内存管理2. 缺页中断3. 页表4. 小节 二、mmap 提前分配物理内存1. mm_populate 函数2. __mm_populate 函数3. populate_vma_page_range 函数4. __get_user_pages 函数5. find_extend_vma 函数6. find_vma 函数7. follow_page_mask 函数8. follow_p…

微信小程序-wx.showToast超长文字展示不全

wx.showToast超长文字展示不全 问题解决方法1 问题 根据官方文档&#xff0c;iconnone&#xff0c;最多显示两行文字。所以如果提示信息较多&#xff0c;超过两行&#xff0c;就需要用其他方式解决。 解决方法1 使用vant组件里面的tost 根据官方例子使用&#xff1a; 1、在…

Java进阶学习笔记36——算法

什么是算法&#xff1f; 解决某个实际问题的过程和方法。 1&#xff09;导航&#xff1b; 2&#xff09;滴滴打车&#xff1b; 3&#xff09;抖音&#xff1b; 不同的算法&#xff0c;效率高、性能好&#xff01; 在Java中&#xff0c;代码已经帮我们写好了&#xff0c;但为…

端午佳节到,礼轻情意重,闪侠惠递帮你高效便宜寄快递

马上就是端午佳节了&#xff0c;我们通常会吃粽子&#xff0c;赛龙舟&#xff0c;但是这些礼物我们该怎么快速的送到我们亲朋好友的手中呢&#xff1f;小编这里非常推荐大家使用闪侠惠递来寄快递。不仅能高效便捷的把礼物送到你的手中&#xff0c;而且还能以非常便宜的价格呢&a…

GIS之arcgis系列06:线划图缓冲区分析

缓冲区工具将在输入要素周围指定距离内创建缓冲区面。 缓冲区例程将遍历输入要素的每个折点并创建缓冲区偏移。 通过这些偏移创建输出缓冲区要素 原理&#xff1a; 01.打开文件 02.确定单位&#xff0c;在文件属性里。 03.工具箱-->分析工具-->邻域分析-->缓冲区。 …

Java中常见错误-Java中注解是否可以继承

Java中注解是否可以继承 Inherited基本概念使用场景注意事项 实体类自定义注解测试方法运行结果使用Inherited不使用Inherited 结论 在解决这个问题之前需要先了解一下Inherited Inherited 基本概念 ​ Inherited是Java中的一个元注解&#xff0c;位于java.lang.annotation包…

如何卸载ollama

文章目录 一 概述二 卸载2.1 Windows平台卸载 ollama2.2 Linux 平台卸载 ollama2.3 Docker 平台卸载 ollama 参考链接 一 概述 本文档主要讲述 ollama 如何卸载&#xff0c;适用范围包括 Windows Linux 以及 Docker 等平台的安装方式。 二 卸载 2.1 Windows平台卸载 ollama …

【干货】SaaS增长|提高销售转化率,用这几个方法就对了!

一、什么是销售转化率 1. 定义 销售转化率是指将潜在客户转化为实际购买者的比率。它衡量了销售过程中的效率和效果&#xff0c;对于评估销售团队的表现和制定销售策略非常重要。 2. 计算公式 销售转化率 (实际购买客户数 / 潜在客户数) 100% 实际购买客户数&#xff1a;…

【python深度学习】——tensor内部存储结构|内存优化与as_strided|内存紧凑化contiguous

【python深度学习】——tensor内部存储结构|内存优化与as_strided|内存紧凑化contiguous 1. tensor的元数据&#xff08;metadata&#xff09;和存储区&#xff08;storage&#xff09;1.1 元数据&#xff08;metadata&#xff09;1.2 存储区&#xff08;Storage&#xff09;1.…

excel公式怎么完全复制到另一列?

例如&#xff1a;A1单元格的内容是E1F1&#xff1b;想要复制到B列&#xff0c;内容也是E1F1&#xff0c;而不是F1G1&#xff1b;怎么做呢&#xff1f; 如果公式复制粘贴到其它位置出现偏差&#xff0c;通常有这么两个原因&#xff1a; 一、公式中的相对引用或混合引起 这个情…