Dynamic google map with custom tiles prevent repeating pan

后端 未结 3 968
梦如初夏
梦如初夏 2021-02-09 19:47

I have a dynamic tile set where I do NOT want to allow panning outside of its bounds.

The below code gets me close, but the user can still scroll horizontally outside of

相关标签:
3条回答
  • 2021-02-09 20:21

    Instead of using map.getCenter() as a check, use map.getBounds()

    Untested, but a simple way to accomplish this would be to check if the union of both bounds were identical (if not, your map bounds is outside your strict bounds):

    var union = strictBounds.union(map.getBounds());
    if (strictBounds.equals(union)) { //new map bounds is within strict bounds
    
    0 讨论(0)
  • 2021-02-09 20:23

    For quick access, here is the jsfiddle for this solution: http://jsfiddle.net/nYz6k/


    Supposed you're happy with the current solution of detecting bounds against the center, all you have to do is to restrict the bounds based on the current map canvas size.

    The result that you're getting now is that every corner of the restricting bounds appears on the center of the map when it's full restricting (i.e. both latitude and longitude exceed the bounds corners).

    The solution is to actually remove the x and y offset from the bounds, and still be able to check against the center (which is the most efficient solution, compared to other bounds-based checking solutions).

    Also, you have to restrict the bounds only when you initialize the map and when the window is resizing, which means that when you pan there is no extra processing overhead besides the check method that you have already provided.

    Don't forget to set the maxZoom property for the map (tweak it as needed) because above a determined zoom level the restricting bounds will by themselves fit in the viewport and there's no solution for that.

    Important! Use 'center_changed' instead of 'drag' because 'drag' has that sliding behavior that when you finished dragging and set the center, the maps still slides in the direction of the panning.


    Before you implement the solution I recommend you to check out how the Maps API coordinate system and projection works, because this solution is heavily based on it, and if you want to tweak it out, it's useful to know this information.

    https://developers.google.com/maps/documentation/javascript/maptypes and check out the Custom Map Types -> Map Coordinates section.


    Here's what you need to do. First, you need to implement 2 short methods of converting between geographic coordinates (LatLng) and pixel coordinates.

    var fromLatLngToPixel = function (latLng) {
      var point = map.getProjection().fromLatLngToPoint(latLng);
      var zoom = map.getZoom();
      return new google.maps.Point(
        Math.floor(point.x * Math.pow(2, zoom)),
        Math.floor(point.y * Math.pow(2, zoom))
      );
    }
    
    var fromPixelToLatLng = function (pixel) {
      var zoom = map.getZoom();
      var point = new google.maps.Point(
        pixel.x / Math.pow(2, zoom),
        pixel.y / Math.pow(2, zoom)
      );
      return map.getProjection().fromPointToLatLng(point);
    }
    

    Next, implement the method that effectively restricts the bounds. Note that you always have to keep a variable that stores the original bounds, because each time the map canvas is resized, the resulting bounds will change.

    For this, let's say originalBounds keeps the bounds you provided with sw_lat, sw_long etc, and shrinkedBounds is modified by this method. You can rename it to strictBounds to still work in your method, but it's up to you. This uses jQuery for getting the width and height of the canvas object.

    var shrinkBounds = function () {
      zoom = map.getZoom();
    
      // The x and y offset will always be half the current map canvas
      // width and height respectively
      xoffset = $('#map_canvas').width() / 2;
      yoffset = $('#map_canvas').height() / 2;
    
      // Convert the bounds extremities to global pixel coordinates
      var pixswOriginal = fromLatLngToPixel(originalBounds.getSouthWest());
      var pixneOriginal = fromLatLngToPixel(originalBounds.getNorthEast());
    
      // Shrink the original bounds with the x and y offset
      var pixswShrinked = new google.maps.Point(pixswOriginal.x + xoffset, pixswOriginal.y - yoffset);
      var pixneShrinked = new google.maps.Point(pixneOriginal.x - xoffset, pixneOriginal.y + yoffset);
    
      // Rebuild the shrinkedBounds object with the modified 
      shrinkedBounds = new google.maps.LatLngBounds(
        fromPixelToLatLng(pixswShrinked),
        fromPixelToLatLng(pixneShrinked));
    }
    

    Next, all you have to do is to call this method:

    • once when the map is initialized. Be aware that depending on when you call the method, you may be getting some weird errors because the map might not have yet initialized all it's properties. Best way is to use the projection_changed event.

      google.maps.event.addListener(map, 'projection_changed', function (e) {
        shrinkBounds();
      });
      
    • every time the map canvas is resized

       google.maps.event.addListener(map, 'resize', function (e) {
         shrinkBounds();
       });
      

    But the most important part of this is that the 'resize' event is never triggered for programmatic resizes of the map container, so you have to trigger it manually every time you resize the canvas programmatically (if you do, but I doubt you do).

    The most common way is to trigger it when the window is resized:

    $(window).resize(function () {
      google.maps.event.trigger(map, 'resize');
    });
    

    After all of this, you can now safely use your method, but as a handler for 'center_changed' and not for 'drag' as I've mentioned earlier.


    I set up a jsfiddle with the full working code.

    http://jsfiddle.net/nYz6k/

    You can see the restricting at the bottom left corner with that little half-marker that shows up, which is positioned in the SW corner of the bounds. There is also one at the NW corner but naturally you can't see it because it's displayed above the position.

    0 讨论(0)
  • 2021-02-09 20:26

    Tiborg version above is the best version so far in keeping the bounds... I noticed a bug that occurs when zoom level and width/height are larger than original bounds

    I have modified his code and added a rectangle to see it in action.

    var buildBounds = function(sw, ne) {
        var swY = sw.lat();
        var swX = sw.lng();
        var neY = ne.lat();
        var neX = ne.lng();
        if (swY > neY) {
          var cY = (swY + neY) / 2;
          swY = cY;
          neY = cY;
        }
        if (swX > neX) {
          var cX = (swX + neX) / 2;
          swX = cX;
          neX = cX;
        }
        return new google.maps.LatLngBounds(
          new google.maps.LatLng(swY, swX),
          new google.maps.LatLng(neY, neX));
      }
    

    http://jsfiddle.net/p4wgjs6s/

    0 讨论(0)
提交回复
热议问题