百度街景图片存MySQL

1 引言

       本文记录百度街景图片的存储过程,主要逻辑是:读取本地图片,存mysql,代码直接下载稍作修改就可运行,存储相关使用的是Java原生接口。更多代码以及百度街景相关背景参考另一篇博客-----百度地图街景图片爬取并存库

2 建表

       表字段主要分为两部分:当前街景部分和历史街景部分,每部分都包含4大属性:id、拍摄日期、图片url(4张)、图片二进制流。为了更好的搜索,建立相关索引

CREATE TABLE `test`.`baidu_pano_pics`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `cur_pano_id` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '当前街景位置ID',
  `cur_pano_date` varchar(6) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '当前街景的拍摄日期',
  `cur_pic_url_1` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片1url',
  `cur_pic_url_2` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片2url',
  `cur_pic_url_3` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片3url',
  `cur_pic_url_4` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片4url',
  `cur_pic_1` mediumblob NULL COMMENT '图片1(从左往右)',
  `cur_pic_2` mediumblob NULL COMMENT '图片2',
  `cur_pic_3` mediumblob NULL COMMENT '图片3',
  `cur_pic_4` mediumblob NULL COMMENT '图片4',
  `his_pano_id` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史街景位置ID',
  `his_pano_date` varchar(6) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史街景的拍摄日期',
  `his_pic_url_1` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史图片1url',
  `his_pic_url_2` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史图片2url',
  `his_pic_url_3` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史图片3url',
  `his_pic_url_4` char(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '历史图片4url',
  `his_pic_1` mediumblob NULL COMMENT '历史街景位置图片1',
  `his_pic_2` mediumblob NULL COMMENT '历史街景位置图片2',
  `his_pic_3` mediumblob NULL COMMENT '历史街景位置图片3',
  `his_pic_4` mediumblob NULL COMMENT '历史街景位置图片4',
  `timestamp` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '入库时间戳',
  PRIMARY KEY USING BTREE (`id`),
  UNIQUE INDEX `pano_index` USING BTREE(`cur_pano_id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '百度街景图片存储表' ROW_FORMAT = Compact;

3 代码

       先封装一个数据库连接的工具类DbUtil,ConfigParser类源码移步至作者之前的爬虫相关文章:

/**
 * @author zhanghuigen
 * @since 0.1.0
 **/
public class DbUtil {

    private final static Logger LOGGER  =  Logger.getLogger(DbUtil.class);

    private DbUtil(){}

    private static class DbUtilHolder{
        private static DbUtil singleton = new DbUtil();
    }

    public static DbUtil getInstance(){
        return DbUtilHolder.singleton;
    }


    // 获取连接
    public Connection getConn() throws SQLException {
        ConfigParser parser = ConfigParser.getInstance();
        String dbAlias = "mysql-data";
        Map<String, Object> dbConfig = parser.getModuleConfig("database");
        Map<String, Object> mysqlConfig = (Map)parser.assertKey(dbConfig, dbAlias, "database");
        String url = (String)parser.assertKey(mysqlConfig, "url", "database." + dbAlias);
        String username = (String)parser.assertKey(mysqlConfig, "username", "database." + dbAlias);
        String password = (String)parser.assertKey(mysqlConfig, "password", "database." + dbAlias);
        return DriverManager.getConnection(url, username, password);
    }

    // 关闭连接
    public void closeConn(Connection conn) {
        if (null != conn) {
            try {
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("关闭连接失败!");
                e.printStackTrace();
            }
        }
    }
}

       建立百度街景图片的java bean,通过该类实现相关属性的封装或存取,PanoPic的属性填充详见前文,这里不再赘述。

import java.util.List;

/**
 * @author zhanghuigen
 * @since 0.1.0
 **/
public class PanoPic {

    /**
     * 当前街景ID
     */
    private String curPanoId;

    /**
     *当前街景拍摄时间
     */
    private String curShootDate;

    /**
     * 当前街景本地存储路径
     */
    private List<String> curPicPath;

    /**
     * 历史街景ID
     */
    private String hisPanoId;

    /**
     * 历史街景拍摄时间
     */
    private String hisShootDate;

    /**
     * 历史街景本地存储路径
     */
    private List<String> hisPicPath;

    public PanoPic(String curPanoId,String curShootDate,List<String> curPicPath){
        this.curPanoId = curPanoId;
        this.curShootDate = curShootDate;
        this.curPicPath = curPicPath;
    }

    public String getCurPanoId() {
        return curPanoId;
    }

    public void setCurPanoId(String curPanoId) {
        this.curPanoId = curPanoId;
    }

    public String getCurShootDate() {
        return curShootDate;
    }

    public void setCurShootDate(String shootDate) {
        this.curShootDate = shootDate;
    }

    public List<String> getCurPicPath() {
        return curPicPath;
    }

    public void setCurPicPath(List<String> curPicPath) {
        this.curPicPath = curPicPath;
    }

    public String getHisPanoId() {
        return hisPanoId;
    }

    public void setHisPanoId(String hisPanoId) {
        this.hisPanoId = hisPanoId;
    }

    public String getHisShootDate() {
        return hisShootDate;
    }

    public void setHisShootDate(String hisShootDate) {
        this.hisShootDate = hisShootDate;
    }

    public List<String> getHisPicPath() {
        return hisPicPath;
    }

    public void setHisPicPath(List<String> hisPicPath) {
        this.hisPicPath = hisPicPath;
    }
}

       街景图片存储的工具类封装如下:

/**
 * @author zhanghuigen
 * @since 0.1.0
 **/
public class BlobInsertUtils {

    private final static Logger LOGGER  =  Logger.getLogger(BlobInsertUtils.class);
    private final String SQL_NO_HIS;
    private final String SQL_WITH_HIS;

    public BlobInsertUtils(String tableName){
        SQL_NO_HIS = getSQLWithoutHis(tableName);
        SQL_WITH_HIS = getSqlWithHis(tableName);
    }

    /**
     * 将当前街景图片插入数据库
     */
    public void readImage2DB(String tableName,String panoId, List<String> picPath) {
        Connection conn = null;
        PreparedStatement ps = null;
        ImagePersistence imagePersistence = new ImagePersistence();
        List<FileInputStream> fileInputStreamsList = new ArrayList<>(4);
        FileInputStream fileInputStream;
        try {
            for (String pic:picPath){
                fileInputStreamsList.add(imagePersistence.readImage(pic));
            }
            conn = DbUtil.getInstance().getConn();
            String sql = "insert into "+ tableName +
                    "(pano_id,pic_url_1,pic_url_2,pic_url_3,pic_url_4,pic_1,pic_2,pic_3,pic_4,timestamp)values(?,?,?,?,?,?,?,?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, panoId);
            for (int i = 0; i < picPath.size(); i++){
                fileInputStream = fileInputStreamsList.get(i);
                ps.setString(2+i, picPath.get(i));
                ps.setBinaryStream(6+i, fileInputStream, fileInputStream.available());
            }
            ps.setTimestamp(10,new Timestamp(System.currentTimeMillis()));
            int count = ps.executeUpdate();
            if (count > 0) {
                LOGGER.info(panoId + "插入成功!");
            } else {
                LOGGER.info(panoId + "插入失败!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtil.getInstance().closeConn(conn);
            if (null != ps) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getSql(String tableName){
        return "insert into " + tableName +
                "(cur_pano_id,cur_pano_date,cur_pic_url_1,cur_pic_url_2,cur_pic_url_3,cur_pic_url_4,cur_pic_1,cur_pic_2,cur_pic_3,cur_pic_4,"+
                "(his_pano_id,his_pano_date,his_pic_url_1,his_pic_url_2,his_pic_url_3,his_pic_url_4,his_pic_1,his_pic_2,his_pic_3,his_pic_4,"+
                "timestamp)"+
                "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"+
                "on duplicate key update"+
                "cur_pano_id=?,cur_pano_date=?,cur_pic_url_1=?,cur_pic_url_2=?,cur_pic_url_3=?,cur_pic_url_4=?,cur_pic_1=?,cur_pic_2=?,cur_pic_3=?,cur_pic_4=?"+
                "his_pano_id=?,his_pano_date=?,his_pic_url_1=?,his_pic_url_2=?,his_pic_url_3=?,his_pic_url_4=?,his_pic_1=?,his_pic_2=?,his_pic_3=?,his_pic_4=?";
    }

    private String getSqlWithHis(String tableName){
        return "insert into " + tableName +
                "(cur_pano_id,cur_pano_date,cur_pic_url_1,cur_pic_url_2,cur_pic_url_3,cur_pic_url_4,cur_pic_1,cur_pic_2,cur_pic_3,cur_pic_4,"+
                "his_pano_id,his_pano_date,his_pic_url_1,his_pic_url_2,his_pic_url_3,his_pic_url_4,his_pic_1,his_pic_2,his_pic_3,his_pic_4,"+
                "timestamp)"+
                "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
    }

    private String getSQLWithoutHis(String tableName){
        return "insert into " + tableName +
                "(cur_pano_id,cur_pano_date,cur_pic_url_1,cur_pic_url_2,cur_pic_url_3,cur_pic_url_4,cur_pic_1,cur_pic_2,cur_pic_3,cur_pic_4,"+
                "timestamp)"+
                "values(?,?,?,?,?,?,?,?,?,?,?)";
    }

    /**
     * 将当前及历史街景图片插入数据库
     *
     * @param panoPic bean
     */
    public void insertAllImage2DBWithNoCheck(PanoPic panoPic) {
        Connection conn = null;
        PreparedStatement ps = null;
        ImagePersistence imagePersistence = new ImagePersistence();
        int allPicNum;
        boolean isHisEmpty = true;
        List<String> curPicPath = panoPic.getCurPicPath();
        List<String> hisPicPath = null;
        if (null==panoPic.getHisPicPath()){
            allPicNum = curPicPath.size();
        }else {
            isHisEmpty = false;
            hisPicPath = panoPic.getHisPicPath();
            allPicNum = curPicPath.size() + panoPic.getHisPicPath().size();
        }
        List<FileInputStream> fileInputStreamsList = new ArrayList<>(allPicNum);
        FileInputStream fileInputStream;
        try {
            //加载当前街景输入流
            for (String curPic:curPicPath){
                fileInputStreamsList.add(imagePersistence.readImage(curPic));
            }
            conn = DbUtil.getInstance().getConn();
            //加载历史街景输入流
            if (!isHisEmpty){
                for (String hisPic:panoPic.getHisPicPath()){
                    fileInputStreamsList.add(imagePersistence.readImage(hisPic));
                }
                ps = conn.prepareStatement(SQL_WITH_HIS);
            }else {
                ps = conn.prepareStatement(SQL_NO_HIS);
            }
            ps.setString(1, panoPic.getCurPanoId());
            ps.setString(2, panoPic.getCurShootDate());
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            for (int i = 0; i < curPicPath.size(); i++){
                fileInputStream = fileInputStreamsList.get(i);
                ps.setString(3+i, curPicPath.get(i));
                ps.setBinaryStream(7+i, fileInputStream, fileInputStream.available());
            }
            if (!isHisEmpty){
                ps.setString(11, panoPic.getHisPanoId());
                ps.setString(12, panoPic.getHisShootDate());
                for (int j = 0; j < hisPicPath.size(); j++){
                    fileInputStream = fileInputStreamsList.get(4+j);
                    ps.setString(13+j, hisPicPath.get(j));
                    ps.setBinaryStream(17+j, fileInputStream, fileInputStream.available());
                }
                ps.setTimestamp(21,timestamp);
            }else {
                ps.setTimestamp(11,timestamp);
            }
            int count = ps.executeUpdate();
            if (count > 0) {
                LOGGER.info(panoPic.getCurPanoId() + "插入成功!");
            } else {
                LOGGER.info(panoPic.getCurPanoId() + "插入失败!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtil.getInstance().closeConn(conn);
            if (null != ps) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 将当前及历史街景图片插入数据库
     * 已有则更新
     * @param panoPic bean
     */
    public void readAllImage2DB(PanoPic panoPic) {
        Connection conn = null;
        PreparedStatement ps = null;
        ImagePersistence imagePersistence = new ImagePersistence();
        int allPicNum;
        boolean isHisEmpty = true;
        List<String> curPicPath = panoPic.getCurPicPath();
        List<String> hisPicPath = null;
        if (null==panoPic.getHisPicPath()){
            allPicNum = panoPic.getCurPicPath().size();
        }else {
            isHisEmpty = false;
            hisPicPath = panoPic.getHisPicPath();
            allPicNum = panoPic.getCurPicPath().size() + panoPic.getHisPicPath().size();
        }
        List<FileInputStream> fileInputStreamsList = new ArrayList<>(allPicNum);
        FileInputStream fileInputStream;
        try {
            //加载当前街景输入流
            for (String curPic:curPicPath){
                fileInputStreamsList.add(imagePersistence.readImage(curPic));
            }
            //加载历史街景输入流
            if (!isHisEmpty){
                for (String hisPic:panoPic.getHisPicPath()){
                    fileInputStreamsList.add(imagePersistence.readImage(hisPic));
                }
            }
            conn = DbUtil.getInstance().getConn();
            ps.setString(1, panoPic.getCurPanoId());
            ps.setString(2, panoPic.getCurShootDate());
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            for (int i = 0; i < curPicPath.size(); i++){
                fileInputStream = fileInputStreamsList.get(i);
                ps.setString(3+i, curPicPath.get(i));
                ps.setBinaryStream(7+i, fileInputStream, fileInputStream.available());
            }
            if (!isHisEmpty){
                ps.setString(11, panoPic.getHisPanoId());
                ps.setString(12, panoPic.getHisShootDate());
                for (int j = 0; j < hisPicPath.size(); j++){
                    fileInputStream = fileInputStreamsList.get(4+j);
                    ps.setString(13+j, hisPicPath.get(j));
                    ps.setBinaryStream(17+j, fileInputStream, fileInputStream.available());
                }
                ps.setTimestamp(21,timestamp);
                //on duplicate key update 后续参数拼接
                ps.setString(22, panoPic.getCurPanoId());
                ps.setString(23, panoPic.getCurShootDate());
                for (int i = 0; i < curPicPath.size(); i++){
                    fileInputStream = fileInputStreamsList.get(i);
                    ps.setString(24+i, curPicPath.get(i));
                    ps.setBinaryStream(28+i, fileInputStream, fileInputStream.available());
                }
                ps.setString(32, panoPic.getHisPanoId());
                ps.setString(33, panoPic.getHisShootDate());
                for (int j = 0; j < hisPicPath.size(); j++){
                    fileInputStream = fileInputStreamsList.get(4+j);
                    ps.setString(34+j, hisPicPath.get(j));
                    ps.setBinaryStream(38+j, fileInputStream, fileInputStream.available());
                }
            }else {
                ps.setTimestamp(21,timestamp);
                //on duplicate key update 后续参数拼接
                ps.setString(22, panoPic.getCurPanoId());
                ps.setString(23, panoPic.getCurShootDate());
                for (int i = 0; i < curPicPath.size(); i++){
                    fileInputStream = fileInputStreamsList.get(i);
                    ps.setString(24+i, curPicPath.get(i));
                    ps.setBinaryStream(28+i, fileInputStream, fileInputStream.available());
                }
            }
            ps.setTimestamp(42,timestamp);
            int count = ps.executeUpdate();
            if (count > 0) {
                LOGGER.info(panoPic.getCurPanoId() + "插入成功!");
            } else {
                LOGGER.info(panoPic.getCurPanoId() + "插入失败!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtil.getInstance().closeConn(conn);
            if (null != ps) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

       这里有个输入流的封装(线程安全),传入文件路径参数,用于读取本地图片,源码如下:

/**
 * @author zhanghuigen
 * @since 0.1.0
 **/
public class ImagePersistence {

    // 读取本地图片获取输入流
    public FileInputStream readImage(String path) throws IOException {
        return new FileInputStream(new File(path));
    }

    // 读取表中图片获取输出流
    public void readBin2Image(InputStream in, String targetPath) {
        File file = new File(targetPath);
        String path = targetPath.substring(0, targetPath.lastIndexOf("/"));
        if (!file.exists()) {
            new File(path).mkdir();
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            int len = 0;
            byte[] buf = new byte[1024];
            while ((len = in.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fos) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

       最后封装整个街景图片存储逻辑示例调用如下:

private void storePanoPicsToDb(PanoPic panoPic,String tableName){
    //读本地图片存数据库
    BlobInsertUtils blobInsertUtils = new BlobInsertUtils(tableName);
    blobInsertUtils.insertAllImage2DBWithNoCheck(panoPic);
}

3.1 注意

       BlobInsertUtils类中有些插入方法是没有用到的,本文只调用了insertAllImage2DBWithNoCheck方法。readAllImage2DB方法和insertAllImage2DBWithNoCheck方法区别是考虑到爬虫逻辑一般为周期性的计划任务(街景可能每周或每个月爬取1次),为了实现插入时判断数据库表中是否已有记录,readAllImage2DB采用on duplicate key update关键字建立插入语句(无则插入,有则更新),但本方法由于字段过多(超过了41个),调用该方法插入会报错,解决办法:重新建表,删除不必要的字段(如:图片路径),重新封装sql参数即可。

参考

blob类型数据存储参考:
https://blog.youkuaiyun.com/qq_38785658/article/details/75009700
https://www.cnblogs.com/warrior4236/p/5682830.html

<think>首先,用户的问题是:“我想通过GIS技术获取百度街景图片的方法或API使用 请问如何使用GIS技术通过百度地图API获取街景图片”。这涉及到使用GIS(地理信息系统)技术和百度地图API来获取街景图片。 从系统级指令中,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考提供的引用: - 引用[1]:百度地图API提供地图展示、地点搜索等功能。[^1] - 引用[2]:百度地图街景图片爬取的请求示例。[^2] - 引用[3]:在GIS软件中打点并计算经纬度,然后用于街景爬取。[^3] - 引用[4]:学习如何申请API Key、使用Python代码等。[^4] - 引用[5]:全景API的请求构成示例。[^5] 用户的问题是关于如何使用GIS技术通过百度地图API获取街景图片。所以,我需要整合这些引用,提供一个逐步的指南。 回答结构: 1. **介绍**:简要解释GIS技术与百度地图API的结合。 2. **步骤**:详细步骤,包括申请API Key、准备GIS数据、使用API等。 3. **代码示例**:提供Python代码示例,因为引用中提到了Python。 4. **注意事项**:一些关键点或常见问题。 5. **相关问题**:生成相关问题。 关键点: - GIS技术用于准备地理数据,如点的经纬度。 - 百度地图API用于请求街景图片。 - 需要申请API Key(ak)。 - 请求URL格式如引用[5]所示:`http://api.map.baidu.com/panorama/v2?ak=YOUR_AK&width=512&height=256&location=经度,纬度&fov=180&heading=0&pitch=0` 步骤大纲: 1. **申请百度API Key**:参考引用[4],需要注册百度地图开放平台账号并申请Key。 2. **准备GIS数据**:参考引用[3],使用GIS软件(如ArcGIS)沿路网打点,计算点的经纬度(单位为度),并导出为表格。 3. **编写Python代码**:使用requests库发送HTTP请求获取街景图片。 4. **处理响应**:解析API返回的数据(可能是图片URL或直接图片数据)。 5. **图片**:保存图片到本地。 代码示例: - 基于引用[2]和[5],构建请求URL。 - 例如:`url = f"http://api.map.baidu.com/panorama/v2?ak={ak}&width={width}&height={height}&location={lon},{lat}&fov={fov}&heading={heading}&pitch={pitch}"` 确保回答真实可靠:基于引用,百度API确实提供这些功能。 生成相关问题:在最后添加相关问题,如优化方法、应用场景等。 现在,构建回答。</think>### 使用GIS技术通过百度地图API获取街景图片的方法 百度地图API提供了获取街景图片的接口,结合GIS(地理信息系统)技术,您可以高效地批量采集特定位置的街景图像。GIS技术用于准备和优化地理数据(如点的经纬度),而API则负责请求和返回图片。以下是详细的步骤指南,确保方法真实可靠,基于百度地图开放平台的官方文档和实践经验[^4][^5]。 #### 步骤1: 申请百度地图API Key - **为什么需要**:API Key是访问百度地图服务的唯一授权凭证,所有请求都必须包含它。 - **如何操作**: 1. 注册百度地图开放平台账号(访问[百度地图开放平台](https://lbsyun.baidu.com/))。 2. 创建应用,选择“服务端”类型(适合Python脚本调用)。 3. 获取API Key(ak),形如`E4805d16520de693a3fe707cdc962045`。 - **注意事项**:免费额度有限(每日配额),超出需付费;确保在代码中保密ak,避免泄露[^4]。 #### 步骤2: 使用GIS技术准备地理数据 GIS技术用于生成采样点(如沿路网的点),并导出其经纬度坐标。这些坐标将作为API请求的输入。 - **工具推荐**:使用ArcGIS Pro或QGIS等GIS软件。 - **操作步骤**: 1. **导入路网数据**:加载您的路网Shapefile或GeoJSON文件到GIS软件中。 2. **沿线生成点**:使用工具如“Generate Points Along Lines”(在ArcGIS Pro中)。设置参数: - 采样间隔(例如每100米一个点)。 - 输出坐标系为WGS84(EPSG:4326),以确保经纬度单位为度(degree)[^3]。 3. **计算经纬度**:导出点的属性表,添加字段计算经度(longitude)和纬度(latitude)。在ArcGIS中,使用“Calculate Geometry”工具,设置单位为度。 4. **导出数据**:将点数据转换为CSV或Excel表格,格式示例: | ID | Longitude | Latitude | |-----|-----------|----------| | 1 | 116.313393| 40.04778 | | 2 | 116.320000| 40.05000 | - **GIS优化**:通过缓冲区分析或密度调整,减少冗余点,提高效率[^3]。 #### 步骤3: 使用Python调用百度街景API 百度街景API的请求URL基于参数化构建,返回图片数据。以下是Python代码示例,使用`requests`库发送HTTP请求。 - **API请求格式**:参考官方文档,基本URL为: ``` http://api.map.baidu.com/panorama/v2?ak=YOUR_AK&width=WIDTH&height=HEIGHT&location=LON,LAT&fov=FOV&heading=HEADING&pitch=PITCH ``` - 参数说明: - `ak`: 您的API Key。 - `width`和`height`: 图片分辨率(建议512x256以平衡质量)。 - `location`: 经纬度,格式为`经度,纬度`(如`116.313393,40.04778`)。 - `fov`: 视角范围(0-180,默认180表示全景)。 - `heading`: 朝向角度(0-360,0表示北)。 - `pitch`: 俯仰角度(-90到90,0为水平)[^5][^2]。 - **Python代码示例**: ```python import requests import pandas as pd import os # 读取GIS导出的CSV文件(假设文件名为points.csv) df = pd.read_csv('points.csv') # API参数设置 ak = "YOUR_API_KEY" # 替换为您的ak width = 512 height = 256 fov = 180 heading = 0 # 默认朝北 pitch = 0 # 默认水平 # 创建目录图片 os.makedirs('streetview_images', exist_ok=True) # 遍历每个点,发送API请求 for index, row in df.iterrows(): lon = row['Longitude'] lat = row['Latitude'] url = f"http://api.map.baidu.com/panorama/v2?ak={ak}&width={width}&height={height}&location={lon},{lat}&fov={fov}&heading={heading}&pitch={pitch}" try: response = requests.get(url) if response.status_code == 200: # 保存图片(文件名基于经纬度) image_path = f"streetview_images/{lon}_{lat}.jpg" with open(image_path, 'wb') as f: f.write(response.content) print(f"图片保存成功: {image_path}") else: print(f"请求失败: 状态码 {response.status_code}, 位置 {lon},{lat}") except Exception as e: print(f"错误: {e}") print("街景图片爬取完成!") ``` - **代码说明**: - 使用`pandas`读取GIS导出的表格数据。 - 循环每个点构建URL,并下载图片。 - 错误处理确保脚本健壮性。 - 返回的图片可直接保存为JPG文件[^2][^5]。 #### 步骤4: 处理结果与优化 - **储与管理**:图片按经纬度命名,便于GIS软件中重新关联地理数据。 - **性能优化**: - 使用异步请求(如`aiohttp`库)加速批量爬取。 - 调整GIS采样点密度,避免API配额超限。 - **常见问题**: - 位置无街景:API可能返回错误,需检查位置是否在覆盖区。 - 配额不足:监控使用量,或申请企业版API[^4]。 #### 注意事项 - **合法合规**:遵守百度地图API使用条款,禁止大规模商业爬取。 - **数据质量**:GIS数据的精度直接影响图片准确性;建议使用高精度路网数据。 - **替代方案**:如果API不可用,可考虑第三方库如`baidu-maps-api`,但官方API最可靠[^1][^4]。 通过以上步骤,您可以将GIS技术与百度API无缝结合,高效获取街景图片。如果您有具体路网数据,我可以帮助调试代码!
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值