Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show direction between two points in Google Map Api with a padding

I want to use google Map Api and draw direction between two points. My map is partially covered by a gray box in which some text might be shown. The problem is occurred when distance of the two points is too far and one point is covered by the gray box.

How can I force it to draw the path in a way that whole the path is is shown on the right side of the gray box and none of the points is overlapped by the gray box?

What I currently have:

enter image description here

What I expect:

enter image description here

like image 448
Hamid Ghorashi Avatar asked Mar 18 '23 09:03

Hamid Ghorashi


2 Answers

Since I saw already a few questions on SO about offsetting stuff that would appear behind elements overlaid on the map, I thought I'd give it a bit of time.

Here is how I did it:

  1. Plot a route on the map and listen for the map idle event before starting with the offset process.

  2. Check the leftmost point of the route bounds to see if it falls behind the overlay. This makes use of the fromLatLngToPoint() method to translate from lat/lng coordinates to a point on the map projection.

  3. Check how much you can offset the route by comparing the leftmost and rightmost points of the route with the available space on the map. Offset the map until both points fit on the map canvas.

  4. If both points cannot fit within the map canvas, zoom out and start the same process again.

  5. The script must be aware of the width of the overlay and you should apply some margins so that it always fits well.

Here is the function used to translate between coordinates and point:

function fromLatLngToPoint(latLng) {

    var scale = Math.pow(2, map.getZoom());
    var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
    var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
    var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng);

    return new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
}

Here is the demo:

JSFiddle demo

I am sure it can still be optimized but it does the job quite well. Please report issues here if you find any.

Edit:

The same technique works with markers too:

JSFiddle demo

like image 152
MrUpsidown Avatar answered Mar 20 '23 22:03

MrUpsidown


Here is a rewrite for right hand sidebar. Paste the below code in js in JSFiddle link and change CSS for sidebar to right:0

var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var routeBounds = false;
var overlayWidth = 400; // Width of the overlay DIV
var leftMargin = 30; // Grace margin to avoid too close fits on the edge of the overlay
var rightMargin = 30; // Grace margin to avoid too close fits on the right and leave space for the controls

overlayWidth+= rightMargin;

var start = new google.maps.LatLng(48.857380, 2.351717);
var end = new google.maps.LatLng(50.108814, 8.672309);

function initialize() {

    var btn1 = document.getElementById('calcRoute');
    btn1.addEventListener('click', calcRoute);

    var btn2 = document.getElementById('offsetMap');
    btn2.addEventListener('click', offsetMap);

    var btn3 = document.getElementById('fitAndOffsetMap');
    btn3.addEventListener('click', fitAndOffsetMap);

    var btn4 = document.getElementById('fitMap');
    btn4.addEventListener('click', fitMap);

    directionsDisplay = new google.maps.DirectionsRenderer({
        draggable: true
    });

    var mapOptions = {
        zoom: 13,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        center: start,
        panControlOptions: {
            position: google.maps.ControlPosition.TOP_RIGHT
        },
        zoomControlOptions: {
            position: google.maps.ControlPosition.TOP_RIGHT
        }
    };

    map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
    directionsDisplay.setMap(map);
}

function offsetMap() {

    if (routeBounds !== false) {

        // Clear listener defined in directions results
        google.maps.event.clearListeners(map, 'idle');

        // Top right corner
        var topRightCorner = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getNorthEast().lng());

        // Top right point
        var topRightPoint = fromLatLngToPoint(topRightCorner).x;

        // Get pixel position of leftmost and rightmost points
        var leftCoords = routeBounds.getSouthWest();
        var rightCoords = routeBounds.getNorthEast();
        
        var leftMost = fromLatLngToPoint(leftCoords).x;
        var rightMost = fromLatLngToPoint(rightCoords).x;

        // Calculate left and right offsets
        var leftOffset = leftMost-leftMargin;
        var rightOffset = (overlayWidth-(topRightPoint - rightMost));
        console.log(leftMost,rightMost,topRightPoint,leftOffset,rightOffset)

        // Only if left offset is needed
        if (rightOffset >= 0) {

            if (rightOffset < leftOffset) {
var mapOffset = Math.round((leftOffset-rightOffset ) / 2);

                // Pan the map by the offset calculated on the x axis
                map.panBy(mapOffset, 0);
                // Get the new left point after pan
     var newRightPoint = fromLatLngToPoint(rightCoords).x;
  
           console.log("e",newRightPoint,(topRightPoint-newRightPoint))
                if ((topRightPoint-newRightPoint) <= overlayWidth) {
           console.log("jjjj")
                    // Leftmost point is still under the overlay
                    // Offset map again
                 offsetMap();
                                    }
           
            }
            else {
            console.log("j")
                // Cannot offset map at this zoom level otherwise both leftmost and rightmost points will not fit
                // Zoom out and offset map again
                map.setZoom(map.getZoom() - 1);
                offsetMap();
            }
        }
    }
}

function fromLatLngToPoint(latLng) {

    var scale = Math.pow(2, map.getZoom());
    var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
    var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
    var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng);

    return new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
}

function calcRoute() {

    var request = {
        origin: start,
        destination: end,
        travelMode: google.maps.DirectionsTravelMode.DRIVING
    };

    directionsService.route(request, function (response, status) {

        if (status == google.maps.DirectionsStatus.OK) {

            directionsDisplay.setDirections(response);

            // Define route bounds for use in offsetMap function
            routeBounds = response.routes[0].bounds;

            // Write directions steps
            writeDirectionsSteps(response.routes[0].legs[0].steps);

            // Wait for map to be idle before calling offsetMap function
            google.maps.event.addListener(map, 'idle', function () {

                // Offset map
                offsetMap();
            });

            // Listen for directions changes to update bounds and reapply offset
            google.maps.event.addListener(directionsDisplay, 'directions_changed', function () {

                // Get the updated route directions response
                var updatedResponse = directionsDisplay.getDirections();

                // Update route bounds
                routeBounds = updatedResponse.routes[0].bounds;

                // Fit updated bounds
                map.fitBounds(routeBounds);

                // Write directions steps
                writeDirectionsSteps(updatedResponse.routes[0].legs[0].steps);

                // Offset map
                offsetMap();
            });
        }
    });
}

function writeDirectionsSteps(steps) {

    var overlayContent = document.getElementById("overlayContent");
    overlayContent.innerHTML = '';

    for (var i = 0; i < steps.length; i++) {

        overlayContent.innerHTML += '<p>' + steps[i].instructions + '</p><small>' + steps[i].distance.text + '</small>';
    }
}

function fitMap() {

    if (routeBounds !== false) {

        map.fitBounds(routeBounds);
    }
}

function fitAndOffsetMap() {

    if (routeBounds !== false) {

        map.fitBounds(routeBounds);
        offsetMap();
    }
}

initialize();
like image 20
Hari Prasath S Avatar answered Mar 20 '23 22:03

Hari Prasath S