草庐IT

在Cesium中实现与CAD的DWG图叠加显示分析

vjmap 2023-03-28 原文

前言

Cesium是一个非常优秀的三维地球GIS引擎(开源且免费, 能够加载各种符合标准的地图图层,瓦片图、矢量图等都支持。而AutoCAD是为微型计算机上应用CAD技术而开发的绘图程序,它有很强的图形编辑能力,非常适合绘制工程图纸,绘图的精确性和绘图方法的丰富性使得它在机械、电子、建筑、航空航天等领域有着广泛的应用,常见的一些工程图纸基本上都是AutoCAD绘制的DWG格式的图纸;在实际中经常会遇到需要将CAD的图纸叠加到cesium上面,与地表地形叠加显示查看。那如何实现在Cesium中实现与CAD的DWG图叠加显示分析呢?

技术分析

业内一般的做法步骤为:

  • (1) 在AutoCAD中把DWG图导出为DXF明码格式文件

  • (2) 利用第三方工具如GDAL,把dxf转为shp等GIS数据格式

  • (3) 利用开源的gis服务,如geoserver或mapserver把shp文件发布成gis服务

  • (4) 在cesium中加载gis服务的瓦片图层

上面的方案的优点很明显,基本上利用开源的方案能把流程跑通。但在实际项目中缺点也很明显,主要是在步骤 (1)(2), 因为dwg是私有格式,通过dwg转出成dxf再转化成shp文件时,会丢失图中的很多数据,CAD数据类型较为丰富,支持点、块符号、线、面、多段线、椭圆、块、文字等多种数据类型,而转换到GIS中,只转换为点、线、面、注记等类型,这使得CAD图形数据不能很好的满足GIS的要求,如:CAD中的Text数据类型,直接转换后只转换为GIS中的Point。通过这种转换去绘制,会导致和原始CAD图形绘制不太一样, 同时CAD里面有线型、字体也会导致绘制上的差异性。

唯杰地图vjmap完全兼容AutoCAD格式的DWG文件,无需通过转换可直接发布成gis的WMS格式,可有效解决上面问题。实现步骤为:

  • (1) 上传DWG图形,利用唯杰地图发布为WMS图层

  • (2) 在cesium中加载wms瓦片图层

实现

先上效果图,(红色的线为cad图层)

 

具体步骤:

  • 确定CAD图的坐标系

    在打开的CAD图中,随便找了图中的一个点坐标,坐标为”614802.89177, 3257472.36997”

    那怎么确定这个坐标是哪个坐标系呢? 如果已经知道坐标能不能倒推坐标系呢?

    可以,但是得分情况。这里我们只讨论高斯克吕格投影。

    假设已经知道数据的投影是高斯克吕格,怎样判定是6°带还是3°带?怎样判定中央经线是多少呢?

    如果坐标范围是6位,我们可以判定坐标系不带带号的。如果Y值范围在333000m~667000m,可能是3°分带法;如果Y值范围在166 000m~834000m,一定是6°分带法。如果取值范围重合的部分有可能3°分带法也可能在6°分带法。

    如果坐标范围是8位,我们可以判定坐标系是以带号命名,并且根据带号可以知道是3°带还是6°带。我们国家的经度范围大致在73°33′E~105°05′E,所以如果是以带号命名,带号在13~23带之间的,说明是6°分带法;带号在25~45带之间的,说明是3°分带法。

    在这个图中,坐标为”614802“为6位,大概率能确定为省去了带系的3°分带坐标。并且根据图中图框中坐标系写的为2000坐标系,同时地点为重庆附近。而重庆的经度差不多是以106开头。我们去epsg官网查询得知,该坐标系应该是"EPSG:4544".

  • 上传打开CAD图

    如果在Web网页端展示CAD图形(唯杰地图云端图纸管理平台 https://vjmap.com/app/cloud),这个在前面的博文中已讲过,这里不再重复,有需要的朋友可下载工程源代码研究下。

  • 在cesium增加cad图层

let imageryProvider= new Cesium.WebMapTileServiceImageryProvider({
            url: "http://t0.tianditu.gov.cn/img_w/wmts?tk=3346bb6ad29b5013c5952cf1117b80e9",
            layer: "img",
            style: "default",
            tileMatrixSetID: "w",
            format: "tiles",
            maximumLevel: 14,
        });
​
        let viewer = new Cesium.Viewer('cesiumContainer', {
            imageryProvider: imageryProvider,
            contextOptions: {
                webgl: {
                    alpha: true
                }
            },
            selectionIndicator: false,
            animation: false, //是否显示动画控件
            baseLayerPicker: false, //是否显示图层选择控件
            geocoder: false, //是否显示地名查找控件
            timeline: false, //是否显示时间线控件
            sceneModePicker: false, //是否显示投影方式控件
            navigationHelpButton: false, //是否显示帮助信息控件
            infoBox: false, //是否显示点击要素之后显示的信息
            fullscreenButton: false,
            shouldAnimate: true //动画播放
        });
​
        var layers = viewer.scene.imageryLayers;
​
        layers.addImageryProvider(
            new Cesium.WebMapServiceImageryProvider({
                // 通过vjmap的wms加载cad图
                url: `https://vjmap.com/server/api/v1/map/cmd/wms/
sys_cadcesium/v1?mapbounds=&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:4326&transparent=true&width=256&height=256&layers=se92105f73&crs=EPSG:4544&fourParameter=&token=your token`,
            })
        );

  

上面的代码好像简单明了,可实际中发现,在cesium中,cad可以和天地图影像叠加。但在放大的过程中,发现wms生成的瓦片有错开的问题。如下图所示:

问题分析

在cesium中,是WGS84坐标系,而CAD图是4544坐标系。两个不同的椭球体之间转换,在WMS中瓦片生成是根据坐标转换生成的瓦片数据,而由于坐标转换是非线性的,导致生成的图片内容不能完全对上。所以有错开的问题。

那怎么解决呢?

最终的方案是:

  • 先通过四参数,把CAD与墨卡托3857的转换参数求出来

  • 再墨卡托3857转wgs4326

    通过这样的转换,数据转换是线性转换了,就不会有错开的问题了。

    具体代码如下:

    // --Cesium中加载CAD图(WMS图层)--
    // 地图服务对象
    let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
    // 打开地图
    let mapId = "sys_cadcesium";
    let res = await svc.openMap({
        mapid: mapId, // 地图ID
        mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
        style: vjmap.openMapDarkStyle() // div为深色背景颜色时,这里也传深色背景样式
    })
    if (res.error) {
        // 如果打开出错
        message.error(res.error)
    }
    let layer = res.layer;//图层样式名
    ​
    let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
    let boundsArray = mapBounds.toPointArray();
    ​
    // 得到坐标转换后的墨卡托点
    let mktPoints = await svc.cmdTransform("EPSG:4544", "EPSG:3857", boundsArray.map(a => vjmap.geoPoint(a)));
    // cad上面的点坐标
    let cadPoints = [...boundsArray];
    // 通过坐标参数求出四参数
    let fourparam = vjmap.coordTransfromGetFourParamter(
        mktPoints.map(a => vjmap.geoPoint(a)),
        cadPoints.map(a => vjmap.geoPoint(a)),
        true
    );
    ​
    if (typeof Cesium !== "object") {
        // 如果没有环境
        await vjmap.addScript([{
            src: "js/Cesium/Cesium.js"
        }, {
            src: "js/Cesium/Widgets/widgets.css"
        }])
    }
    ​
    let imageryProvider= new Cesium.WebMapTileServiceImageryProvider({
        url: "https://t0.tianditu.gov.cn/img_w/wmts?tk=3346bb6ad29b5013c5952cf1117b80e9",
        layer: "img",
        style: "default",
        tileMatrixSetID: "w",
        format: "tiles",
        maximumLevel: 14,
    });
    ​
    let viewer = new Cesium.Viewer('map', {
        imageryProvider: imageryProvider,
        contextOptions: {
            webgl: {
                alpha: true
            }
        },
        selectionIndicator: false,
        animation: false, //是否显示动画控件
        baseLayerPicker: false, //是否显示图层选择控件
        geocoder: false, //是否显示地名查找控件
        timeline: false, //是否显示时间线控件
        sceneModePicker: false, //是否显示投影方式控件
        navigationHelpButton: false, //是否显示帮助信息控件
        infoBox: false, //是否显示点击要素之后显示的信息
        fullscreenButton: false,
        shouldAnimate: true //动画播放
    });
    ​
    ​
    var layers = viewer.scene.imageryLayers;
    ​
    // 加一个高德注记图层
    layers.addImageryProvider(
        new Cesium.UrlTemplateImageryProvider({
            url: "https://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
        })
    );
    ​
    ​
    // 增加cad的wms图层
    let wmsUrl = svc.wmsTileUrl({
        mapid: mapId, // 地图id
        layers: layer, // 图层名称
        bbox: '', // bbox这里不需要传,cesium会自动加上
        srs: "EPSG:4326", // cesium地图是wgs84
        crs: "EPSG:3857", // 先把wgs84转3857
        fourParameter: [fourparam.dx,fourparam.dy,fourparam.scale,fourparam.rotate] // 转成 3857后,再用四参数去转成cad坐标
    })
    layers.addImageryProvider(
        new Cesium.WebMapServiceImageryProvider({
            url: wmsUrl,
        })
    );
    ​
    ​
    // cad图坐标转web wgs84坐标
    const cadToWebCoordinate = point => {
        // 先用四参数转成墨卡托3857,再墨卡托3857转wgs84
        // 这里需要用四参数反算,因为上面的四参数是3857转cad的,这里是反过来,要cad转3857,所以需要反算
        let ptMkt = vjmap.coordTransfromByInvFourParamter(vjmap.geoPoint(point), fourparam);
        return vjmap.Projection.mercator2LngLat(ptMkt);// 再墨卡托3857转经纬度
    }
    // 转web wgs84坐标转cad图坐标
    const webTocadCoordinate = point => {
        // 先wgs84转墨卡托3857,再用四参数转成cad
        let ptMkt = vjmap.Projection.lngLat2Mercator(vjmap.geoPoint(point));
        return vjmap.coordTransfromByFourParamter(ptMkt, fourparam);
    }
    // 根据cad图的中心点,计算wgs84的中心点坐标
    let cadCenter = mapBounds.center();
    let webCenter = cadToWebCoordinate(cadCenter);
    //设置初始位置
    viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(webCenter[0], webCenter[1], 30000)
    });
    ​
    // 如果需要在地图上查询cad的实体坐标,可通过svc.rectQueryFeature来实现,需要传入两个cad的点坐标范围
    // 可以通过 webTocadCoordinate 接口把wgs84的坐标转成 cad 的坐标去查询.

     


    出于对数据保密的需要,我们对CAD图进行了脱敏处理,叠加的效果如下

