大疆智图api(java实现)

前言

最近在研究大疆智图api,感觉文档写的不是很详细,样例代码也看不懂,而且没有java样例代码。本文章只写了部分api调用,详细api查看API Reference (dji.com)本文章。这是本人第一次发表文章,也不是java大佬,如果文章和代码出现问题或者需要优化的地方,请大家纠错提醒。本文章尽可能把使用大疆智图api步骤写完整。

申请大疆智图api账号

访问大疆智图api网址DJI Developer

如果没有账号,则进行注册,有账号,则直接登入。

成为开发者

注册后,返回大疆智图api页面,点击右上角的控制台

 进入控制台,申请成为开发者

 

 填写信息,点击提交后,会刷新页面。如果没有开通,则点击免费使用

根据自己情况填写,应该很好过的。不出意外,两三天就可以申请成功。

申请成功后的控制台页面

 我已经使用过了。App Key 和Secret Key 我们会用到。

准备建模素材

进入大疆智图api文档大疆智图API (dji.com)

 点击快速入门,点击右侧 准备素材

根据需求下载素材,本文章选择3D素材。下载后进行解压。

 实现步骤

主要代码

引入依赖,本文章使用的是hutool的请求工具类

<!--    hctool-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.2</version>
    </dependency>

用于各个请求处理(目前只写了POST,GET请求)

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Locale;
import static java.security.MessageDigest.getInstance;


