Search code examples
google-mapsd3.jsgeojsonoverlays

How do I prevent horizontal wrapping of overlays on a google map when panned?


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!


Solution

  • 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];
    }