Tuesday, June 22, 2010

Unfortunatly in google maps api v3 it is not possible to access properties of polylines that represent directions, but it is possible to get points of path that are used do draw this polyline. So if you want to highlight selected direction by changing its color or weight, it is necessary to draw new polyline without using DirectionsRenderer.



So to get information about path we want to display let's get steps from the first route:
    var steps = response.routes[0].legs[0].steps;    

As steps recived from DirectionsService is an two dimensional array, it must be changed into simple array of points:
function createColorPoly(steps){    
var path = Array();
for(var step = 0; step < steps.length; step++){
for(var stepP = 0; stepP < steps[step].path.length; stepP++){
path.push(steps[step].path[stepP]);
}
}

No display this new polyline and add listeners where properties of polyline are changed.

    
var polySelected = {'strokeWeight':'9','strokeColor':'red'};
var polyUnselected = {'strokeWeight':'6','strokeColor':'blue'} ;

var newPoly = new google.maps.Polyline(polyUnselected);
newPoly.setPath(path);
newPoly.setMap(map);
google.maps.event.addListener(newPoly, 'mouseover', function(){newPoly.setOptions(polySelected);});
google.maps.event.addListener(newPoly, 'mouseout', function(){newPoly.setOptions(polyUnselected);});




And this is it :)


Here's the working example example


Here's the full source:
var map;
var myLatLng;
var dirService;

function initialize() {
var latlng = new google.maps.LatLng(40.7694,-73.9542);

myLatLng = latlng;
var myOptions = {
zoom:9,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

dirService = new google.maps.DirectionsService();
getDirections();
}
function getDirections(){
var start = 'Broadway, New York, NY, United States';
var end = 'Lincoln Memorial, Washington, DC, United States';

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

dirService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var steps = response.routes[0].legs[0].steps;
createColorPoly(steps);
}
});


}
function createColorPoly(steps){
var path = Array();
for(var step = 0; step < steps.length; step++){
for(var stepP = 0; stepP < steps[step].path.length; stepP++){
path.push(steps[step].path[stepP]);
}
}

var polySelected = {'strokeWeight':'9','strokeColor':'red'};
var polyUnselected = {'strokeWeight':'6','strokeColor':'blue'} ;

var newPoly = new google.maps.Polyline(polyUnselected);
newPoly.setPath(path);
newPoly.setMap(map);
google.maps.event.addListener(newPoly, 'mouseover', function(){newPoly.setOptions(polySelected);});
google.maps.event.addListener(newPoly, 'mouseout', function(){newPoly.setOptions(polyUnselected);});

}
$(document).ready(function(){
initialize();
});

Monday, June 7, 2010

How to add context menu to google maps, using api V3

Here's the way how to add context menu to google map, using api V3 and jQuery.



step 1:
while initializing maps, add listener for event "rightclick":

 google.maps.event.addListener(map, "rightclick",function(event){showContextMenu(event.latLng);});

;


step 2:
Body of contextmenu is created in showContextMenu()
      function showContextMenu(caurrentLatLng  ) {
var projection;
var contextmenuDir;
projection = map.getProjection() ;
$('.contextmenu').remove();
contextmenuDir = document.createElement("div");
contextmenuDir.className = 'contextmenu';
contextmenuDir.innerHTML = '<a id="menu1"><div class="context">menu item 1<\/div><\/a>'
+ '<a id="menu2"><div class="context">menu item 2<\/div><\/a>';

$(map.getDiv()).append(contextmenuDir);

setMenuXY(caurrentLatLng);

contextmenuDir.style.visibility = "visible";
}


step 3:
When menu is created we must calculate where to display it, so LatLng is converted into appropriate X,Y.

      function getCanvasXY(caurrentLatLng){
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(caurrentLatLng);
var caurrentLatLngOffset = new google.maps.Point(
Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale),
Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale)
);
return caurrentLatLngOffset;
}


step 4:
Just to avoid displaying menu outside the map, X and Y coordinates are redefined if nessesary

  function setMenuXY(caurrentLatLng){
var mapWidth = $('#map_canvas').width();
var mapHeight = $('#map_canvas').height();
var menuWidth = $('.contextmenu').width();
var menuHeight = $('.contextmenu').height();
var clickedPosition = getCanvasXY(caurrentLatLng);
var x = clickedPosition.x ;
var y = clickedPosition.y ;

if((mapWidth - x ) < menuWidth)//if to close to the map border, decrease x position
x = x - menuWidth;
if((mapHeight - y ) < menuHeight)//if to close to the map border, decrease y position
y = y - menuHeight;

$('.contextmenu').css('left',x );
$('.contextmenu').css('top',y );
};


step 5:
To display context menu in right way we must apply this style:


.contextmenu{
visibility:hidden;
background:#ffffff;
border:1px solid #8888FF;
z-index: 10;
position: relative;
width: 140px;
}
.contextmenu div{
padding-left: 5px
}


And now complete source:

  
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
var map;
function initialize() {
var latlng = new google.maps.LatLng(49.814357,19.025956);
var myOptions = {
zoom: 12,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
google.maps.event.addListener(map, "rightclick",function(event){showContextMenu(event.latLng);});

}
function getCanvasXY(caurrentLatLng){
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(caurrentLatLng);
var caurrentLatLngOffset = new google.maps.Point(
Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale),
Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale)
);
return caurrentLatLngOffset;
}
function setMenuXY(caurrentLatLng){
var mapWidth = $('#map_canvas').width();
var mapHeight = $('#map_canvas').height();
var menuWidth = $('.contextmenu').width();
var menuHeight = $('.contextmenu').height();
var clickedPosition = getCanvasXY(caurrentLatLng);
var x = clickedPosition.x ;
var y = clickedPosition.y ;

if((mapWidth - x ) < menuWidth)
x = x - menuWidth;
if((mapHeight - y ) < menuHeight)
y = y - menuHeight;

$('.contextmenu').css('left',x );
$('.contextmenu').css('top',y );
};
function showContextMenu(caurrentLatLng ) {
var projection;
var contextmenuDir;
projection = map.getProjection() ;
$('.contextmenu').remove();
contextmenuDir = document.createElement("div");
contextmenuDir.className = 'contextmenu';
contextmenuDir.innerHTML = "<a id='menu1'><div class=context>menu item 1<\/div><\/a><a id='menu2'><div class=context>menu item 2<\/div><\/a>";
$(map.getDiv()).append(contextmenuDir);

setMenuXY(caurrentLatLng);

contextmenuDir.style.visibility = "visible";
}
$(document).ready(function(){
initialize();

});
</script>

<style type="text/css">
#map_canvas{
width: 400px;
height: 300px;
}
.contextmenu{
visibility:hidden;
background:#ffffff;
border:1px solid #8888FF;
z-index: 10;
position: relative;
width: 140px;
}
.contextmenu div{
padding-left: 5px
}
</style>
<div class="formDiv" id="map_canvas"></div>