三十四、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>
相关文章:

三十四、openlayers官网示例Dynamic clusters解析——动态的聚合图层
官网demo地址: https://openlayers.org/en/latest/examples/clusters-dynamic.html 这篇绘制了多个聚合图层。 先初始化地图 ,设置了地图视角的边界extent,限制了地图缩放的范围 initMap() {const raster new TileLayer({source: new XYZ…...

SpringBoot登录认证--衔接SpringBoot案例通关版
文章目录 登录认证登录校验-概述登录校验 会话技术什么是会话呢?cookie Session令牌技术登录认证-登录校验-JWT令牌-介绍JWT SpringBoot案例通关版,上接这篇 登录认证 先讲解基本的登录功能 登录功能本质就是查询操作 那么查询完毕后返回一个Emp对象 如果Emp对象不为空,那…...

vue3状态管理,pinia的使用
状态管理 我们知道组件与组件之间可以传递信息,那么我们就可以将一个信息作为组件的独立状态(例如,单个组件的颜色)或者共有状态(例如,多个组件是否显示)在组件之传递,…...

入门到实践,手把手教你用AI绘画!
前言 一款无需魔法的PS插件!下载即用,自带提示词插件,无论你是小白还是大神都能轻松上手,无配置要求,win/mac通通能用! AI绘画工具——StartAI 官网:StartAI官网 (istarry.com.cn) 近段时间…...

大模型应用框架-LangChain
LangChain的介绍和入门 💥 什么是LangChain LangChain由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架,LLMs使用机器学习算法和海量数据来分析和理解自然语言,GPT3.5、GPT4是…...

探索Linux中的强大文本处理工具——sed命令
探索Linux中的强大文本处理工具——sed命令 在Linux系统中,文本处理是一项日常且重要的任务。sed命令作为一个流编辑器,以其强大的文本处理能力而著称。它允许我们在不修改原始文件的情况下,对输入流(文件或管道)进行…...

冯喜运:6.3黄金原油晚间最新行情及独家操作策略指导
【黄金消息面分析】:在全球经济的波动和不确定性中,黄金作为传统的避险资产,其价格走势和市场分析一直是投资者关注的焦点。本周一(北京时间6月3日),现货黄金价格基本持平,交易商正在等待本周公…...

Spark_SparkOnHive_海豚调度跑任务写入Hive表失败解决
背景 前段时间我在海豚上打包程序写hive出现了一个问题,spark程序向hive写数据时,报了如下bug, org.apache.spark.sql.AnalysisException: The format of the existing table test.xx is HiveFileFormat It doesnt match the specified for…...

SaaS 电商设计 (十一) 那些高并发电商系统的限流方案设计
目录 一.什么是限流二.怎么做限流呢2.1 有哪些常见的系统限流算法2.1.1 固定窗口2.1.1 滑动窗口2.1.2 令牌桶2.1.3 漏桶算法 2.2 常见的限流方式2.2.1 单机限流&集群限流2.2.2 前置限流&后置限流 2.3 实际落地是怎么做的2.3.1 流量链路2.3.2 各链路限流2.3.2.1 网关层2…...

【算法】MT2 棋子翻转
✨题目链接: MT2 棋子翻转 ✨题目描述 在 4x4 的棋盘上摆满了黑白棋子,黑白两色棋子的位置和数目随机,其中0代表白色,1代表黑色;左上角坐标为 (1,1) ,右下角坐标为 (4,4) 。 现在依次有一些翻转操作&#…...

头颈肿瘤在PET/CT中的分割:HECKTOR挑战赛| 文献速递-深度学习肿瘤自动分割
Title 题目 Head and neck tumor segmentation in PET/CT: The HECKTOR challenge 头颈肿瘤在PET/CT中的分割:HECKTOR挑战赛 01 文献速递介绍 高通量医学影像分析,常被称为放射组学,已显示出其在揭示定量影像生物标志物与癌症预后之间关…...

Kafka重平衡导致无限循环消费问题
1. 问题描述 Kafka消费者消费消息超过了5分钟,不停的触发重平衡,消费者的offset因为重平衡提交失败,重复拉取消费,重复消费。 2. 问题原因 kafka默认的消息消费超时时间max.poll.interval.ms 300000, 也就是5分钟,…...

执行shell脚本时为什么要写成./test.sh,而不是test.sh?
一定要写成 ./test.sh,而不是 test.sh 运行其它二进制的程序也一样! 直接写 test.sh,linux 系统会去 PATH (系统环境)里寻找有没有叫 test.sh 的! 而只有 /bin, /sbin, /usr/bin,/usr/sbin 这…...

【人工智能】第一部分:ChatGPT的基本概念和技术背景
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...

雪花算法详解及源码分析
雪花算法的简介: 雪花算法用来实现全局唯一ID的业务主键,解决分库分表之后主键的唯一性问题,所以就单从全局唯一性来说,其实有很多的解决方法,比如说UUID、数据库的全局表的自增ID 但是在实际的开发过程中࿰…...

