# Cesium and CAD Overlay Display

Cesium is an excellent 3D Earth GIS engine, open source and free. It can load various standard map layers, including tile maps, vector maps, and more. In practice, there is often a need to overlay CAD drawings on Cesium and display them together with terrain. So how do you overlay and analyze CAD DWG drawings in Cesium?

# Technical Analysis

The common industry approach is:

  • (1) Export DWG to DXF plain text format in AutoCAD
  • (2) Use third-party tools such as GDAL to convert DXF to GIS formats like SHP
  • (3) Use open-source GIS services such as GeoServer or MapServer to publish SHP files as GIS services
  • (4) Load GIS service tile layers in Cesium

The advantages of the above approach are obvious—basically the workflow can be completed using open-source solutions. But in actual projects the disadvantages are also clear, mainly in steps (1) and (2). Because DWG is a proprietary format, converting DWG to DXF and then to SHP loses much of the data in the drawing. CAD supports many data types: points, block symbols, lines, polygons, polylines, ellipses, blocks, text, etc. When converting to GIS, these become only points, lines, polygons, and annotations. This makes CAD graphic data unable to fully meet GIS requirements. For example, CAD Text data is converted only to GIS Point type. Drawing from such conversions can differ from the original CAD drawing. In addition, CAD line types and fonts can cause rendering differences.

VJMap fully supports AutoCAD DWG format files. No conversion is needed; they can be published directly as GIS WMS format, effectively solving the above problems. The implementation steps are:

  • (1) Upload DWG drawings and publish them as WMS layers using VJMap
  • (2) Load WMS tile layers in Cesium

# Implementation

First, the effect (red lines are the CAD layer):

cesiumcad.gif

