Sunday, May 16, 2010

Mixing Google Maps with Raphael JS (II)

In this second part of this "mix" we will add a transparent layer over the Google Maps map. The purpose is to use the layer as a working area for drawing shapes.
The shapes built on this layer can be added to the map as shown on this page. This designer is useful as a tool when you want to add custom shapes for example not coding, but visually.

Prerequisites:
Besides the loading of Google Maps script and Raphael JS script, in the head of the document we load two libraries, jQuery and jQuery UI and a stylesheet to use together with jQuery UI. You can use any other library you like, but this is what I have used.

Page structure:
a) Head:
- google maps;
- Raphael JS script;
- jQuery;
- jQuery UI;
- CSS file;
- the JavaScript which will be used to put out designer on work.
b) Body:
- a div which will contain the Google map (id=map_canvas);
- a div which will be positioned absolutely on the same coordinates as the Google map, with a z-index value that will put it on the front (id=r_canvas);
- a div which will be used as a handler to move/select objects.

Strategy for the code building:
-first, we add the map:
function initialize() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
}
}
The rest of the code has to be positioned inside the 'initialize()' function.
- then we add the Raphael JS object which is the working area:
var r = Raphael(document.getElementById("r_canvas"), 500, 300);
- now we add a mouse position function to the document:
$("#canvas").mousemove(function(e){
xPos=e.pageX;
yPos=e.pageY;
});
var position={
"x":'0',
"y":'0'
} ;
This code will be used as follows:
"xPos" and "yPos" are declared with local scope, to be updated dynamically when the mouse moves.
"position" is declared globally, but will be used only on "onmousedown" document event.

Make a chart of events which are common for jQuery,Raphael JS and Google Maps that will be used.

DOM jQuery Raphael JS Google Maps
onmousedown mousedown mousedown mousedown
onmousemove mousemove mousemove mousemove
onmouseup mouseup mouseup mouseup
onclick click click click

The usage is this:
DOM:
document.onclick(function(alert("test")));
jQuery
$(document).click(function(){alert("test")})
Raphael JS
Same as DOM, but the event can be attached only to a Raphael JS object.

Google Maps
It can be attached only to a Google map object.
GEvent.addListener(map, "click", function() {
alert("test");
});

With the above events you can start to build the engine.
These events will pass actions and information from one library to another as required.
In our designer we use Raphael JS for its enhancements,such as shape rotation,custom shapes, control and more, but we could design very well without any library, just with Google Maps

Friday, May 14, 2010

Similar results with Google Maps geocoding service

How to display all the results found by the Google Mas geocoding service.
The geocoding service returns a set of results where the first one in the set should be the most accurate. Of course, the resulting set can be influenced by the map options.



Follow the comments in the script that describes the functionality.
var map;
var geocoder;

function initialize() {
map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(34, 0), 1);
geocoder = new GClientGeocoder();
}

// addAddressToMap() is called when the geocoder returns an
// answer. It adds a marker to the map with an open info window
// showing the nicely formatted version of the address and the country code.
function addAddressToMap(response) {
map.clearOverlays();
if (!response || response.Status.code != 200) {
alert("Sorry, we were unable to geocode that address");
} else {


// clear the container where will appear the similarities
document.getElementById('s').innerHTML='';
//this is the marker that will be placed on the map
place = response.Placemark[0];
point = new GLatLng(place.Point.coordinates[1],
place.Point.coordinates[0]);
marker = new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml(place.address + '<br>' +
'<b>Country code:</b> ' + place.AddressDetails.Country.CountryNameCode);
//now we add the similarities

//'lgt' is the total number of similaties (including the one placed on the map)
var lgt=response.Placemark.length;
//now we loop through each similarity
for(i=0;i<lgt;i++){
var mn=response.Placemark[i];
var mnpoint=new GLatLng(mn.Point.coordinates[1],
mn.Point.coordinates[0]);
markere = new GMarker(mnpoint);
//and add it on our list
document.getElementById('s').innerHTML+=mn.address+'<br/>';
}

}
}

// showLocation() is called when you click on the Search button
// in the form. It geocodes the address entered into the form
// and adds a marker to the map at that location.
function showLocation() {
var address = document.forms[0].q.value;
geocoder.getLocations(address, addAddressToMap);
}

// findLocation() is used to enter the sample addresses into the form.
function findLocation(address) {
document.forms[0].q.value = address;
showLocation();
}

Wednesday, March 31, 2010

Nearest intersection to an address on Google Maps

How to find the nearest address on a Google Map using it's API.
There are at least 2 steps in order to achieve this goal.
Let's say that you ask to geocode an address.
The map returns the exact location and centers the map on the address.
Then you ask for a direction from point A, which is the SW bound of the viewport, to point B, which is your address, to point C which is the NE bound of the viewport.
So, now you could know the last intersection before the address and also the first intersection after the address.
If you want all the intersection you can repeat the direction search with different routes, let's say NW to SE, N to S, E to W. and so on...
It might not be accurate, but is a workaround.

Friday, March 26, 2010

Geocoding the stops on a route with Google Maps API

Many times we need to know the coordinates of each stop on a route.
Below is an example page which does that.
The functionality is commented inside the 'script' tag.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Geocoding Directions</title>
<script src="http://www.google.com/jsapi?key=ABQIAAAAnQbookkle8hxu22fQ6NbQhQjkNs2bAs_9Kr1eym0RcuS7Ni0VhQw6LZcRdCzOojjVAt8h8h2T0G6YA" type="text/javascript"></script>
<style type="text/css">
body {
font-family: Verdana, Arial, sans serif;
font-size: 11px;
margin: 2px;
}
table.directions th {
background-color:#EEEEEE;
}

