当前位置: 首页 > news >正文

文件转换-cad转geojson

前言

基于前一篇文章GeoServer系列-通过mongodb发布geojson数据,业务上可将常见的地理文件统一为geojson保存到mongodb,方便统一维护和发布geoserver,这一篇将举例cad格式转geojson。

1,必要的依赖

  • 文件转换和解析用到了gdal,需要先下载并配置环境变量(我用的3.3),安装后可使用以下命令查看版本

    C:\Users\CDLX>gdalinfo.exe --version
    GDAL 3.3.0, released 2021/04/26

  • pom引入

    <dependencies>
        <dependency>
            <groupId>org.gdal</groupId>
            <artifactId>gdal</artifactId>
            <version>3.2.0</version>
        </dependency>
    </dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.7</version>
    </dependency>
    

2,转换入口

    /**
     * dwg转geojson
     *
     * @param fileName gdb文件名  xxx.dwg
     * @param fileMd5  文件md5值   1232131
     * @return
     */
    @Override
    public String dwgToJson(String fileName,String userId, String fileMd5) {
        String wsDirPath = fileDirectory + "/" + fileMd5 + userId;
        //文件名
        String dwgname = fileName.substring(0, fileName.lastIndexOf("."));
        String dwgpath = wsDirPath + "/" + fileName;
        //1,dwg转dxf D:\TS\temp\cadtest\cadtest.dxf
        String outFile = dwgpath.replace(".dwg", ".dxf");
        String res = dwg2dxf(dwgpath, outFile);
        if (res != null) {
            return res;
        }
        //2,dxf转换成json  D:\TS\temp\cadtest\cadtest.geojson
        String jsonPath = outFile.replace(".dxf", "转换前.geojson");
        res = dxfToJson(outFile, jsonPath);
        if (res != null) {
            return res;
        }
        //3,坐标转换  D:\TS\temp\cadtest\cadtest转换.geojson
        long begin = System.currentTimeMillis();
        String newJsonPath = jsonPath.replace("转换前.geojson", "") + ".geojson";
        res = geoJsonCov(jsonPath, newJsonPath, dwgname);
        if (res != null) {
            return res;
        }
        long end = System.currentTimeMillis();
        log.info("dwg坐标转换耗时:{}", (end - begin));
        return null;
    }

3,dwg转dxf

    public String dwg2dxf(String dwgPath, String outFile) {
        File dwgFile = new File(dwgPath);
        if (dwgFile.exists()) {
            if (!dwgFile.getName().endsWith(".dwg")) {
                return "文件格式错误";
            }
            LoadOptions loadOptions = new LoadOptions();
            loadOptions.setSpecifiedEncoding(CodePages.SimpChinese);
            CadImage cadImage = (CadImage) Image.load(dwgFile.getAbsolutePath(), loadOptions);
            cadImage.save(outFile);
            cadImage.close();
            return null;
        } else {
            return "dwg文件不存在," + dwgPath;
        }
    }

4,dwg转dxf

    /**
     * dxf转json
     *
     * @param dxfPath     D:\TS\sharding\filemd5\cadtest.dxf
     * @param geoJsonPath D:\TS\sharding\filemd5\cadtest.geojson
     */
    public String dxfToJson(String dxfPath, String geoJsonPath) {
        ogr.RegisterAll();
        //支持中文路径
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
        gdal.SetConfigOption("DXF_ENCODING", "UTF-8");

        //读取文件转化为DataSource
        DataSource ds = ogr.Open(dxfPath, 0);
        if (ds == null) {
            return "打开文件失败," + dxfPath;
        }
        //获取geojso
        Driver geojsonDriver = ogr.GetDriverByName("GeoJSON");
        DataSource geojsonDataSource = geojsonDriver.CopyDataSource(ds, geoJsonPath);
        geojsonDataSource.delete();
        ds.delete();
        return null;
    }

5,坐标转换(重点来了)