# Steps:

  • Determine the CAD drawing coordinate system

    In the opened CAD drawing, pick any point. The coordinates are "614802.89177, 3257472.36997"

    How do you determine which coordinate system this belongs to? If you already know the coordinates, can you infer the coordinate system?

    You can use the Determining Coordinate System from CAD Coordinates tool. Or use the following method. Here we only discuss Gauss-Krüger projection.

    Assuming the projection is known to be Gauss-Krüger, how do you determine whether it is 6° or 3° zone? How do you determine the central meridian?

    If the coordinate range has 6 digits, the coordinate system likely has no zone number. If the Y value is in the range 333000m–667000m, it may be 3° zone; if Y is in 166000m–834000m, it is definitely 6° zone. If the range overlaps, it could be either 3° or 6° zone.

    If the coordinate range has 8 digits, the coordinate system uses zone numbers. Based on the zone number you can tell 3° or 6° zone. China's longitude is roughly 73°33′E–105°05′E, so if zone numbers are used: zone numbers 13–23 indicate 6° zone; zone numbers 25–45 indicate 3° zone.

    In this drawing, the coordinate "614802" has 6 digits, so it is likely a 3° zone coordinate with the zone number omitted. The map frame indicates the 2000 coordinate system, and the location is near Chongqing. Chongqing's longitude is around 106. Querying the EPSG website shows this coordinate system should be "EPSG:4544".

    image-20220801192221302

  • Upload and open the CAD drawing

    Upload the CAD drawing at VJMap Cloud Drawing Management Platform (opens new window).

  • Add the CAD layer in Cesium

    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, // show animation widget
    			baseLayerPicker: false, // show layer picker
    			geocoder: false, // show geocoder
    			timeline: false, // show timeline
    			sceneModePicker: false, // show scene mode picker
    			navigationHelpButton: false, // show navigation help
    			infoBox: false, // show click info box
    			fullscreenButton: false,
    			shouldAnimate: true // animate
    		});
    
    		var layers = viewer.scene.imageryLayers;
    
    		layers.addImageryProvider(
    			new Cesium.WebMapServiceImageryProvider({
    			    // Load CAD via vjmap WMS
    				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`,
    			})
    		);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

The code above seems straightforward, but in practice it was found that in Cesium the CAD can overlay with Tianditu imagery. However, when zooming in, the WMS-generated tiles had misalignment issues, as shown below:

image-20220801193313571

# Problem Analysis

In Cesium, the coordinate system is WGS84, while the CAD drawing uses EPSG:4544. Conversion between two different ellipsoids in WMS tile generation is based on coordinate transformation. Because coordinate transformation is nonlinear, the generated image content cannot align perfectly. Hence the misalignment.

How to fix it?

The final solution is:

  • First, use four-parameter transformation to compute the conversion parameters between CAD and Mercator 3857

  • Then convert Mercator 3857 to WGS84

    With this approach, the data conversion becomes linear, eliminating the misalignment.

    Code:

    // Load CAD in Cesium (WMS layer)
    // Map service
    let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
    // Open map
    let mapId = "sys_cadcesium";
    let res = await svc.openMap({
        mapid: mapId, // map ID
        mapopenway: vjmap.MapOpenWay.GeomRender, // geometry render
        style: vjmap.openMapDarkStyle() // use dark style when container has dark background
    })
    if (res.error) {
        // open failed
        message.error(res.error)
    }
    let layer = res.layer;// style layer name
    
    let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
    let boundsArray = mapBounds.toPointArray();
    
    // Mercator points after transform
    let mktPoints = await svc.cmdTransform("EPSG:4544", "EPSG:3857", boundsArray.map(a => vjmap.geoPoint(a)));
    // CAD point coords
    let cadPoints = [...boundsArray];
    // Solve four-parameter from point pairs
    let fourparam = vjmap.coordTransfromGetFourParamter(
        mktPoints.map(a => vjmap.geoPoint(a)),
        cadPoints.map(a => vjmap.geoPoint(a)),
        true
    );
    
    if (typeof Cesium !== "object") {
        // Load Cesium if not present
        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, // show animation widget
        baseLayerPicker: false, // show layer picker
        geocoder: false, // show geocoder
        timeline: false, // show timeline
        sceneModePicker: false, // show scene mode picker
        navigationHelpButton: false, // show navigation help
        infoBox: false, // show click info box
        fullscreenButton: false,
        shouldAnimate: true // animate
    });
    
    
    var layers = viewer.scene.imageryLayers;
    
    // Add Amap annotation layer
    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",
        })
    );
    
    
    // Add CAD WMS layer
    let wmsUrl = svc.wmsTileUrl({
        mapid: mapId, // map id
        layers: layer, // layer name
        bbox: '', // Cesium fills bbox automatically
        srs: "EPSG:4326", // Cesium is WGS84
        crs: "EPSG:3857", // WGS84 -> 3857 first
        fourParameter: [fourparam.dx,fourparam.dy,fourparam.scale,fourparam.rotate] // then four-param to CAD
    })
    layers.addImageryProvider(
        new Cesium.WebMapServiceImageryProvider({
            url: wmsUrl,
        })
    );
    
    
    // CAD coords -> WGS84
    const cadToWebCoordinate = point => {
        // Four-param inverse to Mercator 3857, then 3857 -> WGS84
        // Inverse needed: four-param above is 3857->CAD; here we need CAD->3857
        let ptMkt = vjmap.coordTransfromByInvFourParamter(vjmap.geoPoint(point), fourparam);
        return vjmap.Projection.mercator2LngLat(ptMkt);// Mercator 3857 -> lng/lat
    }
    // WGS84 -> CAD coords
    const webTocadCoordinate = point => {
        // WGS84 -> Mercator 3857, then four-param to CAD
        let ptMkt = vjmap.Projection.lngLat2Mercator(vjmap.geoPoint(point));
        return vjmap.coordTransfromByFourParamter(ptMkt, fourparam);
    }
    // CAD center -> WGS84 center
    let cadCenter = mapBounds.center();
    let webCenter = cadToWebCoordinate(cadCenter);
    // Set initial camera
    viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(webCenter[0], webCenter[1], 30000)
    });
    
    // To query CAD entities by map coords, use svc.rectQueryFeature with a CAD bounds (two corner points).
    // Use webTocadCoordinate to convert WGS84 to CAD coords for the query.
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118

    For data confidentiality, the CAD drawing was desensitized. The overlay effect is as follows:

    image-20220801194426286

Visit https://vjmap.com/demo/#/demo/map/web/07cesiumCadWmsLayer (opens new window) to see the effect and code