2

I'm currently making a demonstration to compare the two OpenLayers libraries (2 and 3). For doing this, I have made two div elements and put the different maps on each other. The goal is to make a map which starts in the first div and continues in the second div, fluently. The result can be seen on the following fiddle: http://jsfiddle.net/GFarkas/xxqu97nc/.

To synchronize the two maps, I have to bind events to each one and pass the corresponding center coordinates to the other map.

My basic concept is, that if we want to get the maps aligned, they have to share lower left coordinates (because one of the divs is smaller than the other). Because they are the same height, the latitude won't change, it can be passed from the event object. However, because of the difference in their width, the longitude have to be calculated. As they have to share the same lower left coordinates, the center can be calculated by averaging the longitude at the sides of the corresponding bounding box:

view.setCenter([(map.getLonLatFromPixel(new OpenLayers.Pixel([0,0])).lon +
map.getLonLatFromPixel(new OpenLayers.Pixel([map2.getSize(),0])).lon)/2,
evt.object.center.lat]);

to change the OpenLayers 3 div, and

map.setCenter([(map2.getCoordinateFromPixel([0, 0])[0] + 
map2.getCoordinateFromPixel([640, 0])[0])/2, 
evt.map.getView().getCenter()[1]], evt.map.getView().getZoom());

to change the OpenLayers 2 one.

This method works as intended until I get near to the date line (where the positive and the negative longitudes meet as we're in Pseudo Mercator). When I get between the -19000000m and 20000000m longitudes, this simple equation to calculate the center coordinates fails and my view goes near to the origin.

The question is, does anybody have a better and safer method to calculate coordinates to synchronize the two maps? I have tried to create an OpenLayers.Bounds to find out the center coordinates, but no chance, it still fails.

Oh, and the divs must go on each other, because I'm swiping between them, just JSFiddle refused to maintain that function in my code.

2

As nobody provided an answer and meanwhile I created a solution, I will post it as an answer.

The main problem was, that I was thinking in a cartesian plane, while the projection is based on a sphere. It worked in some situations, while failed in other cases. The task is to filter out these cases. I changed the averaging formula a little to add more flexibility. The new formula is: x1 + (x2 - x1) / 2, where x1 is the starting coordinate and x2 is the ending one. The source of the problem as stated in the question was near the date line. With a spherical approach, the range of the longitudes has to be changed from -180° - 180° to 0 - 360°. 360° in the Spherical Mercator projection is equal to 40075016.6856 meters. The negative values can be set with the following formula:

if (startCoords < 0){
    startCoords = startCoords + 40075016.6856;
}
if (endCoords < 0){
    endCoords = endCoords + 40075016.6856;
}

Now, to set the center coordinates properly, you have to transform the coordinate to the original intervals. However, you only have to apply the transformation to coordinates between 180° and 360°. To do this:

if (centerCoords > 20037508.3428) {
    centerCoords = centerCoords - 40075016.6856;
}

The last filter should be applied to the relation of the starting and ending coordinates. This is essential where the ending longitude passes the 0 value. In this case the algorithm calculates a valid coordinate with a wrong value. To fix this, I have used a last check:

if (startCoords > endCoords) {
    endCoords = endCoords + 40075016.6856;
}

Now the change in the averaging formula is starting to make sense. The ending coordinate can be increased by 360°. If we don't use this coordinate in the averaging, we don't have to pay attention to the increment in the ending coordinate. Instead, we use the difference between the starting and the ending longitudes to calculate the center.

The full code will be something like this:

function changeView(evt) {
    if (movevar === false) {
        startCoords = map2.getCoordinateFromPixel([0, 0])[0];
        endCoords = map2.getCoordinateFromPixel([640, 0])[0];
        if (startCoords < 0) {
            startCoords = startCoords + 40075016.6856;
        }
        if (endCoords < 0){
            endCoords = endCoords + 40075016.6856;
        }
        if (startCoords > endCoords) {
            endCoords = endCoords + 40075016.6856;
        }
        centerCoords = startCoords + (endCoords - startCoords)/2;
        if (centerCoords > 20037508.3428) {
            centerCoords = centerCoords - 40075016.6856;
        }
        map.setCenter([centerCoords, evt.map.getView().getCenter()[1]],
evt.map.getView().getZoom());
        movevar = true;
    } else {
        movevar = false;
    }
}

Live example: http://jsfiddle.net/GFarkas/xxqu97nc/2/.

PS: It is still glitchy a little bit in coordinate precision, so if someone could cook up a better algorithm, I would rather accept that answer. If no more answers will be provided, I will accept mine in a few days.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.