Franckl
Franckl

Reputation: 1491

leaflet : Prevent marker to be dragged outside the map container

Please consider the following code http://jsfiddle.net/franckl/311bcbc8/

var southWest = L.latLng(-90, -180),
    northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);

var map = L.map('map', {
    minZoom: 2,
    zoomControl: false,
    attributionControl: false,
    maxBounds: bounds
});

// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
    minZoom: 2,
    subdomains: 'abcd',
    detectRetina: true,
    attribution: ''
}).addTo(map);

map.fitBounds(bounds);

var newMarker0 = L.marker(map.getCenter(), {
    icon: new L.Icon.Default(),
    zIndexOffset: 10000,
    draggable: true
});

newMarker0.addTo(map);

html

<div id="mycontainer">
    <div id="map"></div>
</div>

css

body {
    margin:0;
    padding:0;
}
#map {
    position:absolute;
    top:0;
    bottom:0;
    width:300px;
}

#mycontainer {
    top: 10px;
    width: 600px;
    height: 250px;
    position: relative;
}

If you drag the marker to the right, it leaves visible area of the map. How can I prevent the user from dragging the marker outside the map ?

Thanks !

Upvotes: 2

Views: 2803

Answers (2)

ericsoco
ericsoco

Reputation: 26253

A solution with slightly more generic code and tailored to dragging the marker rather than the map, but derivative of @Franckl's:

onMarkerDrag: function (event) {

    // keep dragged marker within map bounds

    var containerPoint = this.map.latLngToContainerPoint(event.target.getLatLng()),
        clampX = null,
        clampY = null,
        MARKER_MARGIN = 10;

    if (containerPoint.x - MARKER_MARGIN < 0) {
        clampX = MARKER_MARGIN;
    } else if (containerPoint.x + MARKER_MARGIN > this.mapContainerBounds.width) {
        clampX = this.mapContainerBounds.width - MARKER_MARGIN;
    }

    if (containerPoint.y - MARKER_MARGIN < 0) {
        clampY = MARKER_MARGIN;
    } else if (containerPoint.y + MARKER_MARGIN > this.mapContainerBounds.height) {
        clampY = this.mapContainerBounds.height - MARKER_MARGIN;
    }

    if (clampX !== null || clampY !== null) {
        if (clampX !== null) { containerPoint.x = clampX; }
        if (clampY !== null) { containerPoint.y = clampY; }
        marker.setLatLng(this.map.containerPointToLatLng(containerPoint));
    }

},

I derive this.mapContainerBounds once on map init instead of every time the drag handler fires (my map does not change size), like this:

this.mapContainerBounds = mapDOMNode.getBoundingClientRect();

Upvotes: 0

Franckl
Franckl

Reputation: 1491

answering my own question in case it helps anyone. We detect the map container size and check if the marker is going outside the visible area by converting its lat/lng coordinates to a container point (map.containerPointToLatLng(markerContainerPosition))

As a bonus, this code leaves the marker in the same position relative to the map container when the user moves the map. It ensures that the marker never goes outside the visible area (even when zooming)

var southWest = L.latLng(-90, -180),
    northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);

var map = L.map('map', {
    minZoom: 2,
    zoomControl: false,
    attributionControl: false,
    maxBounds: bounds
});

// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
    minZoom: 2,
    subdomains: 'abcd',
    detectRetina: true,
    attribution: ''
}).addTo(map);

map.fitBounds(bounds);

var newMarker0 = L.marker(map.getCenter(), {
    icon: new L.Icon.Default(),
    zIndexOffset: 10000,
    draggable: true
});

newMarker0.addTo(map);

var mapSize = map.getSize();
var markerContainerPosition = map.latLngToContainerPoint(newMarker0.getLatLng());

function mapMove() {
    newMarker0.setLatLng(map.containerPointToLatLng(markerContainerPosition));
}

function markerDrag(e) {
    var mTempContainerPos = map.latLngToContainerPoint(newMarker0.getLatLng());
    var newPos;

    if (mTempContainerPos.x < 20) {
        if (mTempContainerPos.y < 45) {
            newPos = L.point(20, 45);
        } else if (mTempContainerPos.y > (mapSize.y - 20)) {
            newPos = L.point(20, mapSize.y - 20);
        } else {
            newPos = L.point(20, mTempContainerPos.y);
        }
    } else if (mTempContainerPos.x > mapSize.x - 20) {
        if (mTempContainerPos.y < 45) {
            newPos = L.point(mapSize.x - 20, 45);
        } else if (mTempContainerPos.y > (mapSize.y - 20)) {
            newPos = L.point(mapSize.x - 20, mapSize.y - 20);
        } else {
            newPos = L.point(mapSize.x - 20, mTempContainerPos.y);
        }
    } else {
        if (mTempContainerPos.y < 45) {
            newPos = L.point(mTempContainerPos.x, 45);
        } else if (mTempContainerPos.y > (mapSize.y - 20)) {
            newPos = L.point(mTempContainerPos.x, mapSize.y - 20);
        }
    }

    if (newPos) {
        markerContainerPosition = newPos;
        newMarker0.setLatLng(map.containerPointToLatLng(newPos));
    } else {
        markerContainerPosition = mTempContainerPos;
    }
}

map.on('move', mapMove);
newMarker0.on('drag', markerDrag); 

Upvotes: 3

Related Questions