Golang TCP网络编程
文章目录 网络编程介绍TCP网络编程服务器监听客户端连接服务器服务端获取连接向连接中写入数据从连接中读取数据关闭连接/监听器 简易的TCP回声服务器效果展示服务端处理逻辑客户端处理逻辑 网络编程介绍 网络编程介绍 网络编程是指通过计算机网络实现程序间通信的一种编程技术…...

先进制造aps专题十 aps项目成功指南
aps项目成功指南 为了保证aps项目的成功 现在国内的aps项目 一是看aps软件本身是不是实现了复杂的排程算法和优化算法,算法引擎使用c高性能编译语言开发,支持工序的复杂关系,考虑副资源约束和特殊规格约束,提供了能考虑各种约束…...

实现Dropdown下拉菜单监听键盘上下键选中功能-React
用过ant design的小伙伴都知道,select组件是支持联想搜索跟上下键选中的效果的,但是在项目中我们可能会遇到用select组件无法实现我们的需求的情况,比如说一个div框,里面有input,又有tag标签,在input中输入…...

Ubuntu系统升级k8s节点的node节点遇到的问题
从1.23版本升级到1.28版本 node节点的是Ubuntu系统20.04的版本 Q1 node节点版本1.23升级1.28失败 解决办法: # 改为阿里云镜像 vim /etc/apt/sources.list.d/kubernetes.list# 新增 deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main# 执…...

前端将DOM元素导出为图片
前端工作中经常会用到把一些元素导出,比如表格,正好项目有遇到导出为excel和导出为图片,就都封装实现了一下,以供其他需求的开发者使用: 1.导出为文档 这个说白了就是下载的功能,传过去检索参数ÿ…...

变现 5w+,一个被严重低估的 AI 蓝海赛道,居然用这个免费的AI绘画工具就能做!
大家好,我是画画的小强,致力于分享各类的 AI 工具,包括 AI 绘画工具、AI 视频工具、AI 写作工具等等。 但单纯地为了学而学,是没有任何意义的。 这些 AI 工具,学会了,用起来,才能发挥出他们的…...

Ubuntu server 24 (Linux) 安装部署smartdns 搭建智能DNS服务器
SmartDNS是推荐本地运行的DNS服务器,SmartDNS接受本地客户端的DNS查询请求,从多个上游DNS服务器获取DNS查询结果,并将访问速度最快的结果返回给客户端,提高网络访问速度和准确性。 支持指定域名IP地址,达到禁止过滤的效…...

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.5,6 SPI驱动实验-ICM20608 ADC采样值
前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…...

安装vllm的时候卡主:Collecting vllm-nccl-cu12<2.19,>=2.18 (from vllm)
按照vllm的时候卡主: ... Requirement already satisfied: typing-extensions in /home/wangguisen/miniconda3/lib/python3.10/site-packages (from vllm) (4.9.0) Requirement already satisfied: filelock>3.10.4 in /home/wangguisen/miniconda3/lib/python…...

O2O : Finetuning Offline World Models in the Real World
CoRL 2023 Oral paper code Intro 算法基于TD-MPC,利用离线数据训练世界模型,然后在线融合基于集成Q的不确定性估计实现Planning。得到的在线数据将联合离线数据共同训练目标策略。 Method TD-MPC TD-MPC由五部分构成: 状态特征提取 z h θ ( s ) …...

嵌入式学习(Day:31 网络编程2:TCP)
client, server browser b/s http p2p peer TCP的特征:1.有链接;2.可靠传输;3.流式套接字 1、模式 C/S 模式 》服务器/客户端模型(服务端1个,客户端很多个) server:socket()-->bind()---…...

正则表达式 0.1v
正则表达式 扩展 --> :% s/\///g //文件里面所有的 / 去掉 * 通配符 \ //转义,让字符变成原本的意思 ^ //行首 $ //行尾 [0-9] //数字 [a-z] //小写字母 [A-Z] //大写字母 把文件的小写字母替换为大写字母? 固定写法 :% s/[a-…...

免费的仓库出入库管理软件有哪些?
中小企业因为预算有限,所以希望能在出入库管理软件方面能够减少成本。 但我们必须清醒地认识到,所谓的“永久免费”往往只是一个幌子。这些软件要么是新上市的、功能尚未完善的产品,试图通过免费吸引用户试用;要么在数据安全和客…...

python 办公自动化-生成ppt文本和图
最终样式 代码实现 # 可编辑折线写入文字 成功 # 问题: 设置字体类型和加粗和字体为微软雅黑,是只改了字母和数字的字体,中文没变化 pip install pptx_ea_font 这个库可以解决这个问题 import pandas as pd import pptx_ea_font import mat…...

「动态规划」买卖股票的最佳时机
力扣原题链接,点击跳转。 给定一个整数数组prices,prices[i]表示股票在第i天的价格。你最多完成2笔交易。你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。设计一个算法计算最大利润。 我们用动态规划的思想来解决…...