因为cad文件没有坐标系,需要预设一个坐标系,将上一步中geojson中的坐标进行转换

    /**
     * 坐标转换
     *
     * @param geoJsonPath D:\\ITS\\Sharding\filemd5\xx.geojson
     * @param outputJson  D:\\ITS\\Sharding\filemd5\xx转换.geojson
     * @param dwgName     图层名称
     */
    public String geoJsonCov(String geoJsonPath, String outputJson, String dwgName) {
        ogr.RegisterAll();
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");//支持中文路径
        gdal.SetConfigOption("DXF_ENCODING", "UTF-8");
        DataSource ds2 = ogr.Open(geoJsonPath, 0);
        System.out.println("----------坐标转换开始-----------");
        //读取第一个图层,这个图层不是cad中的layer
        Layer oLayer = ds2.GetLayerByIndex(0);
        if (oLayer == null) {
            log.error("从dwg的geojson中获取layer 获取失败:{}", geoJsonPath);
            ds2.delete();
            return "从dwg的geojson中获取layer 获取失败";
        }

        //新创建一个图层geojson
        Driver geojsonDriver = ogr.GetDriverByName("GeoJSON");
        SpatialReference epsg2000 = new SpatialReference("");
        epsg2000.ImportFromEPSG(4490);
        DataSource dataSource = geojsonDriver.CreateDataSource(outputJson, null);
        Layer geojsonLayer = dataSource.CreateLayer(dwgName, epsg2000, ogrConstants.wkbUnknown, null);

        Feature feature;
        String geometryStr;
        String newGeometryStr;
        while ((feature = oLayer.GetNextFeature()) != null) {
            //获取空间属性
            Geometry geometry = feature.GetGeometryRef();
            // 解决自相交问题
            //geometry = geometry.Buffer(0.0);
            //System.out.println(geometry.ExportToJson());
            geometryStr = geometry.GetCurveGeometry().ExportToJson();
            if (geometryStr == null) {
                continue;
            }
            newGeometryStr = String.valueOf(paseCoordinates(geometryStr));
            Geometry newGeometry = Geometry.CreateFromJson(newGeometryStr);
            //传给新建的矢量图层
            Feature dstFeature = feature.Clone();
            dstFeature.SetGeometry(newGeometry);
            geojsonLayer.CreateFeature(dstFeature);
        }
        geojsonDriver.delete();
        ds2.delete();
        dataSource.delete();
        return null;
    }

6,一个朴实无华的逐行坐标转换

逐行读取geojson的Feature,遍历每个坐标

    /**
     * 转换每个Feature的坐标
     * @param coordinatesStr Feature原始json字符串
     * @return
     */
    public StringBuffer paseCoordinates(String coordinatesStr) {
        //String a = "{ \"type\": \"Feature\", \"id\": 0, \"properties\": { \"Layer\": \"SXSS\", \"SubClasses\": \"AcDbEntity:AcDbBlockReference\", \"Linetype\": \"Continuous\", \"EntityHandle\": \"4F2\" }, \"geometry\": { \"type\": \"MultiLineString\", \"coordinates\": [ [ [ 406567.373450083076023, 3058272.568403942044824 ], [ 406565.039480640378315, 3058269.633257158566266 ] ], [ [ 406565.039480640378315, 3058269.633257158566266 ], [ 406565.597624949878082, 3058269.85315527068451 ] ], [ [ 406565.039480640378315, 3058269.633257158566266 ], [ 406565.128001464472618, 3058270.226590381469578 ] ] ] } }";
        int offset = coordinatesStr.indexOf("coordinates\":") + 13;
        String b = coordinatesStr.substring(offset);
        //System.out.println(b);
        String[] c = b.split("\\],");
        StringBuffer stringBuffer = new StringBuffer();
        int n = c.length;
        for (String d : c) {
            //System.out.println(d);
            int begin = d.lastIndexOf("[");
            int end = d.indexOf("]");
            String oldZb = d.substring(begin < 0 ? 0 : begin + 1, end < 0 ? d.length() - 1 : end - 1);
            //System.out.println(oldZb);
            String newZb = CadUtil.covStr(oldZb);
            //System.out.println(newZb);
            if (begin > 0) {
                stringBuffer.append(d, 0, begin + 1).append(newZb);
            }
            if (n != 1) {
                stringBuffer.append("]");
            }
            if (end > 0) {
                stringBuffer.append(d.substring(end - 1));
            }
            if (n > 1) {
                stringBuffer.append(",");
            }
            n--;
        }
        stringBuffer.insert(0, coordinatesStr.substring(0, offset));
        return stringBuffer;
    }

纠正经纬度颠倒问题

    public static String covStr(String zb) {
        String[] poi = zb.trim().split(",");
        double x = Double.parseDouble(poi[0]);
        double y = Double.parseDouble(poi[1]);
        //坐标经纬度交换
        if (x > y) {
            return dxfToSwapxy(x, y);
        } else {
            return dxfToSwapxy(y, x);
        }
    }

