So far in the course, you've spent the bulk of your time learning about the various web technologies that are involved in the creation of even the simplest Google Maps page. The last project deliverable was quite basic, and you probably came away thinking that the map was a bit boring. In Lesson 4, we'll dig deeper into the API to learn how to produce a more useful and personalized map. At the conclusion of the lesson, you'll be asked to map a set of locations and enable callout windows to appear when those points are clicked.
At the successful completion of this lesson, students should be able to:
If you have any questions now or at any point during this week, please feel free to post them to the Lesson 4 Discussion Forum. (That forum can be accessed at any time by clicking on the Discussions tab within Canvas.)
Lesson 4 is one week in length. (See the Calendar in Canvas for specific due dates.) To finish this lesson, you must complete the activities listed below. You may find it useful to print this page out first so that you can follow along with the directions.
Step | Activity | Access/Directions |
---|---|---|
1 | Work through Lesson 4. | You are in the Lesson 4 online content now. Click on the "Next Page" link to continue. |
2 | Complete the "Jen and Barry's" project on the last page of the lesson.
|
Follow the directions throughout the lesson and on the "Jen and Barry's" page. |
3 | Take Quiz 4 after you read the online content. | Go to the Canvas Homepage and click on the "Lesson 4 Quiz" link to begin the quiz. |
In this lesson, you'll be adding to the code you wrote in Lesson 3, so create a copy of your lesson3.html file and call it lesson4.html.
The code examples in this lesson and beyond do not utilize the "my" prefix on variable names that was suggested in the previous lesson. That naming scheme was only meant to clarify what were variables and what were object names from the API. Hopefully now you'll be comfortable with variables having the same name as the object classes they reference.
Unless otherwise specified, a Google map includes a set of controls in the upper-right corner that allows the user to switch between the available map types (Map, Satellite and Terrain). There is also a zoom control in the lower-right corner for zooming in/out on the map. Depending on the application or your personal preference, you may want to deviate from these defaults.
Altering the map type control involves creating an object of the MapTypeControlOptions [1] class. As with the MapOptions and MarkerOptions classes we encountered in Lesson 3, a MapTypeControlOptions object must be created as an object literal. It has a mapTypeIds property for specifying which map types should be made available, a position property for specifying where on the map the control should appear and a style property for specifying whether the control should be a set of side-by-side buttons or a drop-down menu.
In Lesson 3, we saw that the Hello World example initialized the map so that it displayed the basic road map using the ROADMAP map type ID. The other map type IDs currently built into the API are SATELLITE, HYBRID, and TERRAIN. These ID constants are accessed through the MapTypeId class, which like all classes in the Google Maps API, must be referenced through the "google.maps" namespace.
Setting the mapTypeIds property of the MapOptions object requires an array of MapTypeId constants. A simple way to produce an array in JavaScript is to enclose a comma-separated list of items in square brackets. Thus, the code for setting up a map to display only the Map and Hybrid options might look like this:
var mapTypeControlOpts = { mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID] };
The ControlPosition class defines the constants that can be used to set the position of the map type control. These constants are TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, TOP, BOTTOM, LEFT, and RIGHT. To position the map type control on the bottom of the map, you would use code like this:
var mapTypeControlOpts = { position: google.maps.ControlPosition.BOTTOM };
Finally, the MapTypeControlStyle class defines the constants that can be used to set the style of the map type control. These constants are DEFAULT, DROPDOWN_MENU and HORIZONTAL_BAR. The HORIZONTAL_BAR style shows the map type options as a set of buttons arranged side by side horizontally and is best suited for maps viewed on a large display such as a desktop monitor. The DROPDOWN_MENU style lists the map type options in a hierarchical dropdown menu and is best suited for maps viewed on a small display such as a smartphone. The DEFAULT style enables the API to switch automatically between a horizontal bar and dropdown menu depending on the size of the browser window that's displaying the map rather than hard wiring one or the other. To display the map types as a menu (and use the same mapTypeIds and position settings from above), you would use code like this:
var mapTypeControlOpts = { mapTypeIds: [google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID], position: google.maps.ControlPosition.BOTTOM, style: google.maps.MapTypeControlStyle.DROPDOWN_MENU };
Once the MapTypeControlOptions object is defined, it can be used to set the mapTypeControlOptions property of the MapOptions class:
var mapOptions = { mapTypeControlOptions: mapTypeControlOpts };
The MapOptions object would then be applied to the map as an argument to the Map class constructor, just as before.
This discussion presumes that you want to give users a choice of background layers. If you'd instead prefer to lock them into one map type, the MapOptions class has a Boolean mapTypeControl property that can be set to false to remove the map type buttons from the interface:
var mapOptions = { mapTypeControl: false };
Past versions of the API made it possible for developers to select from a zoom control that included a slider and one that did not (i.e., just + and - buttons). The slider version of the control also included a set of four arrows for panning the map. At version 3.22, the option to implement the slider version of the control was removed. The zoom control is now a pair of + and - buttons, located in the bottom right of the map by default. The control can be re-positioned using one of the constants described above in the discussion of the map type control. For example:
var zoomControlOpts = { position: TOP_LEFT };
Using the same mapOptions object literal from the previous section on customizing the map type control, you would use the zoomControlOpts variable to set the zoomControlOptions property.
var mapOptions = { zoomControlOptions = zoomControlOpts };
The developer also has the ability to disable the zoom control altogether:
var mapOptions = { zoomControl: false };
The elimination of the slider style of the zoom control also did away with the panning buttons. Users must pan the map by clicking and dragging (or by swiping on a touchscreen).
Other map controls that can be customized by developers include the street view control and scale control. As with the ones discussed above, these controls can be enabled/disabled using the Boolean MapOptions properties streetViewControl and scaleControl. The street view control is enabled by default, while the scale control is disabled.
Besides enabling/disabling them, the only customization option available for these controls is the position of the street view control. The street view control ("pegman" icon) appears just above the navigation controls by default. Overriding this position requires creating a StreetViewControlOptions object much like the ControlOptions objects discussed above.
As you can see, the API provides developers with a fair bit of control over the user interface. One last point I want to bring to your attention is the disableDefaultUI property. If you want to build an interface that differs quite a lot from the default, you can set this property to true, effectively giving yourself a clean slate with which to work. Leaving the property unset means that any changes you make will have the default interface as the starting point.
The Google documentation devotes a full page to map controls [2] and I encourage you to consult that page for a more thorough discussion of this topic. You'll find a couple of working examples on this page along with information on adding your own custom controls.
Once you've experimented with the various controls discussed in this section and gotten a feel for modifying the user interface, move on to the next part of the lesson, where you'll see how to style the Google basemap.
The Maps API provides a great deal of control over the appearance of the base map. The process of modifying part of the base map involves specifying a feature type (e.g., road, transit, water), an element type (e.g., geometry, labels) and the desired style for that feature/element. To illustrate these concepts further, let's have a look at the source code of a couple of examples. Note that these examples initialize the page slightly differently than the Hello World example discussed in the previous lesson. I recommend you consider Google's current Hello World initialization method a best practice, but you should also not be "thrown off" by the method used in the examples below. You're likely to see many slight variations like this if you look at other people's source code, such as in places like stackoverflow.
This first example produces a new map type (called "Styled Map") [3] in which the color of water and of state names has been altered. Let's look first at the creation of the styles variable. This variable is assigned an array of MapTypeStyle [4]objects -- in this case, two such objects. Note that each of these objects has three properties set: featureType, elementType, and stylers.
The featureType property is set using one of the MapTypeStyleFeatureType [5] constants: to 'water' for the first MapTypeStyle object and 'administrative.province' for the second.
The elementType property is set using one of the MapTypeStyleElementType [6] constants: to 'geometry.fill' for the first object and 'labels.text.fill' for the second.
Finally, the stylers' property is set using a MapTypeStyler [7]object. In both cases, this object has only its color property set, though other commonly set MapTypeStyler properties include visibility and weight.
With an array of MapTypeStyle objects created, the next step is to create a StyledMapType object. To create this object, the MapTypeStyle array is passed to its class constructor along with a StyledMapTypeOptions object literal composed of two property settings: map and name. The map property defines the Map object that the StyledMapType will be associated with, while the name property defines the label that will appear on the map type control.
The next step is to add the StyledMapType to the Map's MapTypeRegistry. That is done with the line:
map.mapTypes.set('styled', styledMapType);
styledMapType refers to the object created in the previous statement, while the string 'styled' is the name assigned to the map type in the registry. This name is entirely up to you, and you'll note that it's different in Google's example.
The next statement sets the map's opening map type to the 'styled' map type. In some contexts, you might omit this statement so that your styled map type is an option for the user to switch to, but is not the default.
A final important step is to add the StyledMapType to the map type control array. This is actually done near the beginning of the initialize() function when the mapTypeControlOptions property is set using the expression:
mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'styled']
It may seem strange that this setting comes before the StyledMapType is actually created, but apparently it's OK to do this.
This second styled map example [8] demonstrates how you might go about simplifying what's shown on the base map by turning off a number of its features. As with the first example, the key to manipulating the components of the base map is to specify a feature type, element type and desired style.
The first featureType altered in this example is 'road'. Keep in mind that it is possible to manipulate different categories of roads (e.g., roads.highway, roads.arterial). The use of 'road' here indicates that the style setting should be applied to all types of roads.
The setting of the elementType to 'all' specifies that the style should be applied to both the geometry of the roads and their labels.
Unlike the previous example where we changed the colors of features, here we set the visibility of the road features to 'off'.
Continuing on through the script, note that a number of other base map features are turned off in a similar way (national boundaries, neighborhood boundaries, land parcel boundaries, points of interest, and transit features). Also note that locality features (showing urbanized areas) have not been turned off, but have been lightened by setting their lightness property to 40%.
This base map is similar to the light options that we saw in Google My Maps and ArcGIS Online in Lesson 1 and is especially appropriate in situations where you want to ensure that the user's attention is drawn to the map's overlays rather than features on the base map.
One final difference to note between this example and the previous one is in the application of the styles. In the previous example, a new map type was created and made available as an option for the user to switch to. In this case, the styles are applied across all the map types that are made available to the user -- in this case, ROADMAP and TERRAIN -- through the line:
map.setOptions({styles: visStyles});
To help API developers style their base maps, Google has put together a Styled Maps Wizard [9]. Let's walk through how you might use this wizard to reproduce the styling code we saw in Example 1 above.
Now that you've seen how you can style the Google basemap to your liking, let's move to the next page where you'll see how to display a callout window when a marker is clicked.
A disappointing aspect of the map from Lesson 3 was that clicking on the marker didn't do anything. The behavior that you're probably used to seeing when a marker is clicked is that a callout window opens with some information about that marker. To add this kind of behavior, we need to add an event "listener" for our marker. This listener will enable us to execute a function whenever certain events are triggered with respect to the marker (e.g., when it is clicked, when it is double-clicked, or when the mouse moves over it). The full list of events supported by markers can be found by looking up the Marker class in the API Reference. We will add a handler for the click event that will open an info window and display text that we've encoded as HTML.
Like several other classes we've encountered so far, an object of the InfoWindow class can be created by passing an "Options" object to its class constructor. So we'll start by defining an InfoWindowOptions object using object literal notation.
Add the following code (in which the long URL has been split across two lines for readability) to the end of the initMap() function:
var infoWindowOpts = { content: '<a href="https://www.youtube.com/watch?v=aLSFcF8SOiw">' + 'My Hometown</a>' }; var infoWindow = new google.maps.InfoWindow(infoWindowOpts);
The first statement in this block of code (which spans four lines) creates an object literal and sets its content property equal to a string of HTML that we want to display in the info window - a link to a website. Pay close attention to the two pairs of quotes used in this statement. The first pair is a set of single quotes used to enclose the entire string that is to be assigned to the content property. The second pair is a set of double quotes used to enclose the URL that's being assigned to the href attribute of the anchor tag. It would also be acceptable to use double quotes around the full string and single quotes around the URL. The important thing is that one kind of quotes be used to set the JavaScript content property and the other kind be used to set the HTML href attribute.
The second statement creates an InfoWindow object based on the content setting defined in infoWindowOpts. We're now ready to create the event listener.
Add the following code where you left off:
google.maps.event.addListener(marker, 'click', function() { infoWindow.open(map, marker); });
This code calls the addListener() method in the event namespace, passing it the object whose events we want to listen for (marker), the event we want to listen for (click), and a function to execute when that event is triggered.
Even if you're an experienced programmer, the code used for the third argument may have you scratching your head a bit. We could have supplied the name of a function that was defined elsewhere in the document, but instead we're using something called an anonymous function. Anonymous (or inline) functions have a number of uses in JavaScript that are beyond the scope of this class, so we won't discuss them in depth. For our purposes, you should simply understand that they are frequently used to define the code to be executed as part of an event listener. Note that the function is defined in the same way as a normal function, with the exception that it is left unnamed.
In this case the anonymous function is just one line in length, calling on the open() method of the InfoWindow. The open() method's first parameter specifies the Map to display the InfoWindow on, while the second specifies an object to anchor the InfoWindow to (typically a Marker, Polyline or Polygon).
Save the file and reload it in your browser. Clicking on the marker should now open an info window that contains a link to a website. Note that we could have inserted any amount of HTML text into the info window.
Now, let's say we want to add a second marker with its own associated info window text. We could repeat the same statements (beginning with the creation of the latlng variable and ending with the call to the addListener method). I've added such code to an example page so that you can see why an approach like this doesn't work:
Do not enter this code into your file. This is the wrong way to add a second marker. We'll see a better way to do this momentarily.
var latlng = new google.maps.LatLng(40.812, -77.8565); var markerOpts = { position: latlng, map: map }; var marker = new google.maps.Marker(markerOpts); var infoWindowOpts = { content: '<a href="http://www.gopsusports.com/facilities/' + 'beaver-stadium.html">Beaver Stadium</a>' }; var infoWindow = new google.maps.InfoWindow(infoWindowOpts); google.maps.event.addListener(marker, 'click', function() { infoWindow.open(map, marker); });
View my bad marker example page [10] and click on the markers to see why there is a problem with adding a second marker this way. If you click on the northern marker, an info window opens with the correct text. However, if you click on the southern marker, the info window associated with the northern marker opens again.
Note that this piece of code re-uses a number of variables that I had defined earlier in the script. The problem with re-using these variables in this way is that when the click event for either marker is being triggered, the variables are holding the values last assigned to them, rather than the values that were originally assigned when the event handlers were set up.
The simplest solution to this problem is to use different variable names for each marker (e.g., infoWindowOpts2, infoWindow2). However, if you're adding more than two or three markers, you'll find that such a solution is inefficient. A better solution is to create a function you can call on any time you want to create a new marker with an associated info window. That is the approach we'll take.
Add the following function to your lesson4.html file, just prior to the initMap() function (after the initMap() function would be fine also):
function createMarker(point,info,map) { var markerOpts = { position: point, map: map }; var marker = new google.maps.Marker(markerOpts); var infoWindowOpts = { content: info }; var infoWindow = new google.maps.InfoWindow(infoWindowOpts); google.maps.event.addListener(marker, 'click', function() { infoWindow.open(map,marker); }); }
This function accepts a LatLng object for its first parameter (stored locally in the function in the point variable), a string for its second parameter (stored in the info variable) and a Map object for its third parameter. A marker is created from the point and a handler is set up for the marker's click event that displays an info window containing the text passed into the info variable. The marker is added to the Map object passed into the map variable. (It might help to keep in mind that pages aren't restricted to having just a single map.)
In the next few steps, we'll modify the initMap() script so that it creates the marker using the new function.
First, a word about the map variable. Going back to the Google Hello World example, you've been working with a map variable defined outside your initMap() function. This gives the variable a global scope, meaning it can be used in any script or function found in the document. Thus, in this new createMarker() function we just added, we could easily utilize the global map variable to specify which map the marker should be added to. If we were going that route, it would not make sense to define a map parameter in the createMarker() function definition. However, defining the function with a map parameter makes it a bit more flexible because it won't be locked into adding markers to a single map. When going this route, it doesn't make sense to use a global variable for the map.
Remove the var map statement found outside your initMap() and createMarker() functions.
Modify the var marker statement to read:
createMarker(latlng, '<a href="https://www.youtube.com/watch?v=aLSFcF8SOiw">' + 'My Hometown</a>', map);
Now that you've seen how to display an info window in response to a click on a marker, let's see how you can plot a marker using a custom icon.
Unless you specify otherwise, markers you add to a map will be displayed using a red teardrop-shaped icon with a black circle at its center. However, you can change this icon to any image stored in .png format that has been sized appropriately. We'll first look at some icons published by fellow Google Maps developers. Later, we'll see how you might go about creating your own icons.
There are lots of sites that provide useful Google Maps icons. Here are some of my favorites:
Return to your lesson4.html document and add a variable named img to the createMarker() function's parameter list after the map variable:
function createMarker(point,info,map,img) {
Next, above the code that already exists inside the function, specify an Icon object as follows:
var iconURL = 'icons/' + img; var iconSize = new google.maps.Size(20,34); var iconOrigin = new google.maps.Point(0,0); var iconAnchor = new google.maps.Point(10,34); var myIcon = { url: iconURL, size: iconSize, origin: iconOrigin, anchor: iconAnchor };
The first line of this code creates a string defining the location of the icon image. In this case, the image is located in a sub-folder -- relative to the location of the HTML page -- named icons, so the URL is built by concatenating 'icons/' with the name of the image that was passed into the createMarker() function.
The next line creates an object of the Size class and stores a reference to it in a variable. Size objects are defined simply by specifying their width and height in pixels within the class constructor.
The next two lines create objects of the Point class, a class that is used to create objects that define positions on images in pixel coordinates. For example, the coordinates defining the center of a 16x16 pixel image would be 8,8. When defining a Point object, the first argument to the class constructor is the x coordinate and the second argument is the y coordinate. Here the first Point object is being used to define the coordinates of the image's origin (the pixel in the upper-left corner). It is generally a good idea to set these coordinates to 0,0. The second Point object is being used to define the part of the image that should appear to be touching the map (i.e., where it should be anchored). For the teardrop-shaped icons, the anchor point should be defined as 10,34 (10 pixels to the right of the origin and 34 pixels down). It turns out that the API assumes an anchor point of (width/2, height) if one is not defined, so it is not really necessary to define one for this icon. But this shows you how to define the anchor point if you use an icon that has a different shape.
You can find the width and height of an image in Windows Explorer by right-clicking on it, selecting Properties, and viewing the Details tab. You can determine the coordinates of your image's anchor point using any image manipulation software. For example, in Microsoft Paint, you would simply open the image, hover the mouse over the desired anchor point and make note of the coordinates reported in the lower right of the window.
Finally, the four variables are used to set the properties associated with Icon objects. These property settings are stored in a variable called myIcon. With the Icon created, it can be used to set the icon property of the MarkerOptions object.
Add the line in bold to your code:
var myMarkerOpts = { position: latlng, map: map, icon: myIcon }
Recall that we added an img parameter to the createMarker() function's parameter list. Our next step will be to return to the initialize() function and specify the desired image name when making a call to the createMarker() function.
In the initialize() function, modify the calls to the createMarker() function so that they include the name of the desired icon (I used blue, you should specify the color you uploaded to your web space):
createMarker(latlng,"some info window content",map,"blue_MarkerA.png"); ... createMarker(latlng,"some other info window content",map,"blue_MarkerB.png");
There are a number of different ways to set up the passing of the icon name to the createMarker() function. For example, if your page only used blue markers, you could choose to "hard-code" that into the function (var iconURL = 'icons/blue_Marker' + img + '.png';). In that scenario, your calls to the function would simply include the desired letter ('A', 'B', etc.).
When using someone else's images as we did here, be sure that the owner of the images does not object. Also, save a local copy of the images to your server instead of referencing the originals. It is bad etiquette to ask someone else's server to serve icons that are being used on your site.
At this point, you could view the map and see your custom icon. However, the map would suffer from a relatively minor deficiency: when hovering the mouse cursor over the icons, the cursor will change (i.e. the marker will become clickable) whenever the cursor is hovered over the entire 20x34 image instead of just the visible part of the image. To fix this problem, we'll need to set the MarkerOptions object's shape property.
The shape property is set using a MarkerShape object, which has two required properties: coord and type. See the documentation of the MarkerShape class [15] and note the different ways of specifying the shape depending on whether the marker is circular, rectangular, or irregular. In this instance, our marker has an irregular shape, so we need to set the type property to 'poly'. We also need to set the coord property equal to an array of x/y coordinate pairs that delineates the shape.
var iconShape = [8,33, 6,21, 1,13, 1,5, 5,1, 13,1, 18,6, 18,13, 12,21, 10,33]; var myMarkerShape = { coord: iconShape, type: 'poly' };
Note that I inserted spaces between coordinate pairs to make the list more readable. The spaces make no difference to the API.
To determine the coordinates that describe your marker's shape, you should use some sort of image manipulation package like MS-Paint. Trace the boundary of the icon with your mouse, stopping where you would place the vertices if you were digitizing the shape in GIS software, for example. The software should report the coordinates as you move the mouse around, so you should jot down the coordinates at each stop.
Finally, we can set the shape property of the MarkerOptions object by adding the code in bold:
var myMarkerOpts = { position: latlng, map: map, icon: myIcon, shape: myMarkerShape }
As mentioned above, any png-formatted image can be used as an icon for markers you add to your maps. If you wish to create your own icon, here are a number of points to consider:
Now that you've seen how to jazz up your pages a bit with custom marker icons, let's move beyond plotting simple points to more complex line and polygon geometry.
Though you won't be expected to create maps with lines or polygons at this stage in the course, we're going to cover how to do so now. You can copy and paste the example code into test documents of your own if you like, but it is also OK if you just read through the examples.
Adding a line to a map actually involves creating an object of the Polyline [17]class. The name Polyline comes from the fact that a number of shorter straight lines are drawn in sequence to build the complete geometry. A straight line is drawn between the first vertex and the second vertex. Then another straight line is drawn between the second vertex and the third vertex. This continues until the last vertex in the sequence is encountered and the polyline is complete.
As you might guess, a Polyline is created by passing a PolylineOptions [18]object to the Polyline class constructor. As with the MarkerOptions class, a PolylineOptions object must have its map property set to specify which Map the Polyline should be added to. However, unlike MarkerOptions which has a position property that is set using a LatLng, PolylineOptions has a path property that is set using an array of LatLng objects.
To customize the look of the Polyline, the PolylineOptions class has three simple properties:
Here is a simple example of a three-vertex line drawn in red with 100% opacity and four pixels in width:
var lineCoords = [ new google.maps.LatLng(40.73033, -77.87844), new google.maps.LatLng(40.812, -77.8565), new google.maps.LatLng(40.812419, -77.922449) ]; var lineOpts = { path: lineCoords, map: map, strokeColor: "#FF0000", strokeOpacity: 1.0, strokeWeight: 4 }; var line = new google.maps.Polyline(lineOpts);
The first statement creates the array of LatLng objects representing the desired line. Recall from earlier in the lesson that a simple way to construct an array is to put together a list of items separated by commas and enclosed within square brackets. In the next lesson, we'll dig deeper into other ways arrays can be constructed.
After the array is built and stored in the lineCoords variable, the next statement creates a PolylineOptions object as an object literal. The lineCoords array is used to set the path property, a variable called map is used to set the map property, the hexadecimal value of "#FF00000" is used to set the strokeColor to red and the strokeOpacity and strokeWeight properties are set to the values of 1 and 4 respectively. A variable called lineOpts is used to store a reference to the PolylineOptions object literal.
With the PolylineOptions object defined, the last statement passes that object to the Polyline class constructor to create a new Polyline object.
You can see this code in a complete working example (polyline) [19].
By default, the lines connecting the vertices in a Polyline are drawn such that they are straight in the two-dimensional Mercator projection used by the base map. But the Earth is not flat and the default two-dimensional lines are not really the shortest paths between the points. Great-circle lines (also called geodesics) represent the true shortest paths because they take the Earth's curvature into account. For large-scale maps like the one above, the difference between three-dimensional paths and two-dimensional paths is negligible, and you as the map author need not worry about the issue. However, if your map is of a regional or continental scale, the difference may be quite significant.
Fortunately, switching from a two-dimensional polyline to a geodesic is as simple as setting the PolylineOptions object's geodesic property to true. Have a look at this geodesic example [20] showing both types of lines connecting Miami to San Francisco.
In addition to illustrating the geodesic concept, this example also shows how properties of an object literal can be re-set to new values after they've been initialized. Because I wanted to draw the same Polyline with different values for just the geodesic and strokeColor properties, I didn't need to create a new PolylineOptions object. I could simply refer to the existing object through the lineOpts variable name and use the 'object.property = value'' syntax to assign new values to the properties I wanted to change.
Adding simple polygons to a map follows a pattern similar to what you just saw for lines. The end goal is to create a Polygon [21]object from a PolygonOptions [22]object. As with Polylines, you need to create an array of LatLng objects that delineate your Polygon's boundary. (I recommend making the last LatLng in the array the same as the first, though the documentation says that Polygons will close themselves if not closed explicitly.) Like PolylineOptions, PolygonOptions objects have a strokeColor, strokeOpacity, and strokeWeight property. These properties control the appearance of the polygon outline. To control the appearance of the polygon's interior, you will need to set the fillColor and fillOpacity properties.
Have a look at this PolygonsOptions example [23] showing the boundaries of the District of Columbia. Note in the source code that the fillColor and fillOpacity are not set. This demonstrates that the default values for these properties appear to be gray and ~50% opaque.
You should also note from the example that PolygonOptions doesn't have a path property, but instead a paths property. This difference arises from the fact that the API also allows for the display of more complex polygons, such as those with holes in their interior (donut polygons) and those made up of multiple parts (like the islands of Hawaii). In both cases, the idea is to create an array of coordinate arrays. In other words, for a donut polygon, you would create one coordinate array depicting the exterior of the polygon and a separate array depicting the hole. For a multi-part polygon, you would create a separate array for each part. Once all the coordinate arrays have been stored in variables, those coordinate array variables should be put into an array and used to set the paths property. This example demonstrates the depiction of a famous donut polygon, the Pentagon. [24]
So far in the course, you've been encouraged to use a plain text editor like Notepad and have received little guidance on the topic of debugging code that's not producing the desired result. There are a number of tools out there that can make your job as a developer easier, and that will be the focus of this part of the lesson.
jsFiddle.net [25] is a "playground" environment for seeing code and its output side-by-side. HTML, CSS and JavaScript code is entered in separate boxes, then the output is shown in a fourth box. Here's a quick tutorial on using jsFiddle:
jsFiddle is often used by the folks who participate in help forums such as Stack Overflow. If you find yourself in need of help down the road, putting your code into jsFiddle and including a link to your fiddle in your post would be a good idea.
Note: Google used to maintain a similar environment specifically for their APIs called the Google Code Playground. That no longer appears to exist, but I encourage you to keep your eyes open to see if they resurrect it.
Plain text editors like Notepad are OK for beginners getting to know web-publishing languages, but there are more advanced tools out there that make the developer's job easier. Integrated Development Environments (IDEs) are software applications that allow developers to write, test, and debug source code all in one application. IDEs also typically provide auto-completion and syntax highlighting. Examples of popular IDEs include Microsoft Visual Studio and Eclipse. Google's Code Playground can be thought of an online JavaScript IDE. However, it lacks syntax highlighting and, obviously, does not allow for editing code stored on your local file system.
There are lots of IDEs suited for web development, as a web search for "web development ide" [27] will show you. For the purpose of continuing on through this course, I'm going to recommend Notepad ++ [28]. As its name implies, It's a step (or two) up from Notepad. My main reasons for pointing you to it are that it is free, it's easy to use, and it provides line numbers and syntax highlighting. Notepad++ is arguably not a true IDE since it's not possible to view your pages directly within the same application, but it does allow you to easily launch them in various web browsers, so I think it's a rather pointless debate.
Let's walk through a short set of steps to familiarize you with Notepad++.
Now that we've worked with a more fully-featured source code editor, let's discuss some tips for working out the kinks in pages that aren't giving you the results you expect.
So, what do you do when you load your page in a browser and get... nothing? Thankfully, there are tools that can help pinpoint problems in your code without having to manually search line by line. Let's have a look at a couple of broken Hello, World pages and how we can determine why they're broken.
While checking for errors in this way can often be a big help, there are likely to be times when you're unable to identify the problem in your code. Another method for pinpointing problems is to produce some output at strategic points in your code. This could be as simple as an alert() statement that confirms that code execution reached a certain point, like
alert("Finished adding overlays");
You might also use an alert() box to display the contents of a variable or the property of some object:
alert("The coords are: " + pt.lat() + ", " + pt.lng());
An alternative to using alert() in this way is to write messages to the JavaScript console. This is especially preferable when you want to diagnose a problem with a loop and would rather not have to dismiss alert() boxes repeatedly. Writing messages to the console can be done as follows:
console.log("The value of i is " + i);
There are certainly more sophisticated debugging methods and tools out there (e.g., a plugin for Firefox called Firebug). However, over years of web development, I have found the simple ones discussed above to be sufficient.
I'll end this lesson's content with a few tips that I hope will make you a bit more successful as a coder. This advice applies regardless of the kind of programming you're doing:
With that, you've finished reading all of the material associated with this lesson. For your graded assignment, you'll be asked to put many of these coding techniques into practice by developing a page that displays a few markers (and associated info windows) using a custom icon.
If you've taken my other programming classes, I apologize for once again dredging up this scenario! In the project that you'll submit for grading this week, you are required to create a map that displays the locations of the four candidate cities from that scenario. You will depict these sites using a custom ice cream cone icon. Here are the detailed instructions:
This project is one week in length. Please refer to the course Calendar, in Canvas, for the due date.
In this lesson, you've seen how to modify the Google Maps user interface and basemap, open info windows when markers are clicked, implement a custom marker icon, and code the addition of line and polygon overlays. To this point, we've only dealt with a small number of points and have been able to hard-code their locations in our JavaScript code.
In the next lesson, you'll learn how to handle a larger volume of data than can be hard-coded into your page. You'll also learn how to add a sidebar that lists the names of your features and how to use the API's address geocoder.
Links
[1] https://developers.google.com/maps/documentation/javascript/reference?csw=1#MapTypeControlOptions
[2] https://developers.google.com/maps/documentation/javascript/controls?csw=1
[3] http://www.personal.psu.edu/jed124/styled_map1.html
[4] https://developers.google.com/maps/documentation/javascript/reference#MapTypeStyle
[5] https://developers.google.com/maps/documentation/javascript/reference#MapTypeStyleFeatureType
[6] https://developers.google.com/maps/documentation/javascript/reference#MapTypeStyleElementType
[7] https://developers.google.com/maps/documentation/javascript/reference#MapTypeStyler
[8] http://www.personal.psu.edu/jed124/styled_map2.html
[9] http://googlemaps.github.io/js-samples/styledmaps/wizard/index.html
[10] http://www.personal.psu.edu/jed124/bad_markers.html
[11] http://www.personal.psu.edu/jed124/good_markers.html
[12] http://mapicons.nicolasmollet.com/
[13] http://www.benjaminkeen.com/?p=105
[14] http://sparce.cs.pdx.edu/mash-o-matic/tools.html
[15] https://developers.google.com/maps/documentation/javascript/reference?csw=1#MarkerShape
[16] http://favicon.htmlkit.com/favicon/
[17] https://developers.google.com/maps/documentation/javascript/3.exp/reference#Polyline
[18] https://developers.google.com/maps/documentation/javascript/3.exp/reference#PolylineOptions
[19] http://www.personal.psu.edu/jed124/polyline.html
[20] http://www.personal.psu.edu/jed124/geodesic.html
[21] https://developers.google.com/maps/documentation/javascript/3.exp/reference#Polygon
[22] https://developers.google.com/maps/documentation/javascript/3.exp/reference#PolygonOptions
[23] http://www.personal.psu.edu/jed124/polygons.html
[24] http://www.personal.psu.edu/jed124/donut_polygons.html
[25] https://jsfiddle.net
[26] http://jsfiddle.net/9jme51zL/
[27] https://www.google.com/search?q=web+development+ide
[28] http://notepad-plus-plus.org/
[29] http://notepad-plus-plus.org/download/
[30] http://www.personal.psu.edu/jed124/hello_world_broken1.html
[31] http://www.personal.psu.edu/jed124/hello_world_broken2.html
[32] https://www.e-education.psu.edu/geog863_gmaps/sites/www.e-education.psu.edu.geog863_gmaps/files//jen_barry.zip