MySQL数据库之DJBC——day30 事务(转账问题),连接池(四种连接池)

本文详细介绍了JDBC的高级应用,包括批处理、调用存储过程与函数、事务处理以及如何获取自增长键值。此外,深入探讨了多种数据库连接池的使用,如DBCP、C3P0、Druid,并展示了它们的硬编码和配置文件编码方式。

这是数据库中学习的最清楚的一个小章节了,前面的内容还没有补上,在全程班开始的这几天的空挡争取补上,加油,大家都有了自己的目标,学的太晚,加油
批处理

批处理

插入大量的数据的时候建议用批处理
statement.addBatch();//添加批处理,先将数据进行缓存
statement.executeBatch();//执行批处理
statement.clearBatch();//清空缓存

JDBC调用存储过程和函数的自定义

调用存储过程 {call <procedure-name>[(<arg1>,<arg2>, ...)]}
Connection conn = JDBCUtils.getConnection();
String sql="{call testPro(?,?)}"; //输入或输出参数用?占位
 CallableStatement prepareCall = conn.prepareCall(sql);
  //给输入参数设置值
 prepareCall.setInt(1,7369);
  //如果有输出参数我们需要注册输出参数
prepareCall.registerOutParameter(2, Types.INTEGER);
 boolean b = prepareCall.execute();
 //获取输出结果
 int r = prepareCall.getInt(2);
