[GIS] How to synchronize two maps

cesiumol-cesiumopenlayers

I have my main map using projection EPSG:4326.

Now I try to add a 3D view using ol-cesium. The goal is to create a side-by-side map that will show a flat map and a 3D view map (like this). But I can't use my map as the flat map because ol-cesium only work with XYZ Tile layer using EPSG:3857 and does not allow reprojection.

To work around I created a second map pointing to OSM layer to use as the flat map and show the side-by-side maps as a dialog box in my system.

I don't think the result was very nice and want to find a way to show only the 3D map in the dialog box and synchronize the events of the main map (pan, zoom, rotate) with it.

How can I do this?

Here you can see a screnshot of my system. The main map at background and the dialog box with the side-by-side maps (3D and flat). I need to use only the 3D map synchronized with the background map. To do this I must find a way to catch all events (pan, zoom, rotate) of the main map and send to the 3D map.

enter image description here

This is the main map definition:

        MCLM.Map.map = new ol.Map({
            layers: [ MCLM.Map.baseLayer ],
            target: container,
            renderer: 'canvas',
            loadTilesWhileAnimating: true,
            loadTilesWhileInteracting: true,    
            controls: ol.control.defaults().extend([
               new ol.control.FullScreen(),  
               new ol.control.ScaleLine(),
               new ol.control.ZoomSlider(),
               new ol.control.MousePosition({
                   undefinedHTML: '',
                   projection: 'EPSG:4326',
                   coordinateFormat: function(coordinate) {
                       var coord = ol.coordinate.toStringHDMS( coordinate );

                       var template = '{y}  {x}';
                       var mapCoord = ol.coordinate.format(coordinate, template, 6);

                       var lat = coordinate[0];
                       var lon = coordinate[1];                        
                       var utmCoord = MCLM.Functions.latLonToUTM( lon, lat );

                       MCLM.Map.centerHDMS = coord;

                       $("#coord_map").html( mapCoord );
                       $("#coord_hdms").html( coord );
                       $("#coord_utm").html( utmCoord );

                       return "";
                   }
               }),
               new ol.control.OverviewMap({className: 'ol-overviewmap ol-custom-overviewmap'}),
               new ol.control.Rotate({
                   autoHide: false
               }),                      
            ]),

            interactions: ol.interaction.defaults({
                  shiftDragZoom: false
            }).extend([
                new ol.interaction.DragRotateAndZoom(),
                new ol.interaction.DragZoom({
                    duration: 200,
                    condition: function(mapBrowserEvent) {
                        var originalEvent = mapBrowserEvent.originalEvent;
                        return ( originalEvent.ctrlKey && !(originalEvent.metaKey || originalEvent.altKey) && !originalEvent.shiftKey);
                    }
                })
            ]),             

            view: MCLM.Map.theView
        }); 

and this is the side-by-site maps (3D and flat):

    var theView = new ol.View({
        center: ol.proj.transform( MCLM.Map.arrayMapCenter , 'EPSG:4326', 'EPSG:3857'),
        zoom: MCLM.Map.mapZoom
    });     



    var ddMap = new ol.Map({
        layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ],
        target: 'ddMap',
        view : theView,
        renderer: 'webgl',
        controls: ol.control.defaults().extend([
           new ol.control.Rotate({
               autoHide: false
           })                                                   
        ]),
        interactions: ol.interaction.defaults({
              shiftDragZoom: false
        }).extend([
            new ol.interaction.DragRotateAndZoom(),
            new ol.interaction.DragZoom({
                duration: 200,
                condition: function(mapBrowserEvent) {
                    var originalEvent = mapBrowserEvent.originalEvent;
                    return ( originalEvent.ctrlKey && !(originalEvent.metaKey || originalEvent.altKey) && !originalEvent.shiftKey);
                }
            })
        ]),             
    });     


    var tdMap = new ol.Map({
        layers: [ MCLM.Map.imageryMap ],
        target: 'tdMap',
        view : theView,
        renderer: 'webgl'
    });


    var ol3d = new olcs.OLCesium({map: tdMap});


    var scene = ol3d.getCesiumScene();
    scene.terrainProvider = new Cesium.CesiumTerrainProvider({
        url: 'https://assets.agi.com/stk-terrain/world'
    });
    ol3d.setEnabled(true);      

as you can see, I created the ddmap (flat map in right side of the dialog) to allow the user to interact with the 3D map ( tdmap in left side of the dialog box) because I can't use my main map in ol-cesium. I don't liked this solution.


Almost there… I found this: https://stackoverflow.com/questions/32659225/openlayers-3-sync-unsync-different-maps-events and trying to adapt to my needs.

In the code above I made the follow changes:

    ...
    var theView = new ol.View({
        center: ol.proj.transform( MCLM.Map.arrayMapCenter , 'EPSG:4326', 'EPSG:3857'),
        zoom: MCLM.Map.mapZoom
    });     

    MCLM.Globals.tdView = theView; // <<<-- SAVE THE VIEW TO FUTURE USE

    var ddMap = new ol.Map({
        layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ],
        target: 'ddMap',
        view : theView,
        renderer: 'webgl',
        ...

and then:

    ...
    cam.setTilt(1.0442318918054133);        

    // BIND THE MAIN MAP EVENTS TO A LOCAL FUNCTION
    MCLM.Map.theView.on('change:center', this.syncMaps);
    MCLM.Map.theView.on('change:rotation', this.syncMaps);
    MCLM.Map.theView.on('change:resolution', this.syncMaps);

},

and then create this function:

syncMaps = function( evt ) {
    var type = evt.type.split(':');
    if (type[0] === 'change' && type.length === 2) {
        var attribute = type[1];
        var value = evt.target.get(attribute);

        console.log( attribute + " = " + value );

        // The trick is to change the projection of the event value
        if ( attribute == 'center' ) {
            value = ol.proj.transform( value , 'EPSG:4326', 'EPSG:3857');
        }

        // NOW I'M IN TROUBLE HERE!!! 
        if ( attribute == 'resolution') {
            // How to change the resolution ?
        }

        if ( attribute == 'rotation') {
            // How to change the resolution ?
        }

        // Send the event to the 3D map view
        MCLM.Globals.tdView.set(attribute, value);

    }
}

As you can see I solved the pan problem by changing the resolution comming from the main map (EPSG:4326) to the 3D map projection (EPSG:3857) when dragging it.

But when I change the zoom level or rotate the map the main map send this events (and values like this for EPSG:4326):

resolution = 0.005609331367378474 
rotation = 0.007291826612678465

How can I change this values to EPSG:3857 projection?

Best Answer

Don't need to mess with resolutions. Just get the zoom level from the main map and pass it right to the 3D map. For the rotation, It is an angle so there is no need to change it!

Final solution:

        // DRAG: Need to reproject the center.
        if ( attribute == 'center' ) {
            value = ol.proj.transform( value , 'EPSG:4326', 'EPSG:3857');
            MCLM.Globals.tdView.set( attribute, value );
        }

        // Change Zoom: Don't care about the resolution. Use the Zoom level!
        if ( attribute == 'resolution') {
            // Get the main map zoom level...
            var mapZoom = MCLM.Map.map.getView().getZoom();
            // ... and send it to the 3D map view.
            MCLM.Globals.tdView.setZoom( mapZoom );
        }

        // Rotate: Don't care! pass it thru the 3D map as is. 
        if ( attribute == 'rotation') {
            MCLM.Globals.tdView.set( attribute, value );
        }

        // Other events are not processed.
Related Question