img {
color: #000000;
}
</style>
<script type="text/javascript">

google.load('maps' , '2');
var map;
var gdir;
var geocoder = null;
function OnLoad() {
// declare our map
map = new GMap2(document.getElementById("map_canvas"));

// setting up the GDirections to load our directions
gdir = new GDirections(map, document.getElementById("directions"));

// we add an event listener to call a function when the response is ready from Google
GEvent.addListener(gdir, "load", onGDirectionsLoad);

// we request for directions
gdir.load("from: San Francisco to: San Mateo to: Mountain View");

}
function onGDirectionsLoad(){

// we make a loop to get the coordinates for all the cities
for (i=0;i<2;i++){

// 'route' will be the shortest route found by Google
var route = gdir.getRoute(i);

// 'nx' will be the starting coordinate for each segment
var nx=route.getStep(0).getLatLng();

// we now write on the page the coordinates found
document.getElementById('mydata').innerHTML+=nx+'<br/>';
}
// finally we add the final destination coordinate
document.getElementById('mydata').innerHTML+=route.getStep(1).getLatLng()+'<br/>';
}
google.setOnLoadCallback(OnLoad);
</script>
</head>
<body style="font-family: Arial;border: 0 none;">
<h2>San Francisco - San Mateo - Mountain View</h2>
<br/>
<table class="directions">
<tr>
<td valign="top"> <b>Formatted Directions</b>
<div id="directions" style="width: 275px"></div>
<td>
<td valign="top"><b>Map</b>
<div id="map_canvas" style="width: 310px; height: 400px"></div><br/>
<td>
<td valign="top"><b>Coordinates for each city</b><div id="mydata" style="font-size:11px"></div></td>
</tr>
</table>
</body>
</html>

A live sample can be found here.

Tuesday, March 23, 2010

Google Maps, Raphael JS and browsers

The Google Maps renders shapes in svg format for most of the browsers and vml format for Internet Explorer. It seems that IE9 will adopt the svg format finally.

The use of these shapes is useful when you want to draw not standard paths or images.
Which format is better I don't know, I use Mozilla Firefox and Google Chrome most of the time, but Google Maps + vml + Raphael format works better, in this case in Internet Explorer.

In the map below there is a nice ball (click on the marker to see it).
If you are viewing this page in a browser with svg support you will notice that there is a invisible layer on the map close to the ball which doesn't let you handle (drag) the map.
But if you are viewing the page with in a browser with vml support you will notice that you can handle everything around the ball.

The code to handle the ball:
var mymark=new GMarker(latlng);
map.addOverlay(mymark);
GEvent.addListener(mymark, 'click', function(){
x.show();
x.animate({cx: 100}, 2000, "elastic");
x.click(reset);
function reset(){
x.animate({cx: 50}, 2000, "elastic");
}
});


Click on the marker or the ball to animate the ball.

Monday, March 8, 2010

Mixing Google Maps with Raphael JS (I)

Raphael JS is a JavaScript library built to simplify working with vector graphics (for the web) .
This library is useful in Google Maps when you want to do something that simple html is not enough.

What to do:

1) First, create a webpage:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title></title>
</head>

<body>

</body>

</html>

2) Load the scripts in the head of the page:

<script src="raphael-min.js" type="text/javascript" charset="utf-8"></script>
    <script src="http://maps.google.com/maps?file=api&amp;v=2&key=ABQIAAAAnQbookkle8hxu22fQ6NbQhQNPFMnb_xkSQN8CfaH-8v21m5ByRQZ6mgEZeD_g20a0Wrd6MNogUCNZQ" type="text/javascript"></script>

3) Add page elements inside the body tag:

<div id="map_canvas" style="width: 500px; height: 300px"></div>

4) Create the script that do the rest of the  work in the head:

<script type="text/javascript" charset="utf-8">
            function initialize() {
      if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById("map_canvas"));
        map.setCenter(new GLatLng(44, 11), 1);
        var details=document.createElement('div');
        details.innerHTML="<div id='r_canvas' style=' -moz-user-select: none; position: absolute; right: 0px; bottom: 0px;' class='gmnoprint'></div>"
        document.getElementById('map_canvas').appendChild(details);

        raph();
      }
    }
            function raph() {

                var r = Raphael("r_canvas", 100, 100);
                var sq = 1;


                    var color = Raphael.getColor();
                    (function (a, c) {

                    r.rect(0,0, 100,100).attr({"stroke-width":0,fill: "#000", opacity: 0.5}).mouseover(changerad)})(sq, "black");
                    r.text(50,50,"mouse over me").attr({"fill":"yellow","font-size":12});



                          }
                Raphael.getColor.reset();


function changerad() {



 this.animate({cx: 50, r: 40}, 1000, "bounce");


 }



    </script>

5) After doing all above and changing the API key you are ready to test it on your server. It should look like this:

Saturday, March 6, 2010

Reading XML files in Google Maps

There is a sample on Google Maps API documentation which should be used to read XML files. This is a receipt of code taken from there:

var name = markers[i].getAttribute("name");

where in the xml file you would find it here:

<marker name="mymarker"/>

The variable 'name' should have the value 'mymarker', but for some reasons (browser incompatibility mostly) it didn't work for me. My solution was to reconstruct the XML file and the XML line looks like this:

<marker>
<name>mymarker</name>
</marker>


Reading the value :

var name = markers[i].childNodes[0].nodeValue;


Here is the working solution for reading xml files.