Question

I’ve created a page that allows users to highlight countries on a google map. My code is pretty simple - I’m using D3.js and google’s OverlayView – I grabbed the geo json for the countries from here.

Everything works great – but my country outlines will sometimes incorrectly wrap across the center of the map when a country straddles the furthest edges of the map. For example if the map is panned so that the western half of china is on the far right and the eastern half is on the far left. Here is what happens.

enter image description here

Note: this appears to be a map panning problem and doesn’t have to do with the zoom level – here is the same issue with the map’s min zoom is set to 2

enter image description here

Any ideas on how to avoid this effect when the map is panned? Thanks in advance!

Update #1 (2013.07.18)

Thanks so much for the answer and comment. I created a very simple example of my issue. This test page outlines China on a google map and centers on the US (so you can see the problem on load). Notice panning in either direction fixes the issue, zooming in on the US doesn't seem to help. Thanks again!

Was it helpful?

Solution 2

Big thank you to Ted for pointing me in the right direction - I tried to mark your answer as correct but unfortunately I continue having issues editing your answer.

Regardless, here is a simple sample that implements the solution. The strategy we came up with was to keep track of the longitude of the last point that we processed. When processing new points, if we see that a point makes a really bug jump (in the x direction) - we assume that it's wrapped and we add or subtract the width of the world to move the point back to other points from that country.

Here is the meat of the fix in the overlay.draw function

var markerOverlay = this;
var overlayProjection = markerOverlay.getProjection();
var worldwidth = overlayProjection.getWorldWidth();
var prevX = null;

// Turn the overlay projection into a d3 projection
var googleMapProjection = function (coordinates) {
    var googleCoordinates = new google.maps.LatLng(coordinates[1], coordinates[0]);
    var pixelCoordinates = overlayProjection.fromLatLngToDivPixel(googleCoordinates);
    if (prevX != null) {
        // Check for a gigantic jump to prevent wrapping around the world
        var dist = pixelCoordinates.x - prevX;
        if (Math.abs(dist) > (worldwidth * 0.9)) {
            if (dist > 0) { pixelCoordinates.x -= worldwidth } else { pixelCoordinates.x += worldwidth }
        }
    }
    prevX = pixelCoordinates.x;
    return [pixelCoordinates.x + 10000, pixelCoordinates.y + 10000];
}

OTHER TIPS

This question demonstrates how to control the overlay zoom extents and pan extents in order to prevent wrapping by simply not allowing the user to see the whole world at once.

Prevent horizontal world-wrapping of overlays in GMap v3

So the meat of the problem is where the OverlayView is getting the pixel coordinates. If we could figure out how to improve that part to keep the shapes together, it might solve our problem. This may help to test for wrapping, but it may not work since your issue seems to happen even when we are zoomed in.

var proj = overlay.getProjection();
var wwidth = 0;
if (proj) wwidth = proj.getWorldWidth();
var mapsWrapsAround=false;
if (__wwidth > 0 && __wwidth < domContainer.width()) mapsWrapsAround = true;

I realized that testing whether the longitude was way different from the start longitude might not work because the longitude might be correct, but map to multiple div locations on the screen. So it's the div pixel coordinate we have to test. But maybe you can test for flipping in x by testing whether it's in the opposite direction compared to the difference in geographic coordinates.

// UNTESTED

// dimensioned somewhere outside
var start;
var startgeo;

// inside the overlay.onAdd content I think.
// Turn the overlay projection into a d3 projection
var googleMapProjection = function (coordinates) {
    var googleCoordinates = new google.maps.LatLng(coordinates[1], coordinates[0]);
    var pixelCoordinates = overlayProjection.fromLatLngToDivPixel(googleCoordinates);
    var proj = overlay.getProjection();
    var worldwidth = proj.getWorldWidth();
    var finalX = pixelCoordinates.x;

    if(typeof(start) === "undefined"){
        start = pixelCoordinates;
        startgeo = googleCoordinates;
    }
    else {
        if(pixelCoordinates.x > start.x && googleCoordinates.lng() < startgeo.lng()){
              finalX -= worldwidth;
        }
        else if(pixelCoordinates.x < start.x && googleCoordinates.lng() > startgeo.lng()){
             finalX += worldwidth;                     
        } 
    }
    return [finalX, pixelCoordinates.y];
}

So what this is doing is assuming that google maps has picked the screen location closest to North America for each coordinate. If the shape is held together correctly, then if the end longitude is greater than the start longitude, then the end pixel.X should also be greater than the start pixel.X. If, however, the projection has returned a point on the opposite side of the world, then the new pixel, relative to the start pixel, should be in the wrong direction. That is, the end.x now be less than the start.x. So to fix it, we just adjust the pixel by the world width in the direction that would fix the problem. Hopefully.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top