System.out.println(r);
//释放资源
JDBCUtils.close(conn,prepareCall);
调用自定义函数 {?=call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
String sql="{?=call testFun(?)}";
 CallableStatement callableStatement = conn.prepareCall(sql);
  //设置输入参数
callableStatement.setInt(2,7902);
 //注册返回值
callableStatement.registerOutParameter(1, Types.INTEGER);
 callableStatement.execute();
//获取返回的结果
 int r = callableStatement.getInt(1);
System.out.println("结果是:"+r);
 //释放资源
JDBCUtils.close(conn,callableStatement);

获取自增长的键值

要获取自增长键的值,需要在获取操作对象的时候声明一个参数
Statement.RETURN_GENERATED_KEYS
 PreparedStatement preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
  2.当数据插入成功后,就可以取出这个自增长键的值
   //获取自增长键的结果集
            ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
            while (generatedKeys.next()){
                keyValue = generatedKeys.getInt(1);
            }
            
 3.你在其他表中就可以使用这个自增长键的值           

事务

概念:

在一个事务中有多个逻辑单元组成,这些逻辑单元是一个整体,不可分割,要么都成功,要么都失败

事务的特性:
  • 原子性:不可分割,要么都发生,要么都不发生
  • 一致性:在同一个事务中同步进行,从一个一致性状态到另一个一致性状态
  • 持久性:只要进行提交或回滚就不能更改
  • 隔离性:多并发都需要隔离,数据库对每一个用户开启的事务不能被其他的事务进行干扰

案例一:
银行系统,张三给李四进行转钱
解决钱不翼而飞的情况

package org.westos.demo1shiwu;

import org.westos.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @Description:进行转账,出现异常的解决
 * @Author:@李小白
 * @Date:2019/8/29 10:47
 *
 * 如果发生了这种钱不翼而飞的异常,我们应该意识到,我们应该把发钱收钱存到一个事务当中。
 * 解决:
 * 进行手动提交
 * 如果发生异常,我们回滚事务
 * 不管有没有遇见问题我们都要进行正常提交
 * 首先,我们不要抛出异常进行手动抓取,发现关闭资源引用不到向上提取
 * 然后我们再进行测试
 *
 */
public class JDBCDemo1 {
    public static void main(String[] args) {
        //张三给李四转钱
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        try {
            //进行连接数据库
            conn = JDBCUtils.getConnection();//默认是自动提交
            //进行手动提交
            conn.setAutoCommit(false);//设置成手动提交
            //张三少钱
            String sql1="update bank set money=money-1000 where username='zhangsan'";
           statement1 = conn.prepareStatement(sql1);
            //执行
            statement1.executeUpdate();
            //制造错误
            //System.out.println(1/0);
            //李四多钱
            String sql2="update bank set money=money+1000 where username='lisi'";
           statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();

        } catch (Exception e) {
            //如果遇见问题,我们就进行回滚
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //不管有无异常我们都进行提交
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //释放资源
            try {
                conn.close();
                statement1.close();
                statement2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

案例二
银行系统转账,制造回滚点

package org.westos.demo1shiwu;

import org.westos.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

/**
 * @Description:进行转账,设置回滚点
 * @Author:@李小白
 * @Date:2019/8/29 10:47
 *
 * 分别两次进行转账
 * 第一次转账1000,第二次转账500,分别制造异常,发现只要有一个异常我们都转不过去
 * 第一次没有异常第二次遇到异常
 * 解决
 * 制造回滚点
 * 只要出现异常就回到回滚点
 *
 */
public class JDBCDemo2 {
    public static void main(String[] args) {
        //张三给李四转钱
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        PreparedStatement statement3=null;
        PreparedStatement statement4=null;
        Savepoint savepoint=null;
        try {
            //进行连接数据库
            conn = JDBCUtils.getConnection();
            //进行手动提交
            conn.setAutoCommit(false);

            //第一次转账

            //张三少钱
            String sql1="update bank set money=money-1000 where username='zhangsan'";
           statement1 = conn.prepareStatement(sql1);
            //执行
            statement1.executeUpdate();
            //制造异常
           // System.out.println(1/0);
            //李四多钱
            String sql2="update bank set money=money+1000 where username='lisi'";
           statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();

            //第二次转账
             savepoint = conn.setSavepoint();

            //张三少钱
            String sql3="update bank set money=money-500 where username='zhangsan'";
            statement3 = conn.prepareStatement(sql3);
            //执行
            statement3.executeUpdate();
            //制造异常
             System.out.println(1/0);
            //李四多钱
            String sql4="update bank set money=money+500 where username='lisi'";
            statement4 = conn.prepareStatement(sql4);
            statement4.executeUpdate();
        } catch (Exception e) {
            //如果遇见问题,我们就进行回滚
            try {
//                conn.rollback();//回滚事件
                conn.rollback(savepoint);//回滚到指定的回滚点
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //不管有无异常我们都进行提交
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //释放资源
            try {
                conn.close();
                statement1.close();
                statement2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

事务的隔离级别
如果不考虑隔离的话出现的读问题;
  • 脏读:在一个事务中读取到另一个事务没有提交的数据
  • 不可重复度:在一个事物中,每次读取的数据都不一样(针对update)
  • 虚读(幻读):在一个事物中两次查询的结果不一致(针对insert)无法演示,MySQL已经默认避免了
MySQL的四种隔离级别

read uncommitted 读未提交 上面的三种情况都会出现
read committed 读已提交 可以避免脏读的情况 Oracle默认级别
repeatable read 可重复度 可避免脏读和不可重复读 MySQL默认级别
serializable 串行化 可以避免所有的问题,,但是效率最低

演示:脏读的发生
zhangsan 给lisi 转钱

开启两个窗口进行演示,一个转钱一个收钱
mysql  -uroot    -p123456//登录数据库

可以设置数据库的隔离级别,四种隔离级别分别设置
set session transaction isolation level read  uncommitted;

开启事务 start transaction;
修改数据:udate   bank set money=1500 where username='lisi'


第一种隔离级别
让另一个窗口也开启事务,进行查找数据,查到了就是脏读
zhangsan 一提交commit;  lisi又查不到了

第二种隔离级别:
lisi每次查看的钱数都不一样

第三种隔离级别:
lisi需要把卡退出来在进行查询,才能看到

第四种隔离级别:
lisi需要提交再进行查询,才能看到


java中控制隔离级别:(了解)
		Connection的api
			void setTransactionIsolation(int level) 
				level是常量

常见连接池的使用

为什么会有连接池?
由于建立数据库资源是一种非常耗时耗资源的行为,在连接池中放一些连接,放在内存中,需要的时候在从连接池中直接申请,借完要还

DBCP连接池

DataBase Connection Poll数据库连接池
是Apache开发,通过连接池可以自动管理数据库的释放和断开

  • 使用步骤
    1.配置数据库一个JAR包和DBCP的两个JAR包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
    2.使用api
    a.配置文件编码
    a.硬编码(不推荐)

案例:
转账的硬编码和配置文件编码
BasicDataSource bs = new BasicDataSource();

package org.westos.demo2lianjiechi;

import org.apache.commons.dbcp.BasicDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:常见的两种连接池之DBC的硬编码方式
 * @Author:@李小白
 * @Date:2019/8/30 16:36
 * DBCP的硬编码的方式首先导入JAR包
 * 然后就是创建数据源,导入Driver的名字和数据库的驱动和用户名和密码
 * 然后进行连接数据库,遍历结果集和关闭资源
 *
 */
public class DBCPDemo1 {

    public static void main(String[] args) throws SQLException {
        //依赖DBCP的两个jar包
        //创建数据源
        BasicDataSource bs = new BasicDataSource();
        bs.setDriverClassName("com.mysql.jdbc.Driver");
        bs.setUrl("jdbc:mysql://localhost:3306/mydb");
        bs.setUsername("root");
        bs.setPassword("123456");
        bs.setMaxWait(1000);
        //进行连接
        Connection conn= bs.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();
        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }
        //释放资源
        conn.close();
        bs.close();
        statement.close();
        resultSet.close();
    }
}

配置文件编码

package org.westos.demo2lianjiechi;




import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileReader;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

/**
 * @Description:DBPC文件配置
 * @Author:@李小白
 * @Date:2019/8/30 18:11
 */
public class DBPCDemo2 {
    public static void main(String[] args) throws Exception {
        //把JDBC配置文件进行导入,和MySQL的驱动进行导入
        //建立工厂

        Properties properties = new Properties();
        properties.load(new FileReader("src/dbcp.properties"));
        DataSource dataSource = new BasicDataSourceFactory().createDataSource(properties);

        //与数据库进行连接
        Connection conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();

        //进行遍历
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //关闭资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

C3P0

是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。

  • 使步骤
    1.导入jar包(c3p0-0.9.1.2.jar)
    2.使用api
    案例:
    硬编码和配置文件编码
    ComboPooledDataSource source = new ComboPooledDataSource();
package org.westos.demo2lianjiechi;


import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:C3P0硬编代码
 * @Author:@李小白
 * @Date:2019/8/30 18:25
 */
public class C3P0Demo1 {


    public static void main(String[] args) throws PropertyVetoException, SQLException {
        //把数据库和C3P0的jar包进导入
        //创建数据源
        ComboPooledDataSource source = new ComboPooledDataSource();
        source.setDriverClass("com.mysql.jdbc.Driver");
        source.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");//查询的库名字
        source.setUser("root");
        source.setPassword("123456");

        //和数据库进行连接
        Connection conn = source.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user ");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //关闭数据流
        conn.close();
        statement.close();
        resultSet.close();

    }
}

配置文件编码

要求1:配置文件的名称:c3p0.properties 或者 c3p0-config.xml
要求2:配置文件的路径:src下编码只需要一句话

new ComboPooledDataSource()//使用默认的配置
//可以切换xml配置文件中的标签 来加载不同的配置比如更换Orecal的数据库的配置
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置

package org.westos.demo2lianjiechi;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:C3p0进行文件配置
 * @Author:@李小白
 * @Date:2019/8/30 18:45
 */
public class c3p0Demo2 {
    public static void main(String[] args) throws SQLException {
        //导入C3P0的配置文件jar包和MySQL的驱动JAR包

        //方法一:完成了
        //方法二:如果使用xml文件的第二配置,就把xml文件第二配置的名称传过来,就完成了
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("MyConfig");

        //与数据库进行连接
        Connection conn = comboPooledDataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select  * from USER ");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

DBUtils

Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,
使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。

使用步骤:
1.导入jar包(commons-dbutils-1.4.jar)
2.创建一个queryrunner类
queryrunner作用:操作sql语句
构造方法:
new QueryRunner(Datasource ds);
3.编写sql
4.执行sql
query(…):执行r操作
update(…):执行cud操作

	使用步骤
		// 创建对象
		QueryRunner runner = new QueryRunner(new ComboPooledDataSource());
		// 定义sql
		String sql = "insert into bank(username,money) values(?,?)";
		// 执行sql
		runner.update(sql,"赵六",500);
		....
	执行查询后返回的结果集
	ResultSetHandler:封装结果集 接口
	BeanListHandler, 将查询结果的每一条记录封装成指定的bean对象,将每一个bean对象放入list中 返回.

案例

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;

import javax.sql.DataSource;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @Description:DBUtils:简化JDBC封装的开源工具
 * @Author:@李小白
 * @Date:2019/8/30 20:38
 */
public class DuridDBUtilsDemo1 {
    public static void main(String[] args) throws Exception {
        //导入DBUtils的JAR包,和数据库的JAR包
        //创建德鲁伊的工厂类
        Properties properties = new Properties();
        properties.load(new FileReader("src/druid.properties"));
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //创建QueryRunner 对象
        //传入数据源
        QueryRunner queryRunner = new QueryRunner(ds);
        //DML DQL返回值,你影响的行数
        int i = queryRunner.update("insert into user values(?,?)", 4, "李强娜");
        if (i>0) {
            System.out.println("插入成功");
        }else {
            System.out.println("插入失败");
        }
        //把你要查询的数据封装起来,//BeanListHandler,把数据库查出来的多条数据用对象封装气啦,再把对象放到集合里面
        List<User> query = queryRunner.query("select * from user", new BeanListHandler<User>(User.class));
        System.out.println(query);
        //查询一条结果把结果放到对象里面 BeanHandler
        User query1 = queryRunner.query("select * from user ", new BeanHandler<User>(User.class));
        System.out.println(query1);
        //MapHandler () 把查询的结果放到Map集合里面
        Map<String, Object> map = queryRunner.query("select * from user ", new MapHandler());
        System.out.println(map);

        Object id = map.get("id");
        System.out.println(id);
        Object username = map.get("username");
        System.out.println(username);

        System.out.println(map.size());

    }
}

package org.westos.demo2lianjiechi;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/8/30 21:00
 */
public class User {
    private  int id;
    private String name;

    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

德鲁伊连接池

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:连接池里面的德鲁伊连接池(DRUID),硬编码的方式
 * @Author:@李小白
 * @Date:2019/8/30 19:12
 */
public class DruidDemo1 {
    public static void main(String[] args) throws SQLException {
        //德鲁伊连接池和DBCP连接池真的很像
        //导入德鲁伊连接池的JAR包和MySQL的jar包
        //创建数据源
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///mydb");
        ds.setUsername("root");
        ds.setPassword("123456");
        //和数据库建立连接
        DruidPooledConnection conn = ds.getConnection();
        PreparedStatement statement = conn.prepareStatement("select  * from user");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();
    }
}

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

/**
 * @Description:德鲁伊的调用配置文件
 * @Author:@李小白
 * @Date:2019/8/30 19:32
 */
public class DruidDemo2 {
    public static void main(String[] args) throws Exception {
        //导入德鲁伊的配置文件,jar包,MySQL的jar包
        //配置文件的方式
        Properties properties = new Properties();
        properties.load(new FileReader("src/druid.properties"));
        //用工厂类,创建一个数据源
        DataSource dataSource = new DruidDataSourceFactory().createDataSource(properties);

        //开始和数据库进行连接
        Connection conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

<think>我们已知用户希望根据应力数据中的深度信息来设置`psvelo`命令绘制应力线的颜色。在GMT中,`psvelo`命令通常用于绘制速度矢量场,但也可以用于表示应力(作为矢量的一种形式)。要实现根据深度(即第三列数据)改变颜色,我们需要以下步骤: 1. **准备数据文件**:数据文件应包含经度、纬度、应力分量(如东西向应力、南北向应力)以及深度(用于颜色映射)。数据格式示例: ``` 经度 纬度 深度(km) 应力东分量 应力北分量 [其他列...] ``` 2. **创建颜色查找表(CPT)**:使用`makecpt`命令根据深度范围创建CPT文件,定义不同深度对应的颜色。 3. **使用`psvelo`绘制**:在`psvelo`命令中,通过`-C`选项指定CPT文件,并设置`+z`选项表示使用数据中的某一列(这里是深度列)作为颜色值。 4. **添加颜色条**:使用`colorbar`命令添加颜色条以显示深度-颜色对应关系。 具体到命令的修改,用户的数据文件可能如下(假设深度在第3列): ```bash # 数据格式: 经度 纬度 深度(km) 应力东分量 应力北分量 应力值(标量,可选) 等 104.33 31.91 39.8 0.5 0.3 104.11 31.52 27.1 0.2 0.4 ... ``` ### 具体步骤 #### 步骤1:创建CPT文件 ```bash gmt makecpt -Cjet -T0/100/10 -D > depth.cpt ``` 这里假设深度范围为0-100 km,间隔10 km,使用jet色带。用户应根据实际数据调整`-T`参数。 #### 步骤2:使用`psvelo`绘制应力矢量,并根据深度着色 ```bash gmt psvelo data.txt -J -R -W1p -Sx0.01c -Cdepth.cpt -A -L -V -B -G+z ``` 参数解释: - `-Cdepth.cpt`:指定颜色表。 - `-G+z`:表示使用数据文件中的某一列(默认是第3列,即深度列)来设置颜色。注意:`-G`通常用于设置填充色,而`+z`表示从数据文件中读取颜色值所在的列。在GMT 6中,`psvelo`的`-G`和`-W`可以分别设置填充和边框,但这里我们关注的是矢量的颜色(包括线和箭头)。实际上,`psvelo`命令中直接使用`-G`或`-W`设置颜色时,可以通过`+z`来指定颜色数据列。 然而,在`psvelo`中,要使用数据列来设置颜色,我们需要使用`-G+z`或`-W+z`,具体语法如下: - `-G+z`:用指定列(默认第3列,但可以通过`-i`调整)作为填充色的颜色值(如果矢量有填充,比如箭头)。 - `-W[pen][+z]`:用指定列作为笔的颜色值。 但是,由于应力矢量通常用箭头表示,我们可能希望整个矢量(包括线和箭头头)都用同一种颜色(根据深度)。因此,我们可以这样设置: ```bash gmt psvelo data.txt -J -R -W1p+z -Sx0.01c -Cdepth.cpt -A -L -B ``` 这里: - `-W1p+z`:表示笔的宽度为1点,并且使用数据中的某一列(默认第3列)作为笔的颜色值。注意,这时需要同时使用`-C`选项提供CPT文件。 - `-Sx0.01c`:设置矢量的缩放因子(这里0.01c表示每个单位矢量对应的图上长度,需要根据实际应力值调整)。 - `-A`:设置箭头属性,例如`-A0.3c/0.1c/0.1c`表示箭头长度/宽度/尾部宽度。 - `-L`:在矢量下方绘制基线(通常不需要,可以省略)。 但是,在GMT 6中,`psvelo`命令的`-W`选项并不支持`+z`。实际上,`psvelo`命令没有直接提供按数据列设置颜色的功能。因此,我们需要使用另一种方法:通过管道将数据传递给`psvelo`,并在数据中增加一列颜色值(通过CPT查找),然后使用`-Z`选项指定颜色列。 ### 替代方法:预处理数据,添加颜色列 我们可以先使用`gmt grd2xyz`或`gmt apply`等命令将深度值转换为颜色值(通过CPT查找),然后在数据文件中添加一列颜色值(用RGB表示),最后在`psvelo`命令中通过`-G`和`-Z`选项使用该列。 但是,`psvelo`命令并不直接支持RGB颜色列。因此,我们可以将数据按深度分组,然后分别绘制不同深度的矢量(使用循环)。但这样效率较低。 另一种方法是使用`gmt plot`命令绘制矢量,但需要自己计算矢量的终点,并使用带箭头的线段。这比较复杂。 ### 推荐方法:使用GMT 6的`plot`命令绘制矢量 在GMT 6中,我们可以使用`plot`命令绘制矢量,并支持`-i`选项选择列,以及`-G`和`-W`使用数据列颜色。但是,`plot`命令绘制矢量需要提供起点和终点,或者提供方向和长度。而我们的数据是应力分量(东西分量和北分量),可以表示为从该点出发的矢量。 具体步骤: 1. 预处理数据,将应力分量转换为矢量的终点坐标(经度+东西分量,纬度+北分量)。注意:这里需要将应力分量转换为地理距离(例如度),这需要根据实际数据调整缩放因子。 2. 使用`plot`命令绘制从起点(经度,纬度)到终点(经度+东西分量,纬度+北分量)的矢量,并根据深度设置颜色。 但是,这种方法需要将应力分量转换为地理坐标的偏移量,而应力分量的单位通常不是度,所以需要转换(例如,1个应力单位对应图上0.1度)。 ### 实际解决方案 考虑到用户的需求是绘制应力线(可能是矢量箭头)并根据深度着色,而GMT的`psvelo`命令本身不支持`+z`,我们可以采用以下两种方法之一: #### 方法1:使用GMT 6的`plot`命令绘制矢量(带箭头) ```bash # 首先,将数据转换为:起点经度 起点纬度 终点经度 终点纬度 深度 # 假设原始数据:lon lat depth stress_e stress_n # 终点计算:假设应力分量乘以一个缩放因子scale(例如0.1度/应力单位) # 终点经度 = lon + stress_e * scale # 终点纬度 = lat + stress_n * scale # 使用awk预处理数据 awk -v scale=0.1 '{print $1, $2, $1+$4*scale, $2+$5*scale, $3}' data.txt > vectors.txt # 创建CPT gmt makecpt -Cjet -T0/100/10 -D > depth.cpt # 绘制矢量,使用第5列(深度)作为颜色值,通过-C和-Z选项 gmt plot vectors.txt -J -R -W1p -Cdepth.cpt -Z5 -SV0.3c+bc+bt+h0 -B -A ``` 参数说明: - `-SV0.3c+bc+bt+h0`:绘制矢量,箭头大小为0.3厘米,在起点(b)和终点(t)都绘制箭头(这里我们只需要在终点绘制,所以可以改为`-SV0.3c+et+bt`,但通常只在终点绘制箭头,所以用`+e`表示终点,`+b`表示起点,这里我们只需要在终点绘制箭头:`-SV0.3c+e`)。 - `-Z5`:使用第5列(即深度)作为颜色值(通过CPT映射)。 - `-W1p`:笔宽1点,颜色由`-Z`决定。 注意:这种方法需要将应力分量转换为地理位移,缩放因子需要根据实际数据调整,以保证箭头长度合适。 #### 方法2:使用`psvelo`并分别绘制不同深度的矢量(分组) 如果数据量不大,我们可以按深度范围分组,然后分别用不同颜色绘制。例如: ```bash # 创建CPT gmt makecpt -Cjet -T0/100/10 -D > depth.cpt # 提取不同深度范围的数据并绘制 for depth in 0 10 20 30 40 50 60 70 80 90 100; do # 提取深度在[depth, depth+10)范围内的数据 awk -v d=$depth '$3>=d && $3<(d+10)' data.txt > tmp.txt # 从CPT中获取颜色 color=$(gmt color depth.cpt -Z$depth -C) # 绘制该深度范围的矢量 gmt psvelo tmp.txt -J -R -W1p,$color -Sx0.01c -A -B done ``` 这种方法比较简单,但效率较低,且需要提前知道深度范围。 ### 总结 根据用户的需求,我推荐方法1,因为它直接利用GMT 6的`plot`命令的`-Z`选项,可以一次性绘制所有矢量并根据深度自动着色。但需要注意: 1. 应力分量到地理位移的缩放因子需要根据实际数据调整,以确保箭头长度适中。 2. 箭头方向和大小的表示要符合实际。 如果用户的数据已经是矢量形式(即包含起点和分量),则使用方法1的预处理步骤即可。 ### 修改后的命令示例(基于方法1) 假设用户的数据文件`stress.txt`格式为: ``` 经度 纬度 深度(km) 应力东分量 应力北分量 ``` 执行以下命令: ```bash # 预处理:将应力分量转换为终点坐标,缩放因子设为0.1(用户需调整) awk '{print $1, $2, $1+$4*0.1, $2+$5*0.1, $3}' stress.txt > vectors.txt # 绘图 gmt begin map png gmt basemap -J -R -B # 绘制海岸线等底图 gmt coast -Wthin -Glightgray -Baf # 创建CPT gmt makecpt -Cjet -T0/100/10 -D > depth.cpt # 绘制矢量,箭头大小为0.3厘米,终点箭头,颜色由第5列(深度)决定 gmt plot vectors.txt -SV0.3c+e -W1p -Cdepth.cpt -Z5 # 添加颜色条 gmt colorbar -DjBC -Baf -Cdepth.cpt -L0.1 gmt end show ``` 注意:缩放因子0.1需要根据实际应力值的大小调整,以避免箭头过长或过短。 ### 相关问题 §§相关问题§§ 1. 如何根据应力值的大小调整箭头的长度? 2. 在GMT中,如何绘制二维应力张量的主应力方向? 3. 如何将应力值从帕斯卡单位转换为地理位移的缩放因子?</think>在GMT中,使用`psvelo`命令根据应力数据中的深度信息设置应力线颜色,需要结合颜色查找表(CPT)和数据列索引来实现。以下是详细步骤和实现方法: ### 1. 数据文件格式要求 应力数据文件应包含以下列(顺序固定): ``` 经度 纬度 深度(km) 应力东分量 应力北分量 [其他可选列] ``` 示例数据格式: ``` 104.33 31.91 39.8 0.5 0.3 104.11 31.52 27.1 0.2 0.4 103.67 31.13 6.4 0.8 -0.1 ``` ### 2. 实现步骤 #### (1) 创建深度颜色映射(CPT) ```bash gmt makecpt -Cviridis -T0/100/10 -D > depth.cpt ``` - `-Cviridis`:使用科学可视化常用的颜色方案 - `-T0/100/10`:设置深度范围0-100km,间隔10km - `-D`:创建连续渐变色带 #### (2) 使用psvelo绘制彩色应力线 ```bash gmt psvelo stress_data.txt -J -R -W1p,cl -Sx0.1c -Cdepth.cpt -i0,1,2,3,4 -G+z ``` 关键参数说明: - `-W1p,cl`:设置线宽1点,`cl`表示使用CPT颜色 - `-Sx0.1c`:设置矢量缩放比例(根据应力值调整) - `-Cdepth.cpt`:指定颜色映射表 - `-i0,1,2,3,4`:显式指定列索引(经度,纬度,深度,东分量,北分量) - `-G+z`:使用第3列(深度)作为颜色值 #### (3) 添加颜色条 ```bash gmt colorbar -DjBC+w5c/0.3c+h -Bxaf+l"深度(km)" -Cdepth.cpt ``` ### 3. 完整示例脚本 ```bash #!/usr/bin/env bash gmt begin stress_map png # 设置底图 gmt basemap -R102.5/105.5/30.5/32.5 -JQ104/15c -Ba -BWSen gmt coast -Da -Wthin -Glightgray # 创建深度CPT gmt makecpt -Cplasma -T0/100/20 -D -H > depth.cpt # 绘制彩色应力线 gmt psvelo stress.txt -W1.5p,cl -Sx0.08c -Cdepth.cpt -i0,1,2,3,4 -G+z -A0.3c+p1p+e # 添加颜色条 gmt colorbar -DjBC+w6c/0.4c+o0.5c -Bx+l"深度 (km)" -Cdepth.cpt gmt end show ``` ### 4. 关键原理 1. **深度列映射**:`-i0,1,2,3,4`明确指定第3列(索引2)为深度值 2. **颜色绑定**:`-G+z`将深度值绑定到CPT颜色 3. **矢量渲染**:`-W1p,cl`中的`cl`使线条颜色随深度变化 4. **动态缩放**:`-Sx0.1c`根据应力值自动调整箭头长度 ### 5. 高级定制 ```bash # 突出显示特定深度范围 (30-60km) gmt makecpt -Cgreen,red,blue -T0,30,60,100 -D > highlight.cpt # 添加应力值标签 gmt psvelo stress.txt -W1p,cl -Sx0.1c -Cdepth.cpt -G+z -F+a15+jCM+f6p ``` > **注意**:GMT 6.0+版本支持`+z`选项直接使用数据列着色,旧版本需使用`-Z${col}`格式[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值