You may also need to consider wrapping coordinates, distorting curves, and centering on related dimensions if the map resizes or zooms in / out. This is especially necessary if your borders occupy a large percentage of the entire map (for example, like a continent).
One of the problems with checkBounds () is that it does not take into account latitude values close to the north / south poles, which have non-linear distortion, which limit the accuracy limits (I use approximate magic number factors that will not work in all situations) . By right, you must first convert the borders to linear 2d coordinates of the world to see how far from the borders it is in terms of world coordinates, than to compare the actual target center point in the world coordinate with the target actual latitude location. For longitude values this does not seem like a big problem, and the linear clipping approach seems quite accurate, the main problem is related to wrapping the longitude coordinates, which is taken into account (somewhat) in the code below.
// Persitant variables var allowedBounds; // assign something here var lastValidCenter; // initialize this using map.getCenter() function checkBounds() { // when bounds changes due to resizing or zooming in/out var currentBounds = map.getBounds(); if (currentBounds == null) return; var allowed_ne_lng = allowedBounds.getNorthEast().lng(); var allowed_ne_lat = allowedBounds.getNorthEast().lat(); var allowed_sw_lng = allowedBounds.getSouthWest().lng(); var allowed_sw_lat = allowedBounds.getSouthWest().lat(); var wrap; var cc = map.getCenter(); var centerH = false; var centerV = false; // Check horizontal wraps and offsets if ( currentBounds.toSpan().lng() > allowedBounds.toSpan().lng() ) { centerH = true; } else { // test positive and negative wrap respectively wrap = currentBounds.getNorthEast().lng() < cc.lng(); var current_ne_lng = !wrap ? currentBounds.getNorthEast().lng() : allowed_ne_lng +(currentBounds.getNorthEast().lng() + 180 ) + (180 - allowed_ne_lng); wrap = currentBounds.getSouthWest().lng() > cc.lng(); var current_sw_lng = !wrap ? currentBounds.getSouthWest().lng() : allowed_sw_lng - (180-currentBounds.getSouthWest().lng()) - (allowed_sw_lng+180); } // Check vertical wraps and offsets if ( currentBounds.toSpan().lat() > allowedBounds.toSpan().lat() ) { centerV = true; } else { // test positive and negative wrap respectively wrap = currentBounds.getNorthEast().lat() < cc.lat(); if (wrap) { alert("WRAp detected top") } // else alert("no wrap:"+currentBounds); wrap = false; var current_ne_lat = !wrap ? currentBounds.getNorthEast().lat() : allowed_ne_lat + (currentBounds.getNorthEast().lat() +90) + (90 - allowed_ne_lat); wrap = currentBounds.getSouthWest().lat() > cc.lat(); if (wrap) { alert("WRAp detected btm") } //alert("no wrap:"+currentBounds); var current_sw_lat = !wrap ? currentBounds.getSouthWest().lat() : allowed_sw_lat - (90-currentBounds.getSouthWest().lat()) - (allowed_sw_lat+90); } // Finalise positions var centerX = cc.lng(); var centerY = cc.lat(); if (!centerH) { if (current_ne_lng > allowed_ne_lng) centerX -= current_ne_lng-allowed_ne_lng; if (current_sw_lng < allowed_sw_lng) centerX += allowed_sw_lng-current_sw_lng; } else { centerX = allowedBounds.getCenter().lng(); } if (!centerV) { if (current_ne_lat > allowed_ne_lat) { centerY -= (current_ne_lat-allowed_ne_lat) * 3; // approximation magic numbeer. Adjust as u see fit, or use a more accruate pixel measurement. } if (current_sw_lat < allowed_sw_lat) { centerY += (allowed_sw_lat-current_sw_lat)*2.8; // approximation magic number } } else { centerY = allowedBounds.getCenter().lat(); } map.setCenter(lastValidCenter = new google.maps.LatLng(centerY,centerX)); } function limitBound(bound) // Occurs during dragging, pass allowedBounds to this function in most cases. Requires persistant 'lastValidCenter=map.getCenter()' var reference. { var mapBounds = map.getBounds(); if ( mapBounds.getNorthEast().lng() >= mapBounds.getSouthWest().lng() && mapBounds.getNorthEast().lat() >= mapBounds.getSouthWest().lat() // ensure no left/right, top/bottom wrapping && bound.getNorthEast().lat() > mapBounds.getNorthEast().lat() // top && bound.getNorthEast().lng() > mapBounds.getNorthEast().lng() // right && bound.getSouthWest().lat() < mapBounds.getSouthWest().lat() // bottom && bound.getSouthWest().lng() < mapBounds.getSouthWest().lng()) // left { lastValidCenter=map.getCenter(); // valid case, set up new valid center location } // if (bound.contains(map.getCenter())) // { map.panTo(lastValidCenter); // } } // Google map listeners google.maps.event.addListener(map, 'zoom_changed', function() { //var zoom = map.getZoom(); checkBounds(); }); google.maps.event.addListener(map, "bounds_changed", function() { checkBounds(); }); google.maps.event.addListener(map, 'center_changed', function() { limitBound(allowedBounds); });
ps For checkBounds (), to get the correct 2d world coordinate from the center of the map, given 2 lat / lng values, use map.getProjection (). fromLatLngToPoint (). Compare 2 points, find the linear difference between them and compare the difference in world coordinates back with lat / lng using map.getProjection (). FromPointToLatLng (). This will give you exact clip offsets in lat / lng units.
Glidias Oct 08 2018-12-12T00: 00Z
source share