java使用jsqlparser实现入参,并生成可执行sql

话不多说,直接上 验证通过的代码

第一个例子:

package jdbc;import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Database;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import org.apache.commons.lang3.StringUtils;public class GenSelectSqlBySqlParser {public static void main(String[] args) throws JSQLParserException {// 入参String database = "test"; // 数据库String table = "user"; // 表String[] fields = {"id", "name", "age", "count(id)", "avg(age)", "sum(a)", "calculate_sum(concat_ws(',', collect_list(id)))"}; // 字段//String[] functions = {"count(id)", "avg(age)", "sum(a)","calculate_sum(concat_ws(',', collect_list(id)))"}; // 函数(列)数组String[] fieldsAliases = {"", "", "", "c", "a", "s", "t"}; // 列的别名数组String where = "age > 20"; // where条件String groupBy = "name"; // group byString tableAlias = "temp";Select select = getSelectStr(database, table, fields, fieldsAliases, where, groupBy, tableAlias);// 输出sql语句System.out.println(select);}private static Select getSelectStr(String database, String table, String[] fields, String[] fieldsAliases, String where, String groupBy, String alias) throws JSQLParserException {// 创建一个select对象Select select = new Select();// 创建一个plainSelect对象,用于设置各种子句PlainSelect plainSelect = new PlainSelect();// 设置数据库Database db = new Database(database);db.setDatabaseName(database);// 设置表Table t = new Table();t.setDatabase(db);t.setName(table);t.setAlias(new Alias(alias)); // 给表设置别名// 设置字段for (int i = 0; i < fields.length; i++) {Column column = new Column();//column.setTable(t);column.setColumnName(fields[i]);// 解析函数表达式Expression expr = CCJSqlParserUtil.parseExpression(fields[i]);SelectExpressionItem item = new SelectExpressionItem();item.setExpression(expr);// 设置别名if(StringUtils.isNotEmpty(fieldsAliases[i])){item.setAlias(new Alias(fieldsAliases[i]));}plainSelect.addSelectItems(item);}plainSelect.setFromItem(t);// 设置where条件Expression expr = CCJSqlParserUtil.parseCondExpression(where);plainSelect.setWhere(expr);// 设置group byExpression groupByExpr = CCJSqlParserUtil.parseExpression(groupBy);plainSelect.addGroupByColumnReference(groupByExpr);// 将plainSelect设置为select的子句select.setSelectBody(plainSelect);return select;}
}

执行结果如下:

SELECT id, name, age, count(id) AS c, avg(age) AS a, sum(a) AS s, 
calculate_sum(concat_ws(',', collect_list(id))) AS t 
FROM test.user AS temp WHERE age > 20 GROUP BY name

第二个例子:

通过多个单条sql,生成关联sql,也就是 join on

package jdbc;import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;import java.util.*;
import java.util.stream.Collectors;public class c1 {public static void main(String[] args) throws JSQLParserException {// 输入
//        String express = "total_amount * average_amount / user_count + XXX1 - TTT2";
//        List<String> sqlList = new ArrayList<>();
//        // SQL 语句 A
//        String sqlA = "SELECT username, SUM(total_amount) as total_amount FROM hadoop_ind.user_amount GROUP BY username";
//        // SQL 语句 B
//        String sqlB = "SELECT receiver_name as username, avg(total_amount) as average_amount FROM hadoop_ind.`order` GROUP BY receiver_name";
//        // SQL 语句 C
//        String sqlC = "SELECT username, count(*) as user_count FROM hadoop_ind.`user` GROUP BY username";
//        // SQL 语句 C
//        String sqlD = "SELECT username, AVG(*) as XXX1 FROM hadoop_ind.`XXX` GROUP BY username";
//        // SQL 语句 C
//        String sqlE = "SELECT username, MAX(*) as TTT2 FROM hadoop_ind.`TTT` GROUP BY username";
//        sqlList.add(sqlA);
//        sqlList.add(sqlB);
//        sqlList.add(sqlC);
//        sqlList.add(sqlD);
//        sqlList.add(sqlE);List<String> sqlList = new ArrayList<>();//String express = " ( ( min(a)+max(b)) + (min(c)+max(d)) )*10";String express = "  (t1 + t2) *10";String sqlA = "select min(a)+max(b) as t1 from db1.table1";String sqlB = "select min(c)+max(d) as t2 from db2.table2";sqlList.add(sqlA);sqlList.add(sqlB);// 输出String result = combineSql(express, sqlList);System.out.println(result);}/*** 整体的拼接 复合指标的sql 逻辑** @param express 表达式* @param sqlList 衍生指标sql集合* @return 处理后的sql*/public static String combineSql(String express, List<String> sqlList) throws JSQLParserException {// 存储每个sql语句对应的别名HashMap<String, String> aliasMap = new HashMap<>();for (int i = 0; i < sqlList.size(); i++) {char c = (char) ('A' + i);aliasMap.put(String.valueOf(c), sqlList.get(i));}// 解析每个sql语句,获取其中的select items, from items, where items, group by items等Map<String, PlainSelect> plainSelectMap = getPlainSelectMap(aliasMap);// 构造子查询List<SubSelect> subSelectList = getSubSelects(aliasMap, plainSelectMap);// 构造父查询PlainSelect parentSelect = new PlainSelect();// 设置select itemsList<SelectItem> selectItems = new ArrayList<>();//这里处理非函数列的列名setColumnName(plainSelectMap, selectItems);//这里处理函数列的列名(目前只支持 一个函数计算的表达式)setFunctionName(express, aliasMap, selectItems, parentSelect);// 设置from item,只拿第一个sql 作为from ,后面的sql 作为 joinparentSelect.setFromItem(subSelectList.get(0));// 设置join itemsparentSelect.setJoins(getJoins(plainSelectMap, subSelectList));// 生成拼接后的字符串Select finalSelect = new Select();finalSelect.setSelectBody(parentSelect);return finalSelect.toString();}/*** join items*/private static List<Join> getJoins(Map<String, PlainSelect> plainSelectMap, List<SubSelect> subSelectList) {List<Join> joins = new ArrayList<>();for (int i = 1; i < subSelectList.size(); i++) {Join join = new Join();join.setRightItem(subSelectList.get(i));final Expression onExpression = getOnExpression(plainSelectMap, i);if (Objects.nonNull(onExpression)) {join.addOnExpression(onExpression);}joins.add(join);}return joins;}/*** 获取子查询*/private static List<SubSelect> getSubSelects(Map<String, String> aliasMap, Map<String, PlainSelect> plainSelectMap) {List<SubSelect> subSelectList = new ArrayList<>();for (String key : aliasMap.keySet()) {PlainSelect plainSelect = plainSelectMap.get(key);SubSelect subSelect = new SubSelect();subSelect.setAlias(new Alias(key));subSelect.setSelectBody(plainSelect);subSelectList.add(subSelect);}return subSelectList;}/*** 解析每个sql语句,获取其中的select items, from items, where items, group by items等*/private static Map<String, PlainSelect> getPlainSelectMap(Map<String, String> aliasMap) throws JSQLParserException {Map<String, PlainSelect> plainSelectMap = new HashMap<>();for (Map.Entry<String, String> entry : aliasMap.entrySet()) {String sql = entry.getValue(); // 获取键Select select = (Select) CCJSqlParserUtil.parse(sql);PlainSelect plainSelect = (PlainSelect) select.getSelectBody();plainSelectMap.put(entry.getKey(), plainSelect);}return plainSelectMap;}/*** 获取非函数的字段列* 这里去遍历每个sql里面的 非函数字段,* 取出来后,看是否有别名,没有别名就取原本的名称,* 然后进行分组,再比较是否一致** @param plainSelectMap 查询sql 和别名的映射map* @return 字段列*/public static List<String> getOnColumnName(Map<String, PlainSelect> plainSelectMap) {List<String> columnList = new ArrayList<>();String columnName;for (Map.Entry<String, PlainSelect> entry : plainSelectMap.entrySet()) {String key = entry.getKey(); // 获取键PlainSelect plainSelect = entry.getValue(); // 获取值List<SelectItem> subSelectItems = plainSelect.getSelectItems();for (SelectItem selectItem : subSelectItems) {if (selectItem instanceof SelectExpressionItem) {Expression expression = ((SelectExpressionItem) selectItem).getExpression();if (expression instanceof Column) {final Alias alias = ((SelectExpressionItem) selectItem).getAlias();String aliasName = alias == null ? selectItem.toString() : alias.getName();columnName = key + "." + aliasName;columnList.add(columnName);}}}}return columnList;}/*** 这里处理非函数列的列名** @param plainSelectMap 查询sql 和别名的映射map*/private static void setColumnName(Map<String, PlainSelect> plainSelectMap,List<SelectItem> selectItems) {List<String> columnList = getOnColumnName(plainSelectMap);if (CollectionUtils.isEmpty(columnList)) {return;}int i = 1;// 这里取出 去匹配各个表的 column name 是否一致 ,一致就取出第一个final List<String> stringList = compareGroups(columnList);for (String column : stringList) {//然后去设置 父查询的 select 的非函数字段SelectExpressionItem selectExpressionItem0 = new SelectExpressionItem();selectExpressionItem0.setExpression(new Column(column));selectExpressionItem0.setAlias(new Alias(String.format("column%d", i++)));selectItems.add(selectExpressionItem0);}}/*** 这里拼接 函数的名称** @param express  String express = "total_amount-average_amount+user_count";* @param aliasMap A ,B,C*                 parts数组:A.total_amount,B.average_amount,C.user_count* @return 期望结果: A.total_amount-B.average_amount+C.user_count*/private static String getFunctionName(String express, HashMap<String, String> aliasMap) {// 分割表达式String[] parts = express.split("(?=[\\+\\-\\*\\/\\(\\)])|(?<=[\\+\\-\\*\\/\\(\\)])", -1);// 创建字符串缓冲区StringBuilder sb = new StringBuilder();// 遍历字符串数组for (String part : parts) {// 判断是否是一个变量part = part.trim();if (part.matches("[a-zA-Z][a-zA-Z0-9]*")) {// 查找对应的valuefor (String key : aliasMap.keySet()) {if (aliasMap.get(key).contains(part)) {String value = aliasMap.get(key);if (value != null) {// 追加替换后的内容sb.append(key).append(".").append(part);} else {// 追加原变量名sb.append(part);}break;}}} else {// 追加运算符或括号sb.append(part);}}// 返回结果return sb.toString();}/*** 设置 函数,拼接最后的名称*/private static void setFunctionName(String express,HashMap<String, String> aliasMap,List<SelectItem> selectItems,PlainSelect parentSelect) throws JSQLParserException {String finalResult = getFunctionName(express, aliasMap);SelectExpressionItem selectExpressionItem = new SelectExpressionItem();assert finalResult != null;selectExpressionItem.setExpression(CCJSqlParserUtil.parseExpression(finalResult));selectExpressionItem.setAlias(new Alias("result"));selectItems.add(selectExpressionItem);parentSelect.setSelectItems(selectItems);}/*** 用于拼接字段名称,根据分组进行比对* 比如三个sql语句,A有 username,有id,B和C都有,那么就是输出 [A.id, A.username]* 比如三个sql语句,A有 username,B和C都有,那么就是输出 [A.username]** @param dataList 数据集合 [A.username, A.id, B.username, B.id, C.username, C.id]* @return 返回的期望结果*/private static List<String> compareGroups(List<String> dataList) {Map<String, List<String>> categorizedData = dataList.stream().map(item -> item.split("\\.")).filter(parts -> parts.length == 2).map(parts -> new AbstractMap.SimpleEntry<>(parts[0], parts[1])).collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));//假设你的Map叫做categorizedData//创建一个Set来存储相同的值Set<String> commonValues = new HashSet<>();for (String value : categorizedData.values().iterator().next()) {//假设这个值是在所有分组中都存在的boolean isCommon = true;//遍历Map的其他分组for (List<String> list : categorizedData.values()) {//如果这个分组不包含这个值,说明它不是公共的,跳出循环if (!list.contains(value)) {isCommon = false;break;}}//如果这个值是公共的,把它加入到Set中if (isCommon) {//遍历Map的第一个分组的值Iterator<String> iterator = categorizedData.keySet().iterator();commonValues.add(iterator.next() + "." + value);}}//输出Set中的所有元素return new ArrayList<>(commonValues);}/*** 获取on条件* 这里假设每个sql语句都有一个相同的列名作为连接条件,你可以根据你的需求修改这个方法** @param plainSelectMap 查询sql 和别名的映射map* @param index          索引* @return on条件的内容*/public static Expression getOnExpression(Map<String, PlainSelect> plainSelectMap, int index) {// 抽取出两个常量,避免重复计算final String key1 = String.valueOf((char) ('A' + index - 1));final String key2 = String.valueOf((char) ('A' + index));// 获取两个 PlainSelect 对象PlainSelect plainSelect1 = plainSelectMap.get(key1);PlainSelect plainSelect2 = plainSelectMap.get(key2);// 获取两个 SelectItem 列表List<SelectItem> selectItems1 = plainSelect1.getSelectItems();List<SelectItem> selectItems2 = plainSelect2.getSelectItems();// 获取两个列名String columnName1 = getOnColumnName(selectItems1);String columnName2 = getOnColumnName(selectItems2);// 如果列名为空,抛出异常if (!Objects.equals(columnName1, columnName2)) {throw new RuntimeException("No common column name found");}if (StringUtils.isEmpty(columnName1)) {//如果为空,则获取 group by的 字段columnName1 = getOnGroupColumnName(plainSelect1);}if (StringUtils.isEmpty(columnName2)) {//如果为空,则获取 group by的 字段columnName2 = getOnGroupColumnName(plainSelect2);}// 如果 表中没有 group by 字段 那么就不给 on条件即可if (StringUtils.isEmpty(columnName1) || StringUtils.isEmpty(columnName2)) {return null;}// 创建两个 Column 对象Column column1 = new Column(key1 + "." + columnName1);Column column2 = new Column(key2 + "." + columnName2);// 返回一个 EqualsTo 对象return new EqualsTo(column1, column2);}// 抽取出一个辅助方法,用来从 SelectItem 列表中获取列名private static String getOnColumnName(List<SelectItem> selectItems) {// 遍历 SelectItem 列表for (SelectItem selectItem : selectItems) {// 如果是 SelectExpressionItem 类型的对象if (selectItem instanceof SelectExpressionItem) {// 获取表达式和别名Expression expression = ((SelectExpressionItem) selectItem).getExpression();Alias alias = ((SelectExpressionItem) selectItem).getAlias();// 如果表达式是 Column 类型的对象if (expression instanceof Column) {// 获取列名和别名String name = ((Column) expression).getColumnName();return alias == null ? name : alias.getName();}}}// 如果没有找到匹配的列名或别名,返回 nullreturn null;}private static String getOnGroupColumnName(PlainSelect plainSelect) {GroupByElement groupByElement = plainSelect.getGroupBy();if (Objects.isNull(groupByElement)) {return "";}final ExpressionList groupByExpressionList = groupByElement.getGroupByExpressionList();final List<Expression> expressions = groupByExpressionList.getExpressions();// 遍历 SelectItem 列表StringBuilder name = new StringBuilder();for (Expression s : expressions) {name.append(((Column) s).getColumnName()).append(",");}name.deleteCharAt(name.length() - 1);return name.toString();}}

执行结果:

SELECT(A.t1 + B.t2) * 10 AS result
FROM(SELECTmin(a) + max(b) AS t1FROMdb1.table1GROUP BYname) AS A
JOIN (SELECTmin(c) + max(d) AS t2FROMdb2.table2GROUP BYname) AS B 
ON  A.name = B.name

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zabbix监控系统

目录 一、zabbix简介 1.1 zabbix 是什么&#xff1f; 1.2 zabbix 监控原理 二、安装zabbix 5.0 2.1 部署 zabbix 服务端 2.2 部署 zabbix 客户端 2.3 自定义监控内容 作为一个云计算行业从业人员&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff…

深度学习知识回顾

1*1卷积的作用&#xff1a; 1x1卷积&#xff08;1x1 convolution&#xff09;在卷积神经网络&#xff08;CNN&#xff09;中起着多种重要的作用。尽管它的卷积核尺寸很小&#xff0c;但它可以在网络中引入以下功能&#xff1a; 1.降维和增加通道&#xff1a;1x1卷积可以用于减…

实力认证!TDengine 入选 Gartner 中国数据分析与人工智能技术成熟度曲线

近日&#xff0c;国际权威研究机构 Gartner 发布了《2023 年中国数据分析及人工智能技术成熟度曲线》&#xff08;即《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff09;报告&#xff0c;TDengine 成功入选实时数据管理领域代表产品。 作为评估全球新技术成…

[QT编程系列-39]:用户界面UI - 样式表QSS与样式文件快速入门

目录 1. 概述 2. CSS 3. QSS示例 4. QT样式表文件使用方法与步骤 5. QT内置样式 6. QT常见基本样式 1. 概述 Qt提供了一种称为Qt样式表&#xff08;Qt Style Sheets&#xff09;的机制&#xff0c;可以用于自定义和美化Qt应用程序的用户界面&#xff08;UI&#xff09;。…

Android 13 Hotseat定制化修改——002 hotseat图标数量修改

目录 一.背景 二.实践方案 一.背景 由于需求是需要自定义修改Hotseat&#xff0c;所以此篇文章是记录如何自定义修改hotseat的&#xff0c;应该可以覆盖大部分场景&#xff0c;修改点有修改hotseat布局方向&#xff0c;hotseat图标数量&#xff0c;hotseat图标大小&#xff0…

【MySQL安装】卸载与安装MySQL 5.7.X版本

最近由于各种原因&#xff0c;需要重新安装MySQL。之前我的版本是8.0版本&#xff0c;现在装的5.7版本。记录一下自己的安装过程。 目录 1、卸载MySQL8.0 2、安装MySQL5.7 1、卸载MySQL8.0 如何彻底卸载MySQL_mysql 完全卸载_m0小麦麦的博客-CSDN博客相信不少小伙伴们在安装…

EasyPoi导出 导入(带校验)简单示例 EasyExcel

官方文档 : http://doc.wupaas.com/docs/easypoi pom的引入: <!-- easyPoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version></dep…

Linux中无法忘记mysql密码处理办法

找到/etc/my.cnf或者/etc/mysql/my.cnf文件 添加下面两行代码&#xff0c;取消密码验证 [mysqld] skip-grant-table使用命令登录&#xff1a;mysql -u root -p&#xff0c;回车&#xff0c;回车使用sql语句来修改密码 mysql>use mysql; mysql>update user set password…

K-01BFS(2023河南萌新联赛第(五)场:郑州轻工业大学)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 思路&#xff1a; 直接枚举这个图中的拐点 这个拐点是经过左右平移到上下平移或者上下平移到左右平移 假设这个点事左到右后然后再从下到上 左到右就相当于走了个最长上升子序列&#xff0…

Java基础入门篇——数组初识

一、数组 1.假设某公司有100个员工&#xff0c;需要统计某公司员工的工资情况&#xff0c;首先需要声明100个变量来分别记每个员工的工资&#xff0c;那么如果按照之前的做法&#xff0c;可能定义的结构如下所示&#xff1a; int a1,a2,a3,......a100; 要求你输出这100个员工…

JavaWeb学习|JSTL表达式

1.什么是JSTL表达式&#xff1f; JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签&#xff0c;可以供我们使用&#xff0c;标签的功能和Java代码一样 在JSP页面文件中使用JSTL表达式&#xff0c;首先需要引入核心标签库 同时&#xff0c;也要把JSTL的jar包在to…

C指针:程序员的神奇箭头,穿越内存的冒险之旅!

目录 &#x1f575;️‍♂️ 引言&#xff1a;指针&#xff0c;那些指向星星的小箭头&#xff01; 一、&#x1f3af; 探索箭头&#xff1a;指针的基础知识 1.1 指针是什么&#xff1f; 1.2 解引用操作符&#xff1a;* 是关键 1.3 指针的比较和运算 1.4 空指针&#xff1a…

深度使用苹果M1 Mac电脑一个月后的发现与问题解决

自从苹果推出M1芯片的Mac电脑后&#xff0c;其强大的性能和高效的能耗管理引起了广泛关注。许多人纷纷购买了这款新一代的Mac电脑&#xff0c;并深度使用了一个月。然而&#xff0c;在长时间使用的过程中&#xff0c;一些问题也逐渐浮现出来。本文将分享在深度使用苹果M1 Mac电…

进程的调度

文章目录 一、进程的调度过程二、进程调度的一些特点 一个正在执行的程序叫做进程。操作系统会把程序调度到CPU上&#xff0c;让CPU执行程序&#xff0c;此时这个程序就变成了进程。相当于程序在CPU上执行时叫进程&#xff0c;程序没有在CPU上执行时就叫程序。 一、进程的调度过…

ucharts使用

官方地址&#xff1a; https://www.ucharts.cn/v2/#/tool/index 码云地址&#xff1a;码云地址 他官方文档有些地方写的比较模糊&#xff0c;有的还需要付费 地图 详情参见&#xff1a;

初始C语言——详细讲解操作符以及操作符的易错点

系列文章目录 第一章 “C“浒传——初识C语言&#xff08;更适合初学者体质哦&#xff01;&#xff09; 第二章 详细认识分支语句和循环语句以及他们的易错点 第三章 初阶C语言——特别详细地介绍函数 第四章 初始C语言——详细地讲解数组的内容以及易错点 第五章 初始C语言—…

HTML页面生命周期详解

前言 在使用vue的时代本次项目有个需求&#xff0c;就是需要在静态资源未加载时获取当前页面的访问路径&#xff0c;所以需要对html页面的生命周期要有所了解。本次讲讲HTML页面的生命周期事件&#xff0c;希望对大家有所帮助。如果文中有不对、疑惑的地方&#xff0c;欢迎在评…

20230809在WIN10下使用python3批量将TXT文件转换为SRT文件

20230809在WIN10下使用python3批量将TXT文件转换为SRT文件 2023/8/9 17:30 由于喜欢看纪录片等外文视频&#xff0c;通过剪映/PR2023/AUTOSUB识别字幕之后&#xff0c;可以通过google翻译识别为简体中文的DOCX文档。 DOCX文档转换为TXT文档之后&#xff0c;还需要转换为SRT文档…

CentOS7安装MySQL8(RPM方式)

第一步&#xff1a;解压 tar -xvf mysql-8.0.34-1.el7.x86_64.rpm-bundle.tar -C /usr/local/java/mysql 第二步&#xff1a;按顺序安装rpm包 # rpm -ivh mysql-community-common-8.0.34-1.el7.x86_64.rpm# rpm -ivh mysql-community-client-plugins-8.0.34-1.el7.x86_64.rpm…

【PythonRS】植被显示增强(多光谱、正射、照片等)

很多时候我们需要某个区域的正射图&#xff0c;虽然正射图一般都运用了匀色的算法&#xff0c;整体色彩比较均衡。但如果研究区内有大量的植被&#xff0c;这个时候植被突出显示就很有必要了。所以今天给大家分享一下使用Python对多光谱、正射影像进行植被显示增强的算法。 一、…