硬菜(x_x)

    /**
     * 坐标转换
     *
     * @param X
     * @param Y
     * @return
     */
    public static String dxfToSwapxy(double X, double Y) {
        double lat, lon;
        //中央子午线,结合这个网址进行查找,目前用的EPSG:4532中的123D。https://wenku.baidu.com/view/e219e246e3bd960590c69ec3d5bbfd0a7856d530.html
        double L0 = 105;
        Y -= 500000;
        double[] result = new double[2];
        double iPI = 0.0174532925199433;//pi/180
        double a = 6378137.0; //长半轴 m
        double b = 6356752.31414; //短半轴 m
        //扁率 a-b/a
        double e = 0.0818191910428; //第一偏心率 Math.sqrt(5)
        double ee = Math.sqrt(a * a - b * b) / b; //第二偏心率
        double bf = 0; //底点纬度
        double a0 = 1 + (3 * e * e / 4) + (45 * e * e * e * e / 64) + (175 * e * e * e * e * e * e / 256) + (11025 * e * e * e * e * e * e * e * e / 16384) + (43659 * e * e * e * e * e * e * e * e * e * e / 65536);
        double b0 = X / (a * (1 - e * e) * a0);
        double c1 = 3 * e * e / 8 + 3 * e * e * e * e / 16 + 213 * e * e * e * e * e * e / 2048 + 255 * e * e * e * e * e * e * e * e / 4096;
        double c2 = 21 * e * e * e * e / 256 + 21 * e * e * e * e * e * e / 256 + 533 * e * e * e * e * e * e * e * e / 8192;
        double c3 = 151 * e * e * e * e * e * e * e * e / 6144 + 151 * e * e * e * e * e * e * e * e / 4096;
        double c4 = 1097 * e * e * e * e * e * e * e * e / 131072;
        bf = b0 + c1 * Math.sin(2 * b0) + c2 * Math.sin(4 * b0) + c3 * Math.sin(6 * b0) + c4 * Math.sin(8 * b0); // bf =b0+c1*sin2b0 + c2*sin4b0 + c3*sin6b0 +c4*sin8b0 +...
        double tf = Math.tan(bf);
        double n2 = ee * ee * Math.cos(bf) * Math.cos(bf); //第二偏心率平方成bf余弦平方
        double c = a * a / b;
        double v = Math.sqrt(1 + ee * ee * Math.cos(bf) * Math.cos(bf));
        double mf = c / (v * v * v); //子午圈半径
        double nf = c / v;//卯酉圈半径

        //纬度计算
        lat = bf - (tf / (2 * mf) * Y) * (Y / nf) * (1 - 1 / 12 * (5 + 3 * tf * tf + n2 - 9 * n2 * tf * tf) * (Y * Y / (nf * nf)) + 1 / 360 * (61 + 90 * tf * tf + 45 * tf * tf * tf * tf) * (Y * Y * Y * Y / (nf * nf * nf * nf)));
        //经度偏差
        lon = 1 / (nf * Math.cos(bf)) * Y - (1 / (6 * nf * nf * nf * Math.cos(bf))) * (1 + 2 * tf * tf + n2) * Y * Y * Y + (1 / (120 * nf * nf * nf * nf * nf * Math.cos(bf))) * (5 + 28 * tf * tf + 24 * tf * tf * tf * tf) * Y * Y * Y * Y * Y;
        result[0] = retain6(lat / iPI);
        result[1] = retain6(L0 + lon / iPI);
        return result[1] + "," + result[0];
    }
    
     public static double retain6(double num) {
        String result = String.format("%.10f", num);//小数点后面保留10位小数
        return Double.valueOf(result);
    }

相关文章:

  • 文件操作编程
  • 开发脚手架常用的JS库
  • 设备树dtb文件的格式分析
  • CNN PyTorch MNIST
  • python的简单读写操作
  • 面试官:你做过什么有亮点的项目吗?
  • 面试官:了解mysql的架构体系吗?
  • 【LINUX】初识文件系统
  • ChatGPT 各类 Prompt 整理
  • 《Spring系列》第2章 解析XML获取Bean
  • JVM知识点
  • 有限状态机设计及function、task对比
  • 2023年广东省网络安全竞赛——Linux 操作系统渗透解析(超级详细)
  • 算法61天|图论
  • Linux常用命令之文件搜索命令
  • .pyc 想到的一些问题
  • [LeetCode] Wiggle Sort
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • DOM的那些事
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • Fundebug计费标准解释:事件数是如何定义的?
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • js继承的实现方法
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Python 基础起步 (十) 什么叫函数?
  • vue-loader 源码解析系列之 selector
  • 创建一个Struts2项目maven 方式
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 在Docker Swarm上部署Apache Storm:第1部分
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • (a /b)*c的值
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (一)认识微服务
  • (转)jdk与jre的区别
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (转)可以带来幸福的一本书
  • .a文件和.so文件
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .Net FrameWork总结
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 的程序集加载上下文
  • .NET 某和OA办公系统全局绕过漏洞分析
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .net经典笔试题
  • .NET中统一的存储过程调用方法(收藏)
  • @angular/cli项目构建--Dynamic.Form
  • @NoArgsConstructor和@AllArgsConstructor,@Builder
  • [1525]字符统计2 (哈希)SDUT