CHAPTER |
|
5 |
Google Maps API: The Building Blocks of Mapping Applications |
google.maps
library, which provides all the functionality for setting up and manipulating a map at the client through JavaScript. This library is part of the script you include in every page that contains a Google map, and it exposes its functionality through different objects. The Map
object of the google.maps
library represents the map itself. The maps you embed to your pages are instances of this object, and you manipulate the map through the Map
object’s methods. You can set the map’s zoom level from within your script by calling the setZoom()
method, which accepts the zoom level as an argument, or request the map’s center point with the getCenter()
method of the Map
object.google.maps
library, and this is the topic of this chapter. The components you’re going to explore in this chapter are the basic tools for manipulating maps; they will be used in every chapter of this book and every script you write from now on. While a reference chapter, this chapter also contains some interesting examples. Even if you’re familiar with the Google Maps API, you should take a look at the last section of this chapter to see the implementation of a context menu for a Google map.google.maps.Map
object, which represents the map itself.google.maps.LatLng
object, which represents locations on the map. This object is nothing more than a pair of decimal numbers: a point’s latitude and longitude.google.maps.LatLngBounds
object, which represents the bounds of a rectangular area on the map, usually the bounds of the current viewport.google.maps.Marker
object, which represents the markers you use to visually identify features on the map.google.maps.InfoWindow
object, which is a window you use to display information about the selected marker.google.maps.event
object, which is an auxiliary object for handling all map-related events.Marker
and InfoWindow
objects are also covered separately in Chapter 7.google.maps.Map
ObjectMap
object, you pass to the constructor the name of a container element where the map will be rendered. This container is a <div>
element on which the map is displayed, and it usually takes up most of the available space on the page, but this isn’t necessary. You can specify the size and position of the <div>
tag on the page with HTML attributes, or apply a custom style to it. The map’s <div>
element may have a fixed size, or it can be dynamically resized with the browser’s window. This element is passed as an argument to the constructor of the Map
object, so it’s imperative that it has a unique id
attribute. The appearance of the element that hosts the map is controlled through HTML and has nothing to do with maps. If needed, you can create pages with multiple maps, which you can synchronize with some custom code. You will actually see this technique in action in the last section of this chapter.<div>
element are generated by a server application running on a remote Google server, and you interact with the map through a script, http://maps.googleapis.com/maps/api/js
, which is also downloaded from the remote Google server. This is a JavaScript file provided by Google and you can certainly read it; fortunately, you’ll never have to read it. Understanding the script is not required (it’s not even optional) for coding the mapping operations you wish to add to your application. The script is the client portion of Google Maps: It allows users to navigate the map, requests new tiles of the map from the remote server as needed, and renders them on the client application. When you zoom in, the script doesn’t simply blow up the existing tiles; it requests new tiles, which contain additional information. All this takes place in the background and you don’t need to write any code.google.maps.Map
object passing two arguments: a reference to the <div>
element and an auxiliary object that specifies the options of the map, as shown here:map_canvas
is the id
of the <div>
element that will hold the map, and it’s part of the HTML page. Here’s a typical declaration of a <div>
element for displaying a map:<div>
element, you apply a style to it. The following is a typical CSS (Cascading Style Sheet) definition for the map’s section:<div>
element to which it will be applied should take up all the available space on the page (100 percent of the page’s width and height), have a blue border with a width of 1 pixel, and have no margins around the map.google.maps.Map
object must appear in a script that’s executed as soon as the page is loaded. Typically, you create a script that contains the initialize()
function with the initialization data and you call it when the page is loaded by setting the onload
attribute in the page’s <body>
element to the name of this function:initialize()
name. The onload
keyword is an attribute of the <body>
element. It’s also an event of the window
object, which is activated as soon as the window is loaded. You can program the window.onload
event to perform the initialization of the map with a statement like the following:initialize()
function. You can also replace the call to the initialize()
function with the actual contents of the initialize()
function. The preceding statement must appear at the beginning of the <script>
section.google.maps.Map
constructor, the mapOptions
argument, is an array of key-value pairs that determine the map’s overall appearance and its initial settings. You have seen how to use the mapOptions
argument in Chapter 2, and in the following section, you’ll find a detailed discussion of this object.MapOptions
object determines the appearance of the map and it has many properties, three of which are required: the center
, zoom
, and mapTypeId
properties. The first two properties specify the map’s initial center and zoom values. The MapTypeId
property specifies the initial map type, and its value can be one of the members of the google.maps.MapTypeId
enumeration, shown in Table 5-1.MapOptions
object with the most basic map attributes and then use it to set up a new map:MapOptions
object exposes many more properties for customizing the map. Some of the properties allow you to manipulate the appearance of the various controls that appear in the default map: the Navigation control on the left; the Zoom control, which is normally part of the navigation control; and the MapType control, which allows users to change the map type.setOptions()
method, which accepts the same argument: an array of option names and their settings. The setOptions()
method can be called at any time to alter one or more options of the object to which it’s applied. The following call to the setOptions()
method changes the map’s type to satellite view and its zoom level:navigationControl
property to false
(its default value is true
). You can also manipulate the appearance of the Zoom control with the ZoomControlOptions
object, which exposes only two properties: the position
property, which determines the location of the control on the map, and the style
property. The style
property takes on the values as listed in the Table 5-2.MapTypeControlStyle
Options The second control you can manipulate individually on the map is the MapType control, which determines the type of map you want to display. The property mapType
control can be set to false
to hide the entire control (if you don’t want users to change the map type in a specific application). In addition, you can control the appearance of the control by setting the style
property of the mapTypeControlOptions
object to one of the values listed in Table 5-3.Position
property, which can take one of the following values: BOTTOM_CENTER
, BOTTOM_LEFT
, BOTTOM_RIGHT
, LEFT_BOTTOM
, LEFT_CENTER
, LEFT_TOP
, RIGHT_BOTTOM
, RIGHT_CENTER
, RIGHT_TOP
, TOP_CENTER
, TOP_LEFT
and TOP_RIGHT
.google.maps.LatLng
LatLng
object represents a point on the earth’s surface; it consists of the point’s latitude and longitude, in that order. The getCenter()
method of the Map
object returns a LatLng
object with the coordinates of the center of the map. Whenever you need to pass a location to a function, you create a new LatLng
object by specifying its latitude and longitude:Map
object’s setCenter()
method passing the pt
variable as an argument:LatLng
object exposes its two basic components, the point’s latitude and longitude, as methods and their names are lat()
and lng()
. The following statements read the map’s center coordinates and store them to two variables:google.maps.LatLngBounds
LatLngBounds
object represents the rectangle that encloses the current viewport. The bounds of the current viewport are specified by the coordinates of the upper-right and lower-left corners of the viewport. In terms of actual world coordinates, the upper-right corner is the north-east corner of the viewport, while the lower-left corner is the south-west corner of the viewport. The LatLngBounds
object exposes the sw
and ne
properties, which are LatLng
objects, and they represent the coordinates of the map’s opposite corners. To find out the current viewport’s minimum and maximum latitudes, you must call the map’s getBounds()
method and then extract the latitude values from the sw
and ne
properties, as shown here:LatLngBounds
object exposes some very useful methods on its own, which can simplify your script, especially if you’re coding a GIS application. The contains()
method accepts as an argument a LatLng
value (a geo-location) and returns true
if the specified location lies within the bounds of the LatLngBounds
object. The extend()
method accepts as an argument a geo-location and extends the bounds of the current LatLngBounds
object so that it will contain the specified geo-location. This method is especially useful when you place multiple markers on a map (say, the hotels in a city or neighborhood), and you want to make sure that all markers are shown. Instead of experimenting with the zoom level, you start by creating a new LatLngBounds
object with the location of the first marker. Then, as you add markers to the map, you call the extend()
method of the LatLngBounds
object, passing the geo-location of the current marker as an argument. Finally, when you’re done, you call the map’s fitBounds()
method, passing the LatLngBounds
object you have created as argument, and the map will be panned and zoomed in or out as needed to display all markers. The new viewport will comfortably contain all the markers, as its actual size will be slightly larger than the absolute minimum rectangle that contains all markers.fitBounds()
method, and comes from the Fit Bounds Demo.html
web page. The script places a few markers at the locations of major airports in Germany. At the same time, it extends the bounds of a LatLngBounds
object, the bounds
variable, to include the location of the current marker.LatLngBounds
object based on specific locationsfitBounds()
method to display the proper viewport:Fit Bounds Demo.html
web page is shown in Figure 5-1. The initial zoom level is 1, but after you click the button below the map and the airport markers are placed on the map, the zoom level is automatically set to 5 and the map’s viewport is properly adjusted to display all markers. The Fit Bounds Demo.html
page contains a very simple script, but you will find it useful when you need to display multiple markers (or other objects) on the viewport.Map
object has many methods, including the getCenter()
and getZoom()
methods, which return the coordinates of the map’s center point and the map’s current zoom level, respectively. They retrieve the current zoom level, for example, you can call the Map
object’s getZoom()
method as follows in a JavaScript script:getCenter()
method, on the other hand, returns an object of the LatLng
type, which is made up of two floating values: the geo-coordinates of the map’s center point. If you include the following statement in a script:getCenter()
method’s response when it’s converted to a string.toString()
method, which is returned when you request the object’s value as a string. The LatLng
object’s textual representation is a pair of comma-separated values embedded in parentheses. The toString()
method was not called explicitly, but you requested the value of an object as text and JavaScript returned the object’s textual description.lat()
and lng()
methods of the LatLng
object (the method toFixed()
is a JavaScript method that rounds a floating point value to as many decimal digits as indicated by its argument):setCenter()
method, passing as an argument a LatLng
object; to zoom in or out, use the map object’s setZoom()
method, passing as argument an integer value:fitBounds()
method discussed in the preceding section does exactly the same.setCenter()
method changes the map’s location instantly. Another method, the panTo()
method, also accepts a location as an argument and pans the map smoothly to its new location. A third related method, the panBy()
method, accepts as arguments the horizontal and vertical displacements and scrolls the map smoothly to its new location. The Basic Map Events.html
page, shown in Figure 5-2 later in this chapter, demonstrates the difference between the setCenter()
and panTo()
methods. Check either radio button below the buttons, and then click the destination buttons to change the viewport. With the setCenter()
method, the map is relocated instantly; with the panTo()
method, on the other hand, the map is panned smoothly to its new center. The operation lasts much less than a second, but the effect is quite noticeable. Of course, the panning is possible if the destination tiles are already in memory. If you move from one hemisphere to the other, the panTo()
method is no different than the setCenter()
method. The function jumpTo()
in the page’s script accepts the coordinates of the new location as arguments and calls the appropriate method depending on the status of the rbCenter
radio button:Map
object is used routinely with all other objects of the Google Maps model object, such as markers, information windows, lines, and shapes. All of these objects expose the setMap()
method, which accepts a single argument: the map to which the object will be attached. Assuming that the marker
variable represents a marker, you can place the marker on the map by executing the following statement:Marker
object is used to mark a specific location on the map and it’s usually rendered as a pin. Markers contain additional information about the location they identify, and this information can be displayed in a speech bubble icon, which is an InfoWindow
object, when users click the marker. The Marker
and InfoWindow
objects are two of the most recognizable symbols in the computer world. They are very important in designing mapping applications and are described in detail in Chapter 7.Basic Map Events.html
web page, shown in Figure 5-2. The application reacts to the most basic events. The various buttons at the bottom re-center the map at a different location. The text at the bottom (not shown in the figure) describes the map’s events, and you can scroll the page to read it.Map
object when the map is dragged; they notify your application about the drag operations initiated by the user. User interaction with the map involves a lot of dragging and your application should be able to react to these events.drag
drag
event is fired continuously, it’s wise to insert in this event’s handler very simple and highly optimized code that’s executed instantly. If the code in the drag
event takes even a second to execute, it will cause the drag operation to become sluggish. For example, you can easily request the map’s center point coordinates and display them on your page. This operation is carried out at the client and no trip to the server is required. If you want to translate the center point’s coordinates to a physical address, which means an extra trip to the Google server, this will make your page less responsive. Use the dragend
event instead for this operation.drag
and dragend
events. The script displays the string “dragging” in a <div>
element on the page while you’re dragging the map. In the dragend
event, the script resets the same section on the page. First, add the listeners for the two events in the initialize()
function:updateDragLabel()
function is a trivial function that sets the contents of the dragStatus
and mouseStatus
elements on the page:dragend
dragend
event in the preceding section.dragstart
dragstart
event handler and use this variable to slide the map back to its initial location if needed. This event does not provide any arguments; yet you can retrieve the map’s initial position in the dragstart
event’s handler, because it’s fired before the map is moved.mapTypeId_changed
mapTypeId
property changes. The mapTypeId
property determines the type of map being displayed and it’s fired when the user switches from a road map to a satellite/terrain view. Alternatively, you can omit the map type control from the map and change the map’s type from within your script, either in response to a user action, or based on the map’s current content. You can automatically switch to satellite view when the user zooms out beyond a threshold, and back to map view when the user zooms in below this threshold.click
Map
object. The click
event reports the location of the pointer on the map the moment it is clicked through a LatLng
object. Note that the click
event isn’t fired when the user starts a drag operation, so you can write code to take advantage of the click operation (to place a mark on the map, for example, or initiate a drawing operation), without interfering with the dragging operations.click
event, insert the following event listener into your script:initialize()
function, right after the initialization of the map
variable. The mapClicked()
function, which will be invoked automatically every time the user clicks on the map, contains the code to handle the event. Note that the event automatically passes an argument to the listener of the click
event, and this event exposes the coordinates of the mouse location. Here’s a simple function that displays the coordinates of the location where the user clicked in a message box:mapClicked()
function is implemented with a single line of code, which is usually embed in the statement that adds the listener:event
argument conveys specific information about the event. For the click event, this information exposes a LatLng
object as property; this property represents the point that was clicked. In the statement that adds the event listener, the name of the argument is always “event.” In your function that handles the event, the name of the argument can be anything.dblclick
dblclick
event is not distinguished as a separate event from the click
event, so when the user double-clicks on the map, a click
event is fired, followed by a dblclick
event. This behavior makes it impossible to program both the single-click and the double-click events in the same script. There are hardly any applications that make use of the double-click event. If you program the click
event, the dblclick
event will be raised, but never handled.dblclick
event, you must also disable its default function, which is to zoom in by one step. To disable the default function of the double-click, set the disableDoubleClickZoom
option of the Map object to true. Like the click
event, the dblClick
event reports the location of the pointer on the map the moment it was double-clicked through a LatLng
object.mousemove
mouseout
mouseover
mousemove
events. The three mouse events are fired in the following order:mouseover
is fired once as soon as the pointer enters the map.mousemove
is fired continuously while the user is moving the mouse over the map.mouseout
is fired once as soon as the pointer leaves the map.mousemove
event; you must clear the label on the mouseout
event, as the pointer is no longer over the map.rightclick
rightclick
event reports the location of the pointer on the map the moment it was clicked through a LatLng
object. As you already know, right-clicking a map has no effect: The map doesn’t have its own context menu. It’s possible, though not trivial, to implement your own context menu by writing some JavaScript code that reacts to the rightclick
event. You actually see how to add a context menu to a map later in this chapter.rightclick
event is to undo the effect of the click event in an application that annotates a map. Let’s say you use the click event to insert a marker at the location where the mouse was clicked. You can intercept the rightclick
event of the marker to remove an existing marker. Note that the markers are placed on the map from within the map’s click
event handler, but they are removed from within the marker’s rightclick
event handler.bounds_changed
event, for example, is fired when the map’s viewport is changed, whether due to a change in the zoom level, or due to a user-initiated drag operation. These events report changes in the map’s current status; you use them primarily as notifications to update the application’s interface.tilesloaded
tilesloaded
event is fired when the visible tiles that make up the current map have finished loading. This is a good place to execute any statements that add content on top of the map, or enable any buttons that update the map. The onload
event is fired when the page with the map has been completely loaded. It will take a few seconds from the moment you request the map to the moment that the relevant map tiles actually arrive to the client computer. During that time, the map is a gray rectangle. The tiles arrive one at a time and the areas corresponding to tiles that have not arrived yet remain gray.tilesloaded
event’s handler. A button that adds a new marker to the map shouldn’t be enabled while the map is being loaded, for example.zoom_changed
getZoom()
method from within the event’s listener to retrieve the current zoom level, if you need it in your code.bounds_changed
getBounds()
and getZoom()
methods to retrieve the new bounds and the new zoom level. Note that the event is fired after the completion of the operation that caused the event to fire. When the user drags the map, the bounds_changed
event will be fired after the map has been dragged to a new location and the map’s contents have been updated. The zoom_changed
event is also followed by a bounds_changed
event because a change in the zoom level also changes the bounds of the viewport.center_changed
center_changed
event is hardly ever used in coding mapping applications; you use the bounds_changed
event instead because the bounds of the map change when it’s dragged as well as when the map is zoomed in or out.idle
idle
event is fired only once after several operations that may affect the map’s viewport, and it was designed specifically to facilitate the update of the interface with the current status. In the Basic Map Events.html
page, the idle
event is used to update the various <div>
elements on the form. First, the script adds a listener for the event with the following statement:updateLabels()
is a simple function that updates the interface by displaying the map’s current center location and zoom level:getCenter()
and getZoom()
methods because the idle
event doesn’t report any arguments back to the application. The preceding code segment could assign the return value of the getCenter()
method directly to the innerHTML
property, but the coordinate values are formatted with a small number of decimal digits.Customized Map.html
page, which is included in this chapter’s support material. The first thing to notice about this page is that the usual controls on the map are missing. This was achieved by setting the disableDefaultUI
property to true
. To navigate, users can press the arrow buttons, while to zoom they can use the “+” and “–” keys. They can also pan the map around with the mouse and zoom in and out by double-clicking the two mouse buttons.<div>
element with a black background and rounded corners. Moreover, the map’s section is another <div>
element, slightly smaller than its container. Some basic map attributes are displayed on a semitransparent pane at the top of the map. This pane is updated continuously with the map’s basic attributes as the user interacts with the map. This pane is a combination of HTML (it’s a <div>
element with absolute position) and some code in the map’s idle
event that updates the contents of the <div>
element. The HTML code that generated Figure 5-3 is shown in Listing 5-2.idle
event’s listener with the following statements:Customized Map.html
to see how the various <div>
elements are nested to produce the effect shown in Figure 5-3. The outer <div>
element is the one with the rounded corners (property border-radius
) and it contains a <div>
element with the semitransparent header, followed by a <div>
element with the map, and finally another <div>
element with the instructions below the map. The header of the page is implemented as a table with named cells, the location
and zoom
cells, which are then assigned the appropriate values in the idle
event’s listener.<div>
element that contains the menu commands, and a listener for the map’s rightclick
event. This listener will display the <div>
element with the context menu at the proper location on the map when the user requests it by clicking the right mouse button. Let’s build a context menu by implementing the menu’s div
element and then the rightclick
event listener.<div>
element that can be displayed on the current page at will; it may even contain a different set of commands depending on the current status of the application, or even the location that was clicked. If you found the last statement exaggerated, keep reading. The following statement creates the <div>
element that will hold the menu’s commands and assigns it to the script variable contextMenuDiv
:<div>
element that will host the menu:click
event by calling a different function. To populate this <div>
element, set its innerHTML
property to the appropriate HTML fragment, as shown in Listing 5-3.Context Menu.html
page<div>
element and all these elements are formatted with a common style, the contextItem
style. The first command displays the physical address at the location that was clicked by calling the showAddress()
function; the second command centers the map at the location of the click with the recenterMap()
function, and the last command displays the geo-coordinates of the same location with the showCoordinates()
function. They’re simple commands for demonstration purposes, except for the first one. You will see how you can request the physical address of a location on the map in Chapter 16.rightClickLocation
, which is a LatLng
object.menu
. To do so, you must first install a listener for the map’s rightclick
event:showContextMenu()
function accepts as an argument the location that was clicked, creates the context menu by populating a new <div>
element with the statement shown in Listing 5-3, and displays it on the map by setting its upper-left corner to the location that was clicked. Note that you don’t need to do anything in order to retrieve the coordinates of the location that was clicked. The listener’s argument, event
, is supplied automatically, and it exposes the coordinates of the location that was clicked through its latLng
property. The code of the showContextMenu()
function is shown in Listing 5-4. The ellipses in the listing stand for the statements that populate the contextmenuDiv
variable, shown in Listing 5-3.<div>
element with the context menu’s items. Finally, it displays the menu at the appropriate location by calling the positionMenu()
function, which contains jQuery code to properly position the menu, as shown in Listing 5-5.positionMenu()
function calls another function, the getPointerLocation()
function, which converts the geo-coordinates of a location into screen coordinates (pixels) and places the context menu’s upper-left corner at these screen coordinates. It also makes sure that there’s enough space to display the menu and, if not, it places the menu’s upper-right corner there so that the menu appears to the left of the location that was clicked. Similarly, if the user has clicked near the bottom of the screen, the menu is displayed above the clicked location, and not below as usual. To position the menu, the last two statements manipulate the left and top properties of the menu’s style (these properties are introduced by jQuery and there’s no equivalent HTML code).showContextMenu()
, insert the appropriate statements to create the context menu..itemStyle
and .contextMenudiv
style definitions (and modify them accordingly).rightclick
event to call the showContextMenu()
function.rightclickLocation
variable is set to the location of the right-click from within the rightclick
event listener and it’s used by other functions in the script. The implementation of the functions for centering the map and displaying the current coordinates is trivial:HideContextMenu()
function, which removes the element with the context menu from the map by manipulating the visibility
attribute of the div
element that contains the entire menu:Context Menu.html
page and examine its script, which is well documented with comments that were not included in the listings. Try adding more options, apply different styles to the menu items and the menu itself, and implement commands that address your specific application requirements. If you’re ambitious, design a menu with different commands, depending on the location that was clicked. You can retrieve the address of the current location and, if it’s not a well-known address, you know that the user has clicked somewhere in the ocean or some other uninhabited place.Map
object. Practically every item you place on a map recognizes the rightclick
event, which can lead to a custom context menu. In effect, you can add many different context menus: one for the map itself, another one for the lines on the map, another one for the shapes, and so on. You can even use different symbols to identify different features on the map, and each symbol could lead to a different context menu, specific to the feature represented by that symbol.<hr/>
element. You can change the look of the menu as you wish, or as your HTML skills allow you.if
statement because its caption may be “Show crosshair” or “Hide crosshair,” depending on the current visibility of the crosshair. The code uses the crosshair
script variable to keep track of the visibility of the crosshair. If this variable is true
, the code sets the caption of the last command to “Hide crosshair”; otherwise, it sets it to “Show crosshair.” Listing 5-6 shows the statements that generate the context menu.crosshair
script variable: Depending on this variable’s value, the script generates a command with a different name. This is the reason that you should generate the context menu on the fly, instead of creating a <div>
element with the menu commands ahead of time and reusing it as needed. The implementation of the crosshair is a bit involved, and it’s discussed in detail in Chapter 9. The code that draws the cursor is simple, provided you know how to draw lines, but it must be redrawn every time the map viewport changes. You can open the project and see the implementation of the showCrosshair()
function, which draws two vertical lines at the middle of the map.Multiple Maps.html
web page, demonstrates how to place three maps on the same page (see Figure 5-6). The three maps have different content, but they’re synchronized. The small map at the lower-right corner shows an overview of a larger area, like a state or country. The main map is a basic roadmap of a very specific area (a university is shown in the figure), and the small map at the upper-right corner is a small detail in satellite view. The yellow semitransparent rectangle in the large map indicates the area of the satellite view.<div>
elements and each one is assigned a different Google map in the page’s initialize()
function. Placing the three maps on three different <div>
elements is straightforward: You simply initialize three different Map
objects. Keeping the contents of the three maps synchronized is also simple. Each time any of the maps is dragged by the user, the other two maps are repositioned with the setCenter()
or the panTo()
method from within the appropriate event listener.MultipleMaps.pdf
file, which explains the code in detail.18.216.77.153