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.
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?