上面的案例代码已开源。访问 (https://vjmap.com/demo/#/demo/map/web/07cesiumCadWmsLayer) ,查看效果和代码即可。

有关在Cesium中实现与CAD的DWG图叠加显示分析的更多相关文章

  1. ruby - 在 Ruby 中实现 `call_user_func_array` - 2

    我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)

  2. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  5. ruby-on-rails - link_to 不显示任何 rails - 2

    我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article

  6. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  7. ruby-on-rails - 如何在 Ruby on Rails 中实现无向图? - 2

    我需要在RubyonRails中实现无向图G=(V,E)并考虑构建一个Vertex和一个Edge模型,其中Vertex有_多条边。由于边恰好连接两个顶点,您将如何在Rails中执行此操作?您是否知道任何有助于实现此类图表的gem或库(对重新发明轮子不感兴趣;-))? 最佳答案 不知道有任何现有库在ActiveRecord之上提供图形逻辑。您可能必须实现自己的Vertex、EdgeActiveRecord支持的模型(请参阅Rails安装的rails/activerecord中的vertex.rb和edge.rb/test/fixtur

  8. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  9. ruby-on-rails - 复数 for fields_for has_many 关联未显示在 View 中 - 2

    目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi

  10. ruby-on-rails - 在 Flash 警报 Rails 3 中显示错误消息 - 2

    如果我在模型中设置验证消息validates:name,:presence=>{:message=>'Thenamecantbeblank.'}我如何让该消息显示在闪光警报中,这是我迄今为止尝试过的方法defcreate@message=Message.new(params[:message])if@message.valid?ContactMailer.send_mail(@message).deliverredirect_to(root_path,:notice=>"Thanksforyourmessage,Iwillbeintouchsoon")elseflash[:error]

随机推荐