public class TestDJ {
    private static final String DJI_APP_KEY = "";//大疆智图API的APP Key
    private static final String DJI_SECRET_KEY = "";//大疆智图API的Secret Key
    private static final String HOST = "https://openapi-cn.dji.com";
    /*
        URL 大疆智图API 接口请求地址
        payload 请求参数,如果不需要请求参数,请为空字符串
        lowerMethod 请求方法,小写字母  get/post/put/delete
     */
    public static JSONObject AccessDJ(String URI, String payload, String lowerMethod) {
        try {
            String url = HOST + URI;
            String date = getFormattedDate();
            String digest = getDigest(payload);
            String requestSignature = calculateSignature(date, lowerMethod, URI, digest);
            HttpRequest request = null;
            if(lowerMethod.equals("post")){// delete和post请求还未测试 根据需求进行添加
                request = HttpRequest.post(url);
            }else if(lowerMethod.equals("get")){
                request = HttpRequest.get(url);
            }
            request.header("Date", date)
                    .header("Digest", "SHA-256=" + digest)
                    .header("Authorization", "hmac username=\"" + DJI_APP_KEY + "\", algorithm=\"hmac-sha256\", headers=\"date @request-target digest\", signature=\"" + requestSignature + "\"")
                    .header("Content-Type", "application/json;charset=UTF-8");
            if (!payload.isEmpty()) {
                request.body(payload);
            }
            HttpResponse response = request.execute();
            if (response.isOk()) {
                System.out.println("response.body() = " + response.body());
                return JSONUtil.parseObj(response.body());
            } else {
                System.out.println("HTTP error code: " + response.getStatus());
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String getFormattedDate() { 
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US)
                .withZone(ZoneId.of("GMT"));
        return formatter.format(ZonedDateTime.now(ZoneId.of("GMT")));
    }
    private static String getDigest(String data) throws NoSuchAlgorithmException {
        return Base64.getEncoder().encodeToString(getInstance("SHA-256").digest(data.getBytes(StandardCharsets.UTF_8)));
    }

    private static String calculateSignature(String date, String method, String uri, String digest) throws NoSuchAlgorithmException, InvalidKeyException {
        String content = "date: " + date + "\n@request-target: " + method + " " + uri + "\ndigest: SHA-256=" + digest;
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        hmacSha256.init(new SecretKeySpec(DJI_SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        return Base64.getEncoder().encodeToString(hmacSha256.doFinal(content.getBytes(StandardCharsets.UTF_8)));
    }
}

根据文档获取各个API请求路径大疆智图API (dji.com)API Reference (dji.com)

获取Token

获取token的请求路径:

URI=/terra-rescon-be/v2/store/obtain_token
    public static final String URI_TOKEN = "/terra-rescon-be/v2/store/obtain_token";
    public static void main(String[] args) {
                JSONObject accessToken = getAccessToken();
                System.out.println(accessToken);
    }
    public static JSONObject getAccessToken() {
        return AccessDJ(URI_TOKEN, "","post");
    }

结果为JSON格式:

 

上传素材

利用阿里云的OSS

依赖

<!--    OSS-->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.15.1</version>
    </dependency>


import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestOSS {
    /*
    ENDPOINT 大疆智图API的外网访问网址 默认 https://oss-cn-hangzhou.aliyuncs.com
    ACCESS_KEY_ID  token的 accessKeyID
    SECRET_ACCESS_KEY  token的 secretAccessKey
    BUCKET_NAME  token的 cloudBucketName
    STORE_PATH  token的 sessionToken
    TOKEN  token的 sessionToken
    FILE_PATH  本地素材的路径
     */
    public static boolean uploadFile(String ENDPOINT, String ACCESS_KEY_ID, String SECRET_ACCESS_KEY, String BUCKET_NAME, String STORE_PATH, String TOKEN,String FILE_PATH) throws IOException {
        boolean success = false;
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,SECRET_ACCESS_KEY,TOKEN);
        List<Map<String, String>> uploadedFiles = new ArrayList<>();

        File localFolder = new File(FILE_PATH);
        Files.walk(localFolder.toPath())
                .filter(Files::isRegularFile)
                .forEach(path -> {
                    String relativePath = localFolder.toURI().relativize(path.toUri()).getPath();
                    String ossFilePath = STORE_PATH + relativePath.replace("\\", "/");
                    try {
                        PutObjectResult putResult = ossClient.putObject(BUCKET_NAME, ossFilePath, path.toFile());
                        String etag = putResult.getETag();
                        System.out.println("Uploaded: " + path + " -> " + ossFilePath + ", etag: " + etag);

                        Map<String, String> fileInfo = new HashMap<>();
                        fileInfo.put("name", relativePath.replace("\\", "/"));
                        fileInfo.put("etag", etag);
                        fileInfo.put("checksum", etag);
                        uploadedFiles.add(fileInfo);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });

        if (!uploadedFiles.isEmpty()) {
            String json = JSONUtil.toJsonStr(uploadedFiles);
            // 将JSON字符串写入文件
            try {
                Files.writeString(Paths.get("uploaded_files.json"), json);
                System.out.println("文件写入成功!");
                success = true;
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("文件写入失败!");
            }
        }
        ossClient.shutdown();
        return success;
    }
}

获取token和上传文件代码

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.io.IOException;

public class TestRequest {

    public static final String URI_TOKEN = "/terra-rescon-be/v2/store/obtain_token";
    private static final String ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com";
    //我的本地素材地址
    private static final String FILE_PATH = "D:\\DJ\\DJI Product\\Three-Dimension-3D\\Three-Dimension-3D\\3cm-3D-orthomosaic-70pic";

    public static void main(String[] args) throws IOException {
        boolean successUPload = isSuccessUPload();
        System.out.println("successUPload = " + successUPload);
    }

    public static boolean isSuccessUPload() throws IOException {
        //获取token
        JSONObject accessToken = AccessDJ(URI_TOKEN, "", "post");
        JSONObject dataToken = JSONUtil.parseObj(accessToken.get("data"));
        String storePath = (String) dataToken.get("storePath");
        //获取的storePath多了{fileName} 需要去掉
        storePath=storePath.substring(0,storePath.length()-10);
        //将本地素材上传到大疆oss
        return uploadFile(ENDPOINT, (String) dataToken.get("accessKeyID"), (String) dataToken.get("secretAccessKey"), (String) dataToken.get("cloudBucketName"),
                storePath, (String) dataToken.get("sessionToken"),FILE_PATH);
    }
    public static JSONObject getAccessToken() {
        return AccessDJ(URI_TOKEN, "","post");
    }
}

创建resoure

请求POST

URI=/terra-rescon-be/v2/resources

    public static final String RESOURE_PATH = "/terra-rescon-be/v2/resources";

    public static JSONObject creatRescoure(){
        //payload 设置参数 参考API文档  name: resource名称 type: 类型
        String payload="{\"name\": \"202409301736\", \"type\": \"map\"}";
            return AccessDJ(RESOURE_PATH,payload ,"post");
    }
    public static void main(String[] args) throws IOException {
        JSONObject jsonObject = creatRescoure();
        System.out.println("jsonObject = " + jsonObject);
    }

返回的JSON格式

关联文件

需要用到获取token的callbackParam,resource的uuid

 一次上传最多50个文件

    public static JSONObject associateFile(Object resource_uuid, Object callbackParam) throws IOException {
        String filesJson = Files.readString(Paths.get("uploaded_files.json"));
        JSONArray filesArray = JSONUtil.parseArray(filesJson);
        List<JSONObject> payloadJson = new ArrayList<>();
        int a=0;
        String payload="";
        for (int i = 0; i < filesArray.size(); i++) {
            payloadJson.add((JSONObject) filesArray.get(i));
            a++;
            if(a>=50){
                payload = JSONUtil.toJsonStr(Map.of("resourceUUID", resource_uuid, "callbackParam", callbackParam, "files", payloadJson));
                AccessDJ(URI_ASSOCIATE_FILE,payload , "post");
                a=0;
                payloadJson.clear();
            }
        }
        return AccessDJ(URI_ASSOCIATE_FILE,payload , "post");
    }

 创建Job 

POST请求

URI=/terra-rescon-be/v2/jobs
    public static final String CREAT_JOB_PATH = "/terra-rescon-be/v2/jobs";
    public static JSONObject creatJob(){
        //payload 设置参数 参考API文档  name: job名称
        String payload="{\"name\": \"202409301736\"}";
        return AccessDJ(CREAT_JOB_PATH,payload ,"post");
    }
    public static void main(String[] args) throws IOException {
        JSONObject jsonObject = creatJob();
        System.out.println("jsonObject = " + jsonObject);
    }

返回JSON数据:

 启动 3D 类型 job

URI=/terra-rescon-be/v2/jobs/{uuid from create job}/start

需要用到 rescoure的uuid,job的uuid

    public static final String RESOURE_PATH = "/terra-rescon-be/v2/resources";

    public static final String CREAT_JOB_PATH = "/terra-rescon-be/v2/jobs";
    public static JSONObject creatJob(){
        //payload 设置参数 参考API文档  name: job名称
        String payload="{\"name\": \"202409301736\"}";
        return AccessDJ(CREAT_JOB_PATH,payload ,"post");
    }
    public static JSONObject creatRescoure(){
        //payload 设置参数 参考API文档  name: resource名称 type: 类型
        String payload="{\"name\": \"202409301736\", \"type\": \"map\"}";
        return AccessDJ(RESOURE_PATH,payload ,"post");
    }
    public static JSONObject start3DJob(){
        JSONObject rescoureJson = creatRescoure();
        Object resource_uuid = JSONUtil.parseObj(rescoureJson.get("data")).get("uuid");
        JSONObject jobJson = creatJob();
        System.out.println("启动3Djob");
        Object job_uuid = JSONUtil.parseObj(jobJson.get("data")).get("uuid");
        String url="/terra-rescon-be/v2/jobs/"+job_uuid+"/start";
        JSONObject payload = new JSONObject();
        payload.set("type",15).set("resourceUUID",resource_uuid).set("parameters",
                "{\"parameter\":{\"output_mesh\": true,\"generate_obj\": true,\"generate_b3dm\": true,\"generate_osgb\": true}}");
        return AccessDJ(url, payload.toString(), "post");
    }
    public static void main(String[] args) throws IOException {
        JSONObject jsonObject = start3DJob();
        System.out.println("jsonObject = " + jsonObject);
    }

查询job状态

URI=/terra-rescon-be/v2/jobs/{uuid from create job}

需要job的uuid

    public static JSONObject getJobStatus(){
        JSONObject jobJson = creatJob();
        JSONObject data = JSONUtil.parseObj(jobJson.get("data"));
        Object jobUuid = data.get("uuid");
        String url="/terra-rescon-be/v2/jobs/"+jobUuid;
        return AccessDJ(url, "", "get");
    }

    public static void main(String[] args) throws IOException {
        JSONObject jobStatus = getJobStatus();
        System.out.println("jobStatus = " + jobStatus);
    }

返回的JSON结果

主要数据uuid ,errCode,status

uuid job的uuid

status 建模进度

errCode 错误码

 查看大疆智图重建错误指引.pdf (djicdn.com)

 如果status为6,执行完成,说明建模完毕。接下来下载模型文件

下载模型文件

获取文件uuid列表

URI=/terra-rescon-be/v2/resources/{uuid form check job status}

建模完成后,查询job状态,返回的Json为

type为15 表示3D建模

status为6 表示成功 

需要用到查询 job 状态返回的 outputResourceUuid

    public static JSONObject getfilelist(){
        String url="/terra-rescon-be/v2/resources/your outputResourceUuid";
        return AccessDJ(url, "", "get");
    }
    public static void main(String[] args) throws IOException {
        JSONObject getfilelist = getfilelist();
        System.out.println("getfilelist = " + getfilelist);
    }

返回的Json信息

fileUuids存放的是文件uuid

根据文件uuid下载文件

URI=/terra-rescon-be/v2/files/{file uuid}

从文件列表获取文件uuid,然后获取文件JOSN信息

    public static void main(String[] args) throws IOException {
        String uri="/terra-rescon-be/v2/files/uuid of your file";
        JSONObject fileUuidJSON = DJIAuthRequest.AccessDJ(uri, "", "get");
    }

返回的JSON信息

注意:获取文件的后缀可能不一样,不要自定义文件名称,使用需要使用到Json的name信息

 完整代码

    public void jobSuccessDownloadFile(){
        //工作成功后下载文件
        String downurl="/terra-rescon-be/v2/resources/{your outputResourceUuid}";//
        JSONObject jsonObject = AccessDJ(downurl, "", "get");
        //获取
        List<String> fileUuids = (List<String>) JSONUtil.parseObj(jsonObject.get("data")).get("fileUuids");
        for (String fileUuid :fileUuids
             ) {
            String uri="/terra-rescon-be/v2/files/"+fileUuid;
            JSONObject fileUuidJSON = AccessDJ(uri, "", "get");//获取文件信息Json
            String url = (String) JSONUtil.parseObj(fileUuidJSON.get("data")).get("url");
            // 使用HttpRequest获取文件
            HttpResponse response = HttpRequest.get(url)
                    .timeout(5000) // 设置超时时间
                    .execute();
            String savePath = "/file/"+JSONUtil.parseObj(fileUuidJSON.get("data")).get("name");//如果项目在D盘 则保存路径为D:/file/xxx.xxx
            // 判断响应状态码是否为200
            if (response.isOk()) {
                // 读取响应体到字节数组
                byte[] bytes = response.bodyBytes();
                // 将字节数组写入文件
                FileUtil.writeBytes(bytes, savePath);
                System.out.println("文件下载成功,保存路径:" + savePath);
            } else {
                System.out.println("文件下载失败,状态码:" + response.getStatus());
            }
        }
    }

流程完整代码

配好api请求路径

    private static final String ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com";//外网访问网址
    @Test
    public void test() throws IOException {
        //1. 获取token
        System.out.println("获取token");
        JSONObject accessToken = getAccessToken();
        JSONObject dataToken = JSONUtil.parseObj(accessToken.get("data"));
        Object callbackParam = dataToken.get("callbackParam");
        String storePath = (String) dataToken.get("storePath");
        storePath=storePath.substring(0,storePath.length()-10);
        
        //将本地素材上传到大疆oss
        boolean isSuccess = uploadFile(ENDPOINT, (String) dataToken.get("accessKeyID"), (String) dataToken.get("secretAccessKey"), (String) dataToken.get("cloudBucketName"),
                storePath, (String) dataToken.get("sessionToken"));
        if (isSuccess){
            //2. 创建resoure
            System.out.println("创建resoure");
            String payloadResource="{\"name\": \"name of your 创建resoure\", \"type\": \"map\"}";
            JSONObject resource = AccessDJ(URI_RESOURCE,payloadResource,METHOD_POST);
            JSONObject dataResource = JSONUtil.parseObj(resource.get("data"));
            Object uuid = dataResource.get("uuid");
            System.out.println("uuid = " + uuid);
            //3. 关联文件
            System.out.println("关联文件");
            associateFile(uuid,callbackParam);
            //创建job
            System.out.println("创建job");
            String payloadJob="{\"name\":\"name of your job\"}";
            JSONObject job = AccessDJ(URI_CREATE_JOB,payloadJob,METHOD_POST);
            //启动3Djob
            System.out.println("启动3Djob");
            JSONObject data = JSONUtil.parseObj(job.get("data"));
            Object job_uuid = data.get("uuid");
            String url="/terra-rescon-be/v2/jobs/"+job_uuid+"/start";
            JSONObject payload = new JSONObject();
            payload.set("type",15).set("resourceUUID",uuid).set("parameters",
                    "{\"parameter\":{\"output_mesh\": true,\"generate_obj\": true,\"generate_b3dm\": true,\"generate_osgb\": true}}");
            JSONObject jsonObject = AccessDJ(url, String.valueOf(payload), "post");
            System.out.println("jsonObject = " + jsonObject);
        }
        //查看job工作完成状态

        //工作成功后下载文件
    }

如果job状态已经为完成就可以进行下载,只需要运行下载模型文件的完整代码即可,记得补充信息。

大疆制模导入模型

打开大疆制模软件(需要创建账号并激活)

点击新建项目

找到文件下载的目录,下载的文件如下:

AT文件夹没有xml文件可能是配置属性的问题,这个我还没有研究。

点击确定就可以加载模型了。

大疆智图是一款专业的无人机航测软件,用于三维建模和测绘应用。以下是一个简单的教程,帮助您了解如何使用大疆智图进行三维建模: 1. 准备工作: - 确保您已经安装并正确连接了大疆无人机设备。 - 下载并安装最新版本的大疆智图软件。 2. 飞行计划: - 打开大疆智图软件,并选择“飞行计划”选项。 - 在地图中选择您要进行三维建模的区域。 - 设置飞行参数,例如航线间距、航线高度等。 - 点击“生成飞行计划”以生成飞行路线。 3. 飞行操作: - 将无人机放置在安全的起飞点,并确保周围没有障碍物。 - 启动无人机,并按照大疆智图软件上的指示完成飞行任务。 - 在飞行过程中,大疆智图将自动拍摄照片,并记录位置信息。 4. 数据处理: - 完成飞行后,将飞行记录和拍摄的照片导入大疆智图软件。 - 在软件中选择“数据处理”选项,并选择导入的数据。 - 大疆智图将自动对照片进行处理,生成三维模型和纹理贴图。 5. 模型导出: - 在数据处理完成后,您可以在大疆智图软件中查看和编辑生成的三维模型。 - 如果需要,您可以导出模型为常见的三维文件格式,如OBJ、FBX等。 - 导出后的模型可以在其他三维建模软件中进一步处理和应用。 请注意,这只是一个简单的教程概述,实际操作中可能还有其他细节和注意事项。建议您参考大疆智图的官方文档或教程以获取更详细的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值