In theory we can now create maps with numerous markers (although there are
limits to how many can be usefully applied before they need to be clustered – that is, grouped together; which we'll look at later).
However, you will notice a degree of redundancy in the JavaScript code: we have
two sets of lines (for each marker) that are almost identical. If we were to
add a large number of markers, the code would be very repetitive, a clear sign
that we should generalise the code used. The following code shows a revised version.
We have added an additional function called
addMarker()
. This takes three parameters: a position (which must
be a LatLng
object), a title and some informational text. Having
got these, the function creates a marker and an infowindow in the same manner
as in the previous example, substituting in the parameters of the function
where previously we had hard-coded values. Here, then, is where all that
complexity over using the same name for our markers and infowindows plays out:
having got that working, we can now build this kind of function.
var map;
var myCentreLat = 53.8;
var myCentreLng = -1.6;
var initialZoom = 10;
function infoCallback(infowindow, marker) {
return function() {
infowindow.open(map, marker);
};
}
function addMarker(myPos,myTitle,myInfo) {
var marker = new google.maps.Marker(
{position: myPos, map: map, title: myTitle}
);
var infowindow = new google.maps.InfoWindow({content: myInfo});
google.maps.event.addListener
(marker, 'click', infoCallback(infowindow, marker));
}
function initialize() {
var latlng = new google.maps.LatLng(myCentreLat,myCentreLng);
var myOptions = {
zoom: initialZoom,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map
(document.getElementById("map_canvas"), myOptions);
// First marker
var info =
"<div class=infowindow>
<h1>Leeds</h1><p>Population: 715,402</p></div>";
addMarker(
new google.maps.LatLng(53.7996388,-1.5491221),"Leeds",info
);
// Second marker
var info =
"<div class=infowindow>
<h1>Bradford</h1><p>Population: 467,665</p></div>";
addMarker(
new google.maps.LatLng(53.7938530,-1.7524422),"Bradford",info
);
}
Here's the code for download.
The main initialize()
function has also been updated. Instead of the full process of creating a marker that we used previously,
we have replaced this with a line that creates some content to be displayed in
the infowindow, and then a call to our new addMarker()
function:
var info =
"<div class=infowindow>
<h1>Leeds</h1><p>Population: 715,402</p></div>";
addMarker(
new google.maps.LatLng(53.7996388,-1.5491221),"Leeds",info
);
We have removed several lines of identical code, although the lines creating the two markers are still quite similar, suggesting that the process can be further generalised. The next example shows a further revision of this code to make it even more generalisable and compact:
var map;
var myCentreLat = 53.8;
var myCentreLng = -1.6;
var initialZoom = 10;
/*
* The data that we want to map
*/
var markerData = [
{'name': 'Leeds',
'lat': 53.7996388,
'lng': -1.5491221,
'pop': 715402},
{'name': 'Bradford',
'lat': 53.7938530,
'lng': -1.7524422,
'pop': 467665}
];
function infoCallback(infowindow, marker) {
return function() {
infowindow.open(map, marker);
};
}
function addMarker(myPos,myTitle,myInfo) {
var marker = new google.maps.Marker(
{position: myPos, map: map, title: myTitle}
);
var infowindow = new google.maps.InfoWindow({content: myInfo});
google.maps.event.addListener
(marker, 'click', infoCallback(infowindow, marker));
}
function initialize() {
var latlng = new google.maps.LatLng(myCentreLat,myCentreLng);
var myOptions = {
zoom: initialZoom,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map
(document.getElementById("map_canvas"), myOptions);
for (id in markerData) {
var info = "<div class=infowindow><h1>" +
markerData[id].name + "</h1><p>Population: " +
markerData[id].pop + "</p></div>";
addMarker(new google.maps.LatLng(
markerData[id].lat,markerData[id].lng), markerData[id].name,info);
}
}
Here's the code for download.
There are two significant changes. Firstly, we have added a new array called markerData
near the top of
the code, and secondly we have added a loop structure that uses this data near
the end of the code. The data array is constructed using the []
syntax to
declare an array. As shown it consists of two entries. Each entry is an object,
declared using the JSON {}
notation as a series of key:value
pairs. Four
properties are defined in each object – a name, a latitude, a longitude and a
population count. Note that we (may) want to use the population as a numeric
value so it is written as a number (without a ',' separator for the
thousands).
Notice that while the new loop is a for loop, it is a different type to that illustrated in the introduction to JavaScript material:
for (id in markerData) {
var info = "<div class=infowindow><h1>" +
markerData[id].name + "</h1><p>Population: " +
markerData[id].pop + "</p></div>";
addMarker(new google.maps.LatLng(
markerData[id].lat,markerData[id].lng), markerData[id].name,info);
}
In this style, the
generic form is for (variable in object)
{statement}
. The loop cycles through each element of
object
(often an array) and carries out statement
. In each
iteration, variable
will allow us to access members of the object. In
this case, id
acts as an index value for entries in the array
markerData
. Note, however, that what you get out of variable
can change depending on the type of object you are looping through: for
an array of objects like this, we get an index number; for other kinds of collections we may get actual objects out. Whatever we get out, we do not need to know or worry about how many entries
there are in the object – the for loop looks after this for us.
This approach allows us to refer to values using standard notation, for
example markerData[id].name
is the value of the property
name
for the id
th element of
the array
markerData
. In the body of the loop, we construct a
version of the variable info
, joining together strings with values
from the data array. We then call the addMarker()
function,
supplying a new LatLng
object (declared using lat
and
lng
values from our data array), the name
value and
the new info
string.
Adding additional markers can now be done solely by extending the markerData
array with new entries. We can also revise the page structure slightly to move
the data into a separate file, thus separating the page layout (the HTML
document), the map construction code (the main JavaScript file) and the map content
(the marker data). This has been done in the following files:
The first file shown is google_eg10.html, the HTML page that hosts our map. It is largely the same as our previous examples, with the important exception that we have added another script tag in the HEAD section, to load the file markerdata_google_eg10.js.
The HTML file google_eg10.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>
<script type="text/javascript" src="markerdata_google_eg10.js"></script>
<script type="text/javascript" src="map_setup_google_eg10.js"></script>
<link rel="stylesheet" media="all" href="map_style_eg10.css" type="text/css">
<title>Google Maps example 10</title>
</head>
<body onload="initialize()">
<h1>Google Maps examples</h1>
<p>
[<a href="../index.html">Personal home page</a> |
<a href="index.html">Geog5870 work</a> ]</p>
<div class="gmap" id="map_canvas" style="height: 400px; width: 600px"></div>
<p>This example uses generalised methods to display a set of markers</p>
<p>Data contained in marker information show counts from the 2001 Census<p>
<ul>
<li>Source: 2001 Census: Standard Area Statistics (England and Wales)
<li>Census output is Crown copyright and
is reproduced with the permission of the Controller
of HMSO and the Queen's Printer for Scotland.
</ul>
</body>
</html>
We have also modified the page text to include citation information for the data used, following the wording shown on the census citation page.
The second file is map_setup_google_eg10.js. Again, this is largely the same as in our previous example; the only difference is that we have removed the declaration of the markerdata array from this file.
The main JavaScript file map_setup_google_eg10.js
var map;
var myCentreLat = 53.8;
var myCentreLng = -1.6;
var initialZoom = 10;
function infoCallback(infowindow, marker) {
return function() {
infowindow.open(map, marker);
};
}
function addMarker(myPos,myTitle,myInfo) {
var marker = new google.maps.Marker(
{position: myPos, map: map, title: myTitle}
);
var infowindow = new google.maps.InfoWindow({content: myInfo});
google.maps.event.addListener
(marker, 'click', infoCallback(infowindow, marker));
}
function initialize() {
var latlng = new google.maps.LatLng(myCentreLat,myCentreLng);
var myOptions = {
zoom: initialZoom,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map
(document.getElementById("map_canvas"), myOptions);
for (id in markerData) {
var info = "<div class=infowindow><h1>" +
markerData[id].name + "</h1><p>Population: " +
markerData[id].pop + "</p></div>";
addMarker(new google.maps.LatLng(
markerData[id].lat,markerData[id].lng), markerData[id].name,info);
}
Instead, this array is declared in a separate file, markerdata_google_eg10.js. The format is the same as before; here it has been extended to include three other markers (and thus shows all districts in West Yorkshire).
The JavaScript file containing the data
markerdata_google_eg10.js
/*
* The data that we want to map
*/
var markerData = [
{'name': 'Leeds',
'lat': 53.7996388,
'lng': -1.5491221,
'pop': 715402},
{'name': 'Bradford',
'lat': 53.7938530,
'lng': -1.7524422,
'pop': 467665},
{'name': 'Calderdale',
'lat': 53.7420418,
'lng': -1.9952690,
'pop': 192405},
{'name': 'Kirklees',
'lat': 53.5933432,
'lng': -1.8009509,
'pop': 388567},
{'name': 'Wakefield',
'lat': 53.6829650,
'lng': -1.4990970,
'pop': 315172}
];
The final file shown is a revised version of map_style.css. This
has been expanded to include some styles for the infowindow
class.
The CSS file
map_style_eg10.css
body {
background-color: white;
color: black;
font-size: small;
font-family: Arial,Helvetica,san-serif;
}
.gmap {
margin: 5px;
border: thin solid black; float: right;
}
.infowindow{
border: thin solid black;
}
.infowindow h1{
border: thin solid gray;
background-color: #eeeeee;
}
The first new entry draws a border around the window, and the second one
illustrates the way in which we can supply an alternate style for H1 heading
elements inside a division of a given class (in this case, infowindow
). These
styles are probably not what you would want in practice, and are simply shown
to illustrate the way that styles can be applied.
addMarker()
function that includes an event listeneraddMarker()
for all the markers in an array