Part 2: Google Maps
Section 1: Introduction to Part 2
When approaching any sufficiently high-level computing topic, it is often true that we need to be familiar with a broad range of underlying and related concepts to establish the foundation necessary to learn the new material. Virtually every technology not only borrows from, but also builds on others. Furthermore, it is often the case with programming projects that the bulk of the work comes at the beginning and involves carefully thinking through the problem, devising a solution, and building those parts of the project that we need to write before we can get to the code we want to write.
In the first part of the tutorial we wrote a Perl script which allows us to generate valid KML files from collections of geotagged photos. Using our new tool, we generated a number of files containing all of the data (about our photos) that we'll use to populate our Google Maps. We also looked at the structure of KML, and covered a number of other important topics, which are key to understanding the material presented here. If you have not read part 1, do take the time at least to glance through it to confirm for yourself that you won't have trouble following along with this discussion.
Now we turn our attention to Google Maps.
After this short introduction we'll handle the tutorial in 3 parts.
Part 1: This section discusses the document-object-model (DOM), which is a formal methodology for negotiating XML files, like the KML files we created in part 1.
Part 2: Next we'll look at XHTML and the XHTML file we'll use to display our Google Map. As you'll see, the file included with the project is no more than a shell, but it does suffice to allow us to present our map. Furthermore, it affords us an opportunity to talk about integrating XHTML, Javacript, and the Google Maps API. There are a few important things we'll need to understand.
Working with a simple page will help us avoid complicating the issues unnecessarily and will make it easy for you to relocate the map to a page of your own (maybe as part of a weblog for example) with a minimum amount of fuss.
Before we get to all of that, there are a couple of preliminary topics to talk about:
We've already looked at XML, describing it as an open, extensible markup language. We've said that XML is a metalanguage which allows us to define other languages with specific syntactic structures and vocabularies tailored to discrete problem domains. Furthermore we've said that KML is one such XML-based language, engineered to address the problem of describing geographical data and the positioning of geographical placemarks and other features to display on a map, among other applications.
We haven't yet talked about XHTML which is a reimplementation of HTML as a standard, XML-based format. HTML (Hypertext Markup Language) has served as the preeminent language for authoring documents on the World Wide Web since the web's inception in the late 1980s and early 1990s.
HTML is the markup language1; which makes hypertext2; linking between documents on the web possible and defines the set of tags, i.e. elements and attributes, that indicate the structure of a document using plain-text codes included alongside the content itself. Just as the web was not the first hypertext system3, HTML was not the first markup language, though both have become extremely important.
Without delving too deeply into the primordial history of markup, HTML is a descendant of SGML (Standard Generalized Markup Language) which is itself an offspring of GML (Generalized Markup Language).
The Wikipedia entry for GML offers this description of the language:
GML frees document creators from specific document formatting concerns such as font specification, line spacing, and page layout required by Script.
SCRIPT was an early text formatting language developed by IBM, and a precursor to modern page description languages like Postscript and LaTeX, that describe the structure and appearance of documents. Not surprisingly the goal of GML is echoed in the intent of HTML, though the two are far removed from each other.
Berners-Lee; considered HTML to be an application of SGML from the very beginning, but with a clear emphasis on simplicity and winnowing down much of the overhead that had always characterized formal markup languages. In the early days, the web had to struggle for acceptance. The birth of the web is a story of humble beginnings4. The significance of the web was by no means a forgone conclusion and early adoption required that the markup syntax of the web be as accessible as possible. After all, more stodgy languages already existed.
The simplicity of HTML certainly contributed to the success of the web (among many other factors), but it also meant that the standard was lacking in key areas. As the web gained wider appeal there was a tendency to take advantage of the early permissiveness of the standard. Developers of mainstream web browsers exacerbated the problem significantly by tolerating noncompliant documents and encouraging the use of proprietary tags in defiance of efforts on the part of standards organizations to reign in the syntax of the language.
In the short term, this was a confusing situation for everyone and led to incompatibilities and erratic behavior among an ever increasing multitude of documents on the web and the various web browsers available, which differed in their support for, and interpretation of, what were in essence emerging 'dialects' of HTML. These web browsers differed not only from one another, but also frequently from one version to the next of the same application.
There was a real need to establish stability in the language. Lack of dependable syntax meant that the job of building a parser capable of adequately adhering to the standards, and at the same time accommodating various abuses of the standard, had become an exercise in futility. Unlike HTML, XHTML must strictly adhere to the XML standard, which means that any compliant XML parser can negotiate properly formatted documents with relative ease. This reliability paves the way for a more efficient and sophisticated web, resulting in not only more consistent rendering of content, but the development of software that is able to differentiate and act on content based on context, and relationships among data. This is one of the primary advantages of XML, as has already been discussed, and this is a key advantage of XHTML as well.
Long term, Berners-Lee, who currently serves as the director of the World Wide Web Consortium (W3C); and a senior researcher at MIT's Computer Science and Artificial Intelligence Laboratory (CSAIL), continues to actively pursue the vision of a Semantic Web. That is, a web that can be mined as a rich repository of data by intelligent software agents, which not only present information, perform basic pattern matching, and the like, but are capable of analysing information (in a truer sense—keep this or get rid of it?).
From the W3C's Semantic Web Roadmap we have this brief introduction to the concept:
The Web was designed as an information space, with the goal that it should be useful not only for human-human communication, but also that machines would be able to participate and help. One of the major obstacles to this has been the fact that most information on the Web is designed for human consumption, and even if it was derived from a database with well defined meanings (in at least some terms) for its columns, that the structure of the data is not evident to a robot browsing the web. Leaving aside the artificial intelligence problem of training machines to behave like people, the Semantic Web approach instead develops languages for expressing information in a machine processable form.
Whether this vision accurately predicts the future of the web remains to be seen. At the moment the Semantic Web is a veritable stew of protocols, frameworks, concepts, extensions, namespaces, vocabularies, schema, ontologies, and other components. It is the subject of much debate, virtually all of it far beyond the scope of this tutorial. But this 'brave new web' is not purely academic.
The increased emphasis on standards-compliant markup has resulted in developers of web browsers and content creation tools steering their apps toward the standards. This in turn motivates authors to produce compliant documents. In fact the Strict variations of XHTML do not allow for deviation from the standards. Two immediate benefits of this work, whether or not it ultimately leads to some future web, are (1) more consistent document rendering across platforms among mainstream web browsers, (2) and the emergence of the Document Object Model (DOM). We'll look at the DOM shortly.
Section 2: Object Models and the DOM
An object model is a collection of objects and the typically hierarchical, often complex, relationships among them, where the most generic definition of an object is simply a 'thing'. Object models are all around us and are not limited to programming or the broader topic of computing for that matter. If, for example, you were to describe a car in terms of its object model:
You might start by talking about the chassis, or base frame, and then go on to describe the body of the car, its wheels, axles, exhaust system, the drivetrain, etc.; everything that is directly attached to the frame. For each of these you could describe various attributes and elements of that object; for e.g. the body material is an attribute of the body object. The elements may in fact be other objects that collectively comprise those objects within which they are contained.
The body of the car contains the engine compartment, passenger compartment, and trunk elements. The engine compartment includes the engine which is itself a collection of smaller objects. The passenger compartment can be described as the collection of front and rear compartment objects, where the front compartment object includes at least a driver side compartment with a steering wheel, instrument panel, etc. These objects have their own attributes and elements perhaps consisting of other objects each of which is an object itself, with a more specific function than the objects in which they are contained.
If you've ever put together a toy model of a car you may remember seeing an exploded schematic diagram of the completed model which clearly shows all of the objects from the largest container elements to the smallest ties, screws and other fasteners that hold all of the pieces together. This is a nice way to visualize the object model of the toy car.
Of course the object model I have described is only an informal example. It may be fair to dispute the model I've sketched here. As long as you have a better general understanding of what an object model is then this example has served its purpose.
Object models are very common within the realms of computer technology, information technology, programming languages, and formal notation.
The Document Object Model
The Document Object Model (DOM); is a standard object model for describing, accessing and manipulating HTML documents and XML-based formats. The often quoted description of the DOM from the W3C's site dedicated to the specification is:
The Document Object Model is a platform- and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents. The document can be further processed and the results of that processing can be incorporated back into the presented page.
Remember that XHTML is a special case of XML (i.e. it is an XML based format) and essentially no more than a formalization of HTML. Because it is an XML based format the XML DOM applies. Furthermore, because XHTML describes a single format with a number of required structural elements and only a limited collection of allowable elements and attributes, the HTML DOM has a number of unique objects and methods for acting on those objects that are not available in the more generic XML DOM. Having said that, I want to emphasize the the two are more alike than the are different.
Technically we will look at the XML DOM here but nearly everything discussed is applicable to the HTML DOM as well. Though there are differences, some of which we'll encounter later in this tutorial, as an introduction it is appropriate to limit ourselves to fundamental concepts, which the two share in common. We need both. We'll rely on the XML DOM to parse the KML files we generated in part one of the tutorial to populate our Google Map, and the HTML DOM to add the map to our webpage. By the time we've completed this part of the tutorial hopefully you will appreciate just how integral the DOM is for modern web design and development, though we'll only have skimmed the surface of it.
The concept of the DOM is actually quite easily understood. It will seem intuitive to anyone who has ever dealt with tree structures (from filesystem hierarchies to family trees). Even if you haven't had any experience with this sort of data structure, you should anticipate being able to pick it up quickly.
Under the Document Object Model individual components of the structure are referred to as nodes. Elements, attributes and the text contained within elements are all nodes.
The DOM represents an XML document as an inverted tree with a root node at the top. As far as the structure of an actual XML document is concerned, the root is the element that contains all others. In every structure all other nodes are contained within a document node. In the KML files we've generated the Document element is the root element.
Every other node is a descendent of Document. We can express the reciprocal relationship by stating that Document is an ancestor of every element other than itself.
Relationships among nodes of the document are described in familial terms. Direct descendants are called child nodes or simply children of the their immediate ancestor, referred to as a parent.
In our KML files,
We can make a couple of other useful observations about this structure:
- Every node other than the root has exactly one parent.
- Parents may have any number of children including zero, though a node without any children won't be referred to as a parent. (A node with no children is called a leaf.)
Implicitly there are other familial relationships among nodes. For example, elements with parents that are siblings could be thought of as 'cousins' I suppose, but it is unusual to see these relationships named or otherwise acknowledged.
There is one important subtlety. Text is always stored in a text node and never directly in some other element node. For example, the description elements in our KML files contain either plain text or html descriptions of associated Placemarks. This text is not contained directly in the description node. Instead the description node contains unseen text node which contains the descriptive text. So the text is a grandchild of the description node, and a child of a text node, which is the direct descendent of description. Make sure that you understand this before continuing.
Because of the inherent structure of XML, we can unambiguously navigate a document without knowing anything else about it, except that it validates. We can move around the tree without being able to name the nodes before we begin. Starting at the root document node, we can traverse that node's children, move laterally among siblings, travel more deeply from parent to child, and then work our way back up the tree negotiating parent relationships. We haven't yet described how we move among siblings.
The DOM allows us to treat siblings as a list of nodes, and take advantage of the relationships that exist among elements in any list. Specifically, we can refer to the first (firstChild) and last (lastChild) nodes to position ourselves in the list. Once we are at some location in the list, we can refer to the previous (previousSibling) and next (nextSibling) nodes to navigate among siblings. Programmatically we can use the DOM to treat siblings in a XML data structure as we would any other list. For example, we can loop through sibling nodes working on each node in turn.
Keep in mind that we are using generic terminology, not referring to specific node names and we are relying only on the structure of XML which we know must be dependable if the document adheres to the standard. This will work well for our KML files, and it is certainly not limited to KML.
There are primarily two techniques we can use to find and manipulate elements in our XML structure using the DOM.
- Node Properties
Firstly, we can take advantage of relationships among element nodes as we have been discussing.
A number of node properties, some of which have already been mentioned, allow us to move between nodes in the structure. These include:
If we look at a fragment of KML, similar to the KML files generated in part 1 of the tutorial, starting at the Placemark element...
...we see a number of these properties:
is the firstChild of
is the lastChild of
- The nextSibling of
- The previousSibling of
is the parentNode of , , , and
Secondly, we can use the method getElementsByTagName() to find any element regardless of the document structure.
For example, using the syntax...
...we can retrieve all
elements as a nodeList from the document which are descendants of the element we are using when we call the method.
elements in the document and stores that list at the variable list_of_nodes.
var list_of_nodes = getElementsByTagName("name");
What is a node list?
A node list (NodeList) is an object representing an ordered list of nodes, where each node represents an element in the XML document. The order of the NodeList is the same as the order of the elements in the document. Elements at the top of the document appear first in the list, and the first element returned, i.e. the first position in the list, is numbered 0.
Keep in mind that the list includes all <name> elements. If you look at the KML files we've generated you may notice that both <folder> and <placemark> elements contain <name>. We need to be aware that getElementsByTagName("name") will return all of these if we are starting at the document root. We can differentiate between these <name> elements in a number of different ways. For example we can insist that the node is a child of a Placemark node to exclude <name> elements that are the children of <folder> elements.
We need to be able to refer to various properties of these nodes if we are going to act on them in any reasonable way. The XML DOM exposes several useful node properties incl: nodeName, nodeValue, and nodeType.
nodeName: is (quite obviously) the name of the node. What might be less obvious is precisely how the DOM defines nodeName.
- The tag name is always the name of an element, e.g. 'Placemark' is the name of the <Placemark> elements
- The attribute name is the nodeName of an attribute, e.g. the name of the maxLines attribute of <Snippet> is 'maxLines'
- The name of any text node is always the string '#text'. e.g., the plain text or html that we're using as our Placemark descriptions are each contained in a text node, as has already been discussed, and the name of this text node is '#text', and not the name of the element which surrounds the value in the XML document
- The nodeName of the root document node is always the literal string '#document'.
- The value of text nodes is text itself. So the text node of one of our Placemark
elements is all of the plain-text or html within the description tags. Again, as far as the DOM is concerned the text is actually contained within a text node which is a child of a description node
- The value of an attribute node is simply the attribute value. e.g. maxLines="1" has a nodeValue of 1
- nodeValue is not defined for the document node and all element nodes.
nodeValue: is what you might expect.
nodeType: is certainly not something you could guess.
A specific value is assigned to each of the available types (categories) of nodes. The following is an incomplete list of common node types.
- Element, type 1
- Attribute, type 2
- Text, type 3
- Comment, type 8
- Document, type 9
Section 3: XHTML
There are some noteworthy parts of the page you will want to pay attention to:
- The page is compliant with the XHTML 1.0 Strict standard as written. It could just as easily be XHTML 1.1, HTML 4.0.1 Transitional, or any other specification. In the case of XHTML 1.1, all that would need to change is the doctype declaration and MIME type:
Compare the two doctype declarations
XHTML 1.0 Strict
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
and the MIME types
XHTML 1.0 Strict
I chose XHTML 1.0 Strict because it is an XML based format and closer to HTML 4.01 than XHTML 1.1. Furthermore, you may need to reconfigure your web server or change the extensions on your XHTML 1.1 files before your server will deliver the proper MIME types. Again, this point is fairly trivial considering how simple the page is. However, it does present an opportunity to mention validation.
We've already discussed validating the KML files generated in the first part of this tutorial. In that case we used the Feed Validator at http://feedvalidator.org/ to confirm that our KML files were up to spec so that we wouldn't run into trouble opening the files in Google Earth, or when it came time to incorporate them into our Google Maps script.
Here we'll use the W3C's Markup Validation Service (http://validator.w3.org/) to check our XHTML file. The procedure is similar to what we did with KML validation though we're using a different validation service against a different type of file.
- Simply visit the validator's page in your web browser,
- type or paste the complete URI to your map page at the text entry box labeled "Address:', and
- click the 'Check' button.
A valid page will result in the following message:
"This Page Is Valid XHTML 1.0 Strict!"
while an invalid page will produce the error message:
"This page is not Valid XHTML 1.0 Strict!"
Like the Feed Validator, the W3C validation service makes a distinction between errors, which are failures of the page to comply with the standard and must be corrected, and warnings, which are detected deviations from what may be considered ideal. You should take these warnings under advisement and correct as many as you are able to, unless you're intentionally ignoring them for some good reason.
Besides validating a page publicly available on the web by URI, you have the option of uploading a local file, or even copying and pasting a full document into a text entry box for validation, what the validator calls 'Validate by Direct Input'. There are some options you can use to affect the behavior of the service. The default behavior should be fine, but using options you can do things like:
- temporarily override the automatically detected Document Type
- group reported error messages by type
- display the source of the page being validated with the results
- fix problems with your page using the HTML Tidy library, which is code that can correct common editing mistakes (for example it can add closing tags, fix simple tag nesting issues, etc.) and apply a consistent structural style to messily written pages
The validator is a valuable tool for all of us and I encourage you to make a habit of using it, especially when designing your own pages, including weblog templates or themes. Keep in mind that there is no such thing as a personal publicly accessible web page. If your site is accessible on the web then you have a responsibility to produce clean markup for your visitors and the rest of the global community. If you're unmotivated by the argument that you should validate because it's 'the right thing to do', then keep in mind that invalid markup may effectively reduce the size of your audience and impact your site's ranking with search engines. Furthermore, nonstandard markup is ultimately more time-consuming and difficult to maintain, and is likely to break in unpredictable ways.
This brings up a dirty little not-so-secret truth about some of the more popular web services, which are becoming increasingly important these days. Many very useful, slick-looking web services generate broken markup. What can you do about it? Well, from a practical standpoint, don't be too upset by this. The services wouldn't be popular if they weren't functional. So, if you want to use one of them and it works for you and your audience then I say go for it.
Of course, this is no excuse to produce invalid markup yourself. In fact, this is yet another very practical reason to do everything you can to keep your own code as compliant as possible. The problems you introduce that work on their own may react badly and unpredictably with the problems you inherit from others, which may also function correctly until they conflict with your broken markup. These are likely to be very difficult issues to diagnose and resolve, which is good reason to avoid them to begin with. There is a well known quote from internet pioneer Jon Postel (http://en.wikipedia.org/wiki/Jon_Postel) that applies here,
"be conservative in what you do, be liberal in what you accept from others".
Of course, feel free to complain to whichever organization is responsible for the web services you use that are fouling up your otherwise standards-compliant site. Something along the lines of...
Thank you for the service you provide but please invest whatever resources are necessary to clean up the code you're asking others to embed in their sites
...is probably the right approach.
- Notice the
From the Google Maps API documentation:
We provide some information to Google with our request. We need only refer to the official documentation to learn about these and possibly other available parameters.
From the documentation:
The v parameter within the http://maps.google.com/maps?file=api&v=2 URL refers to the version number of the Google Maps API to use. Most users of the API will want to use the stable "Version 2" API by passing the v=2 parameter within that URL. You may instead obtain the latest release (including the latest features) by passing v=2.x instead. However, be aware that the latest release may not be as stable as the v=2 release. We update the Google Maps API about every two weeks, at which point features within the v=2.x release are migrated into the stable v=2 release unless problems are discovered.
We have already identified the 'key' parameter which is required to authorize the page against a valid Google Maps API key. Remember that these keys are tied to a specific directory on your server. You must keep your pages that contain code that references the API in the directory you specified when you requested the key. It will not work with pages in subdirectories.
You can request additional keys to serve pages from multiple directories or in the case that you lose track of the key(s) you have already generated. You do not need to request that unused or misplaced keys be revoked. In fact there is no established procedure for doing this even if you wanted to.
- in the head section of the page,
- the body of the page,
- in an external file referenced by the script tag's src attribute.
Except where appropriate the other two techniques mentioned amount to no more than bad habit which you should work to break or be careful to avoid in the first place. Even with a page as simple as this, separating the structure of the XHTML page from the behavior of the code makes it easier to discuss each as discrete aspects of the project.
<:body onload="setup_map()" onunload="GUnload()">
As you might guess, the behavior associated with the event onunload is triggered when the visitor leaves the page. GUnload() is a function from the Google Maps API that takes care of cleaning up the data structures created by the API.
From the documentation:
function GUnload - You can call this function to cause the map API to cleanup internal data structures to release memory. This helps you to work around various browser bugs that cause memory leaks in web applications. You should call this function in the unload event handler of your page. After this function was called, the map objects that you've created in this page will be dysfunctional.
As you can see from this description, we're doing the right thing by associating GUnload with the onunload event.
We'll be dealing with the Google Maps API and taking a closer look at setup_maps() later in the tutorial.
- I'm including a basic stylesheet which does no more than center the map on the page horizontally, and style the font used on the page. For the purposes of this tutorial, you can either keep the CSS file as it is, make any modifications you like, or even choose not to use it altogether.
If you do choose to forgo the stylesheet, just delete the included tutorial.css file and remove the link tag from the head section of the XHTML file.
If you keep the file, notice that I've used a relative link. The style info will be available to your XHTML page without making any changes as long as you keep the filename 'tutorial.css' and place the file in the same directory as your XHTML page. You are free to use any filename you like at any location by simply changing the value of the src attribute. Any relative path will work, as will absolute paths from your web server's document root, and fully qualified URLs, e.g. http://sample.net/maps_tutorial/tutorial.css.
There is one bit of style information that I am purposefully keeping out of the external stylesheet and instead including in the XHTML file as an inline style; that is the height and width of the div which will contain our map.
From the Google Maps API Documentation:
When you create a new map instance, you specify a named element in the page (usually a div element) to contain the map. Unless you specify a size explicitly for the map, the map uses the size of the container to determine its size.
Other than what has already been discussed, the XHTML file consists of a number of div elements each named by an id attribute. For example:
Is the container for our map, as we have discussed.
The other divs specify locations on the page where we'll output status messages related to the operation of our script.
After we request the photos.kml file from our server, so that we can include markers for each of our photos on our map. The response code returned from our web server will be written to
The other divs are target locations for response codes resulting from requests for the other KML files we need to download, and some basic statistics, e.g. the number of markers generated, etc.
Section 4: A Note about Web Programming
Firstly, the success or failure of our code is dependent on many factors that are out of our control.
- If you are connecting to a remote machine and that computer is malfunctioning, then your code may fail to execute as expected.
- Transient networking issues may cause significant problems even if the machines and all of the code involved are behaving properly.
- Network applications tend to get complicated quickly because the requirement of network communication means that the applications depend on many underlying network services (e.g. name resolution) and network devices (routers, switches, other gateway devices, etc.). What's more, because of increasing concerns about security, many of these devices are intentionally designed to interfere with network communications. Of course the idea is that this interference affects only unwanted or harmful apps but practically speaking this is virtually impossible.
- Compromises inherent in the design of the security mechanisms, configuration errors, other mistakes introduced during development or implementation, and the intentional actions of those agents who endeavor to abuse the network all contribute to the sorts of problems you may encounter when developing network applications.
A page as simple as the XHTML file included with this tutorial will not contribute any errors or warnings to the messages you see in the Error Console. Once you have your map working, it's a trivial task to move it to another page simply by copying and pasting the script tags to your new page, and adding a 'map' div where you want the map to be displayed.
From the Introduction on the Venkman project page:
Section 5: An Introduction to the Code
We'll begin with a high level, natural language discussion that faithfully describes the code.
The second and third passes can be found in the source file itself, accompanying this tutorial. What I'm referring to as a second pass are the detailed comments you will find throughout the source. Pass 3 is the code itself.
This discussion will frequently refer to the official Google Maps documentation pages and version two of the API Reference. The documentation is well written and fairly easy to understand. After you have finished with this tutorial you will want to remember to spend some time reviewing the API reference. You will no doubt find answers to many of your questions and discover new ways of doing things. Where the descriptions presented in the reference are clearly written (and relevant of course) I will quote them here rather than complicating the issue with my own clumsy explanations.
Section 6: Natural language walk-through
The first function definition we see is setup_map(). You may remember from the the discussion about the XHTML page above that setup_map() is attached to the onload event on our page. Accordingly, this function is called when the page is loaded.
First, we check that the browser is compatible with the Google Maps API using the function GBrowserIsCompatible().
From the API Reference:
This function decides whether the maps API can be used in the current browser.
If the browser is compatible then we continue, or else we return having done nothing. You may want to handle the case that the browser is incompatible with the API by writing a helpful message to the page. Alternatively, you could do something more complicated, but certainly you won't be generating a map so the best way to handle this may be a simple message that is informative without wasting your visitors' time or causing some unexpected behavior.
Assuming the browser is compatible we generate a map by creating an instance of the class GMap2. The class defines a Google Map, and we can request an instance of that class with a single statement:
map = new GMap2(document.getElementById("map_container"));
From the API Reference:
class GMap2 - Instantiate class GMap2 in order to create a map. This is the central class in the API. Everything else is auxiliary.
Creates a new map inside of the given HTML container, which is typically a DIV element. If no set of map types is given in the optional argument opts.mapTypes, the default set G_DEFAULT_MAP_TYPES is used. If no size is given in the optional argument opts.size, then the size of the container is used. If opts.size is given, then the container element of the map is resized accordingly. See class GMapOptions.
You can safely ignore the discussion of optional arguments. In the absence of these options sensible defaults will be used.
From the statement above:
map, which is the target of the assignment, is a variable which going forward will be a reference to the map object created with this statement. We will manipulate this object throughout the execution of the script to modify the map that appears on the page.
There is also "map_container", which is the structural element of the page that will contain our map. We saw this div when we looked at our XHTML file.
Note the use of the HTML DOM in the statement. Though our use of the XML DOM with the KML files containing our Placemark data will be a little more extensive, what we do with the HTML DOM will not be much more complicated than what we see here.
We have already discussed the method getElementsByTagName() when we looked at the XML DOM. getElementById() is similar. We simply pass the method the unique name of one of the id attributes included on the page to target the named container. Because all id names are unique we can ignore the nested structure of the document and relationship among elements when using getElementById. Technically the method returns a reference to the first object with the named id, but there should be only one.
In the HTML DOM, the document object represents the entire page. So,
returns a reference to the first container with the id "map" anywhere on the page.
Adding standard controls to our map is just as easy thanks to the API.
This statement adds the built-in large pan and zoom control. This the standard control which typically appears along the left side of a Google Map and allows the user to select the zoom level of the map either by incrementally increasing or decreasing the level or jumping directly to the desired resolution utilizing a slider. The control also allows visitors to pan the map in any direction.
The statement instructs the API to create a new GLargeMapControl() object and then add the control to our map object.
Alternatively we could have opted for the equally common smaller control with the statement,
which offers much the same functionality but lacks the slider. The advantage is that the control takes up significantly less space. This may be especially important for smaller maps. The choice is yours.
Next we'll add a type control which allows visitors to switch between the three primary map views, 'Map', 'Satellite', and 'Hybrid':
This is functionality that most users appreciate and it does seem appropriate for our map of photos. Map view is often the least cluttered of the three, and so the least distracting, and the easiest to navigate. Satellite view on the other hand may help users better understand the context of our photos. Hybrid view combines the two so that we can have the satellite imagery but still find our way around the map with street names and other important indicators.
It is possible to implement your own controls by taking advantage of several classes Google makes available via the API for enabling and disabling standard overlay information and toggling between map types. You may have a good reason for doing this, and if so I don't want to discourage you. I do want to say however, that unless you have cause to do something else, keeping the standard controls is recommended. Chances are good that your visitors will be familiar with the standard controls and replacing them just for the sake of being different is unnecessarily disorienting for users. Interface consistency should be one of the primary goals of any developer. Keep in mind that your map is essentially an instance of the Google Maps application. The more consistent we all are, the more comfortable our visitors will be with our collective maps.
We've created a map (an instance of the GMap2 class), specified where to place the map on the page, and we've added some controls. Before we can expect the Google API to be able to generate a map, we need to tell it what to display, i.e. what location to show us. We do this by setting the center point of the map and defining an initial zoom level. Taken together, a center point, zoom level, and the size of the map (determined by the height and width attributes of the 'map' div on our XHTML page), completely defines the map that we see. A better way to think about an interactive Google Map might be to imagine it as a dynamic window offering a view of a potentially much larger map of the world. We can change the resolution of that view by adjusting the zoom level, or adjust our vantage point by panning our view of the map.
The API provides the function setCenter() to set the position of the map.
From the reference:
Sets the map view to the given center. Optionally, also sets zoom level and map type. The map type must be known to the map. See the constructor, and the method addMapType(). This method must be called first after construction to set the initial state of the map. It is an error to call other operations on the map after construction.
Do not overlook this line from that description:
This method must be called first after construction to set the initial state of the map.
This statement, from setup_map() sets the center of our map object.
setCenter must be provided a point and a zoom level. Technically the zoom level is optional, but it's one of those optional arguments that you will specify most of the time.
Generally a point is a coordinate pair, i.e. standard latitude and longitude values. When working with the Google Maps API, a coordinate pair is represented by a GLatLng object.
From the API reference:
GLatLng is a point in geographical coordinates longitude and latitude.
Notice that although usual map projections associate longitude with the x-coordinate of the map, and latitude with the y-coordinate, the latitude cooridnate is always written first, followed by the longitude, as it is custom in cartography.
Notice also that you cannot modify the coordinates of a GLatLng. If you want to compute another point, you have to create a new one.
When working with GLatLng objects be very careful about the order of the values in the pair. The reference tells us that the latitude must always be written first. Also notice that the latitude and longitude must be specified in decimal degrees, as opposed to degrees, minutes, and seconds. We have already taken care of converting the coordinate metadata from our photos to the proper format in the first part of the project.
The second argument in the call to setCenter above is the zoom level. Google Maps allows for a range of zoom levels. The higher the integer value the greater the amount of detail (the higher the resolution) of the map. Of course higher resolution comes at the expense of field of view. In other words, as we make the map larger, we get a more detailed image of a smaller area. This correctly implies that smaller zoom level values (lower integer values) present a wider field of view (more of the Earth is visible on the map) but less detail (any specific area is smaller in terms of the number of pixels it occupies).
The smallest zoom level available is 0, which displays a map of the entire world.
At a zoom level of 4 we can fit all of (Western) Europe on a map roughly 800px wide by 500px tall: http://maps.google.com/?ie=UTF8&ll=47.279229,21.884766&spn=30.453577,72.246094&z=4&om=1
Increasing the zoom level to 6, adjusts the resolution such that France occupies nearly the entire map: http://maps.google.com/?ie=UTF8&om=1&ll=46.800059,4.130859&spn=7.671813,18.061523&z=6
and at 12 we have a map just larger than the city of Paris in France: http://maps.google.com/?ie=UTF8&om=1&ll=48.856358,2.350731&spn=0.115203,0.282211&z=12
There is one issue about zoom level that we want to be careful to remember. Though 0 consistently represents the low end of the scale, always producing a very wide world map, the high end is less reliable. In an urban area, for example the city of Paris, high zoom levels are likely to be useful because at this level we can clearly distinguish between individual streets, and even buildings and landmarks, like this satellite map of the Eiffel Tower in Paris at zoom level 17.
On the other hand, there are many areas of the planet where high zoom levels will result in a map displaying nothing of any interest. This issue is especially problematic with Satellite maps. The satellite imagery available via Google Maps is fairly inconsistent and somewhat unpredictable at high zoom levels. The problem is that Google simply does not have high resolution satellite imagery covering the entire planet. Where no images exist instead of a map you will see the message 'We are sorry, but we don't have imagery at this zoom level for this region.'.
Your only choice is to move to a different region, or reduce the zoom level until you reach a point where a lower resolution image is available. We need to be aware of these issues when working with Google Maps or our visitors may find themselves staring at a broken looking page.
In some cases, adding a feature to our map is as simple as invoking a function from the API with a single statement, as is the case with the next line from our setup_map function.
At this point we've defined our initial map. It is complete except that there are no markers. There are no icons indicating the location of the markers, no info windows with descriptive text, thumbnails, and links to our photo gallery attached to the non-existent markers. In fact there is nothing linking our photos to the map whatsoever. This is no small omission. We had better get started on customizing our rather generic map.
Still in the setup_map function, we define all of the custom icons we'll use in our project. Icons (GIcon) are very visible components on the map.
Icons are the symbols that represent markers at a particular point on the map but you should think of the marker (GMarker) as the primary object. It is this object which will represent each of our photos. The marker includes an icon (GIcon) described above, a point (GPoint) which describes where to place the marker, and optionally an info window (GInfoWindow) which is an area of content containing descriptive information about the marker displayed when it is clicked. It is not uncommon for people to confuse points and markers either because they do not understand the distinction or because of simple carelessness, but the two are not interchangeable. We'll discuss markers later. Here we're defining the icons we'll use so that later we can refer to them by name.
Icons are a required component of markers but it is not necessary to create custom icons. If we don't supply a custom icon then Google Maps will use a default.
Why then go through the bother of creating custom icons at all? There is no single answer to that question. In fact it's perfectly acceptable to decide to use the defaults. Someone at Google has taken the time to design icons that work quite well, and it would be a mistake to waste your time and effort creating icons that aren't as nice. But there are a number of common reasons why you might want to consider creating your own, and why I have decided to include custom icons with this project.
- Branding and identification of markers as belonging to a specific application or set.
It's possible for a Google map to include not only your markers but also markers from Google (e.g. search results), and even third party applications. The term 'Mashup' has been coined to describe an application that combines data from multiple sources to create what is in some sense a completely new application. Mashups can be much more than the sum of their collected data sources. Custom icons allow users to distinguish markers related to your data from all of the others.
- Custom markers can communicate more information than the default icon.
Using a default icon tells a visitor where a marker is located but not much else. With custom icons you can vary the size, color, and even create a number of entirely different icon styles to indicate that the icons represent different categories of data or to communicate information about the status of a marker. For example a weather application may use several different icons to represent possible weather conditions, e.g. the weather for a particular location may be sunny, raining, overcast, warm, cold, or even a combination of these conditions, maybe sunny but cold. The developer may choose to vary the size of the icons to indicate the severity or intensity of the weather pattern. For example, an icon depicting a rain cloud might be enlarged to indicate a storm and reduced in size to indicate a light rain. The same weather map may use red colored icons to immediately warn users of dangerous conditions.
- Size of icons influences visibility of markers on the map.
While large icons may be more noticeable, and small icons more likely to blend into the background detail if there is not a lot of contrast between the map and the icon, it is possible to fit more small icons than large in a given area and resolution without overlap.
- Icons which indicate general regions vs icons which mark a specific point on the map.
Frequently icons may be used to indicate either a region of the map or a specific place. Icons that represent a precise location should clearly indicate that by attaching to the map at a specific point. It's less useful, maybe even inappropriate, for an icon designating a region to appear to indicate a specific location. We can make regional icons to resemble 'badges' which appear to float over the map, while point specific icons can be made with lines, tails or arrows that appear to extend down to the point of attachment, in much the same way as Google's default icons.
- Aesthetic sensibility.
Of all of these common reasons for using custom icons, this is the hardest to justify. You may choose to use custom icons because you consider Google's default icons ugly. That's fair enough, certainly taste is subjective. The problem with this argument is that it is difficult to completely banish Google's icons from your maps, even if you do choose to use custom icons for your markers.
I've chosen to use several different custom icons for all of the reasons just discussed, except for aesthetic sensibility. I like Google's default icon and wouldn't create a custom icon, something that requires a nontrivial amount of time and effort, just to be different.
Let's briefly discuss the rationale for the icons we'll be using before we get to the business of the code for creating the icons.
First, let's quickly review the concept of clustering. We've described what clustering is and its advantages. Because the topic has already been discussed I'll just remind you that clustering is a way of replacing multiple markers with one representing the group when the resolution of the map (i.e. the zoom level) and the proximity of the markers would result in overlap such that it would not be possible to distinguish any one from the others.
Let's say that at every possible zoom level we want to guide visitors toward our photos. At the lowest zoom levels a visitor will be looking at large geographical areas condensed within a small amount of space in terms of display size. Entire countries are reduced to the size of a small number of pixels. At these levels even just two markers representing photos taken literally thousands of miles away from each other may overlap. A typical collection of photos may include hundreds of photos or more with large groupings of them appearing in the same relatively small geographical areas. For example, the majority of the photos included in the gallery accompanying this tutorial were taken in and around Boston, Massachusetts, USA. It would be ineffective to display markers for these photos at many of the lowest zoom levels. Instead we'll create an icon to indicate areas on the map where photos are available at higher zoom levels. By displaying markers attached to regions appropriate for the current zoom level of the map, we avoid the issue of overlapping markers and the performance issues that would result from trying to display a world's worth of photos in a single view. The details of how to do this are discussed in the comments included with the source code.
The first type of icon we want to create is a regional icon. Depending on the zoom level we will draw a marker with this icon centered on a country, state/province, or city, where we have at least one photo in our collection. You may remember that we created a KML file matching each of these regions. We will rely on these KML files to create our regional markers. For example for the lowest few zoom levels, let's say levels 0 - 2, we can distinguish between countries but not smaller regions. At these zoom levels our regional icons will indicate countries where we have at least one photo. At higher zoom levels the same icons will be used with markers to indicate states and then, as the level increases, eventually cities where we have photos to display on the map.
At intermediate zoom levels the resolution is such that there is a reasonable amount of separation between small geographical areas so that we can add markers for individual photos and expect that many of the icons will be visible without overlap and, for any one view, we will not have so many markers that we overwhelm the map. It may be true that we have thousands of photos but many of the corresponding markers will fall outside the bounds of the view. However, at these levels it may be that we have a number of images appearing in the same small area of the map. In this case we include only a single marker for one of the images, and use an icon to not only indicate the location of a specific photo, but also to let visitors know that there are additional icons which are currently obstructed by this one.
Where we can clearly distinguish between the icons for all of our markers, we will use a third custom icon that simply indicates the location of a particular photo. At the highest zoom levels this is the icon we will use for all of our markers.
Though we have described only three distinct styles we need to define one more type. So that we can include markers for as many photos as possible at intermediate zoom levels, we will need two differently sized icons—one for the highest zoom levels and a smaller one for intermediate levels.
From the API reference:
An icon specifies the images used to display a GMarker on the map. For browser compatibility reasons, specifying an icon is actually quite complex. Note that you can use the default Maps icon G_DEFAULT_ICON if you don't want to specify your own.
There are quite a few properties to consider when it comes to creating a new icon. All of these properties are not required and in fact we do not use all of them in the tutorial. Refer to the API Reference for the full details (http://www.google.com/apis/maps/documentation/reference.html#GIcon). We will limit ourselves to the following list of properties: image, shadow, iconSize, iconAnchor, and infoWindowAnchor
To create a custom icon we create a new instance of the GIcon class
icon = new GIcon();
The image property specifies a URL referencing a file used as the foreground image.
The shadow property specifies a URL referencing a file used as a shadow image for this icon. The shadow image should be based on the foreground image of course. I'll have more to say about the shadow at the end of the list of properties.
iconSize is the size in pixels of the foreground image, listed as (width, height)
photo_large.iconSize = new GSize(32, 34);
shadowSize is the size in pixels of the shadow image, also listed as (width, height)
photo_large.shadowSize = new GSize(56, 32);
iconAnchor is: The pixel coordinate relative to the top left corner of the image at which this icon is anchored to the map. (This description is taken from the API reference)
icon.iconAnchor = new GPoint(6, 20);
infoWindowAnchor is: The pixel coordinate relative to the top left corner of the image at which the info window is anchored to the icon. (This description is taken from the API reference)
icon.infoWindowAnchor = new GPoint(5, 1);
We create a new instance of the GIcon class and define all of these properties for each of the following custom icon styles.
regional_icon = new GIcon();
photo_large = new GIcon();
photo_small = new GIcon();
photo_bunch = new GIcon();
Before we move on, a couple of notes about the icon foreground and shadow images.
The foreground image should be of reasonable size. Google's own default image is approx 32px high by 20 pixels wide. We've already discussed the tradeoffs involved in creating larger versus smaller icons.
The shadow image is derived from the foreground image. Using an image editing application of your choice (I used Adobe's Photoshop), the steps are as follows:
- Copy the foreground image to a new file.
- Fill the shape of the object so that it's black. The icons included in the project started out as Adobe Illustrator files, which I opened in Photoshop. Once in Photoshop I could select the object and set the fill and outline colors to black. If you have another type of image you may need to use a selection tool to grab the shape and set the fill that way.
- Shear the shape at a 45 degree angle. (Note that shearing is not the same as rotating. Shearing the image essentially stretches it at the specified angle rather than turning it around a fixed point. It's the shearing effect that gives it the look of a shadow.)
- Scale the image to half its original height without adjusting the width. (Don't do a proportional scale.)
- Blur the image. In Photoshop this means applying a blur filter. Whatever is identified in your application as a standard blur should work fine.
- Finally, adjust the opacity of the image to lighten it a bit.
- Save the image as a transparent png.
After defining all of the icons, we're essentially finished setting up the map itself. The last statement of the setup_map() function calls setup_markers() which controls the rest of the execution of the script. In setup_markers() we do the following:
- Download from the server the KML files generated in the first part of the tutorial which contain all of the data about our photos. To do this we take advantage of the function GDownloadUrl() from the Google Maps API. This is an important function for us and its use is described at length in the comments of the sourcecode.
- After parsing the KML structure from the data files using a parse method, again from the API, we create a number of NodeLists, which are objects that mimic lists of nodes from the DOM. I want to emphasize that these aren't lists in the traditional sense. A NodeList is a particular type of object which represents an ordered collection of nodes (not an array of nodes). This distinction may seem subtle, but it's not. For example, virtually all of the array object methods that you may be used to using aren't available for NodeList objects. Still, we can loop through these NodeLists and create markers for each of the
elements from the original KML files where each Placemark corresponds to one of our photos.
- We push all of these markers onto one or more arrays where they're collected until we're done processing all of the nodes from the KML structure.
- Finally, we pass these arrays to a number of marker managers (GMarkerManager), which are objects the API makes available to us to control display of collections of markers based on the bounds and zoom level of the map.
The marker managers automatically remove markers that fall outside of the bounds of the view. Without the marker manager, all of the markers, including those that can't be seen, are added to the map. This adds to the amount of work the application must do but isn't useful. Alternatively, we could accomplish the same thing by determining the bounds of the map and checking that each marker falls within the view before adding it. We'd need to repeat this every time the map was moved or the zoom level increased or decreased. The marker managers handle this for us.
Another advantage is that we can define a number of marker managers and assign different collections of markers to each. For each marker manager we set a zoom range over which it is active; that is to say, levels at which it adds and removes all of the markers it's responsible for from the map, subject to the bounds of the view as just described. We can set these marker managers up to accomplish clustering. As we move from low to higher zoom levels we can enable different managers to display increasingly more of our markers so that we're never overloading the map, but always representing as many of our photos as can be accommodated at the current zoom level.
Though we've described the setup_map function here, I'll refer you to the source code for detailed discussion of the rest of the project. It simply makes more sense to structure the discussion as the source code is written. The easiest way to handle that is in the source file itself. Certainly don't be intimidated by the source. It is all explained very explicitly. In fact, because the file is so heavily commented, I'm including a duplicate maps_tutorial.js that omits the commenting so that those of you who feel that the narration is distracting can focus on the code itself.
Once you get the project working, it will only take a few minutes to create a map of your geotagged photos going forward. It really is something that is valuable on its own and potentially the beginning of any number of other more sophisticated projects.
Google Maps has been a fantastically successful application for Google and in recognition of this, the amount of time and effort they continue to invest is only increasing. Recently the API has gotten radically better. They've added their geocoder, which we've used in this project, and expanded it to reach literally billions of new people. Moreover, they're improving their own applications based on the API. For example, they've recently launched My Maps, which is a simple web-based interface for customizing a Google Map with information including photos, markers, routes, etc. And they're doing everything they can to encourage independent developers like us.
Literally, from the time I started writing this tutorial to the time it was completed, it's become possible to do more with Google Maps. One of the best places to find out about improvements to Google Maps and the Maps API is the Official Google Maps API Blog at http://googlemapsapi.blogspot.com/
Whether this project we've put together is everything you want to do with Google maps or just the beginning of something bigger, I hope this tutorial helps you to enjoy your photos.
1 A markup language is any language that uses text codes or symbols alongside the content itself to specify the style and layout of the document.
2 A simple definition of hypertext from the The W3Cs What is Hypertext glossary of Hypertext terms: http://www.w3.org/WhatIs.html.
The full list of terms is available at: http://www.w3.org/Terms.html.
Note that this document is not a complete list of terms, nor does it necessarily reflect current usage. (Last updated 1995.). See the appendix for additional resources.
3History of Hypertext: http://ei.cs.vt.edu/~wwwbtb/book/chap1/htx_hist.html.
4 The World Wide Web: A very short personal history: http://www.w3.org/People/Berners-Lee/ShortHistory.