CHAPTER |
|
8 |
Feature Annotation: Drawing Shapes on Maps |
Polyline
, Polygon
, Rectangle
, and Circle
objects. Routes are represented by polylines, while property and administrative outlines are represented by polygons.LatLng
objects. These locations are the line’s breakpoints and are known as vertices. Figure 8-1 shows a polyline that corresponds to one of the lines of the Los Angeles metro system, the Golden Line. The figure was generated by the LA Golden Metro Line.html
page, included with this chapter’s material. The stations are marked with RichLabel controls, as you recall from the preceding chapter.Polyline
object and place it on the map by calling its setMap()
method. The constructor of the Polyline
object expects an array of LatLng
objects that define the polyline’s path. This array must be passed to the constructor with the path
attribute. Here’s a short segment of the definition of the metro line’s path:Polyline
object, which will be rendered on the map with the default color and default width:path
attribute is the only mandatory attribute of the Polyline
object. To specify any of the optional attributes, add more items to the array following the constructor. The other two basic attributes of the Polyline
’s constructor are the line’s width, which is specified in pixels, and its color. Because the polyline has a color, it can also have an opacity value.GoldenLine
object on the map, you must call its setMap()
method, passing the name of a Map
object as an argument:Polyline
object’s setOptions()
method, which accepts as an argument an array of key-value pairs: The keys are property names and the values are the property values. The following statement changes the color and width of the line on the map:setPath()
method, which accepts as an argument another path.path
attribute, and they have a strokecolor
and a strokeWeight
attribute. In addition, you can specify the polygon’s fillColor
attribute. Note that polygons are always closed and you don’t have to repeat the initial point to close the shape because the end point will be connected to the initial point automatically. In effect, you can use the path of a polyline to create a polygon with the exact same shape.setOptions()
method and passing as argument an array with the attributes you want to change and their new values, similar to the array passed to its constructor.LatLng
objects, and the polygon of Figure 8-2 was based on this path. The polygon encloses the continental part of Italy. As you probably know, Italy encompasses two large islands, as well as many smaller ones. Additional shapes can be defined with additional paths, as you will see in the section “Polygon Islands” later in this chapter. The polygon looks quite reasonable at a moderate zoom level. As you increase the zoom level, the differences between the actual borders on the map and the polygon’s shape become noticeable.Polygons.hml
page.Polygon
object provides two methods for specifying its shape: setPath()
and setPaths()
. The difference between the two methods is that setPaths()
allows you to specify multiple paths for the polygon. The first shape is the basic polygon and the remaining ones represent holes in the outer shape. The state of Utah with a hole that corresponds to Utah Lake was generated with the drawPolygon()
function, which accepts as an argument the path of the shape to draw:state
argument is either a path (an array of vertices) or an array or paths, each path being an array of vertices. The statements shown in Listing 8-1 create the path for the state of Utah, and call the drawPolygon()
function with the path of Utah as argument. The path of Utah is made up of two paths. The first is the outline of the state and the other one is the outline of Utah Lake, as shown in the following listing. The resulting shape is commonly referred to as “donut-shaped.”utah
array is populated in its initializer, while the lake
array was populated by pushing one element at a time. Open the file Polygons.html
, which draws the outlines of three states on the map. One of the states is Utah, which includes a polygon that delimits Utah Lake. The other two polygons correspond to Nevada and Wyoming, whose borders are made up of large straight lines.Islands Polygons.html
file, you will see that the script creates a single Polygon
object with a path that contains three arrays. Listing 8-2 shows just the basic statements of the script.LatLng
objects. The three individual paths are combined into an array of paths, the LosAngelesPath
variable, which is then passed to the Polygon
object’s setPaths()
method. Note that you must use the setPaths()
method, even if you're passing a single array as an argument, because the argument is an array of arrays.click
event and expect that users will identify a location with the mouse and click on it. This approach is neither precise nor user friendly. A better approach is to display a crosshair cursor at the center of the map, like the one shown in Figure 8-5, and allow users to scroll the map until they bring the location they’re interested in exactly under the crosshair.getBounds()
method to retrieve the horizontal and vertical extents of the current viewport.bounds_changed
event. Otherwise, the crosshair will scroll with the map because it was drawn at a specific location on the map. Moreover, users may not wish to keep the crosshair visible at all times, so you must give them the option to show or hide the crosshair. In Chapter 5, you saw how to add to the context menu a command that shows/hides the crosshair. Now that you know how to draw lines on the map, let’s look at the statements that actually draw the crosshair of the Context Menu.html
sample page of Chapter 5.crosshair
variable that indicates the visibility of the crosshair. Set the crosshair variable to true
or false
to control whether the crosshair will be displayed initially.drawCrosshair()
function does all the work. It extracts the coordinates of the minimum and maximum latitude and longitude values, calculates the coordinates of the endpoints of the two lines, and then draws them on top of the map. The code of the drawCrosshair()
function is shown in Listing 8-3, which is a bit condensed to fit on the printed page.centerHLine
and centerVLine
are two Polyline
objects that represent the two lines that make up the crosshair. The horizontal line’s latitude is the latitude of the map’s center point, and it extends from the minimum to the maximum visible longitude. Similarly, the vertical line’s longitude is the longitude of the center point, and it extends from minimum to the maximum visible latitude. The script relies on the crosshair
variable, declared at the script’s level, which indicates whether the crosshair cursor is visible at the time. If the cursor is not currently visible, the script draws two perpendicular lines that meet at the center of the map. If the crosshair is visible, then the script removes the cursor from the map by setting the map
attribute of the centerHLine
and centerVLine
variables to null.LatLngBounds
object, which describes both the location and size of the rectangle. Other than that, they have the same properties as polygons. In fact, you can create rectangular polygons and ignore the Rectangle
object. Use the StrokeColor
and FillColor
to specify their appearance, the StrokeOpacity
and FillOpacity
properties to set the rectangle’s opacity, and the StrokeWeight
to set the width of the rectangle’s outline.Rectangle
object, use a constructor like the following:bounds
property is mandatory; all other properties are optional. The LatLngBounds
object’s constructor accepts two arguments, which are the coordinates of the lower-right (south-west) and upper-left (north-east) corners of the rectangle. While polylines and polygons are used extensively in mapping applications, there aren’t many features that can be approximated by rectangles and circles. These two shapes are too perfect for the shapes you usually mark on the map.LatLng
object as usual (a location on the map), and the radius is specified always in meters. Here’s the constructor of the Circle
object:setOptions()
method as usual, or the setCenter()
and setRadius()
methods, passing the appropriate value as argument. As far as events go, you can detect the usual mouse on the circle as well as the center_changed
and radius_changed
events, which aren’t very common in coding mapping applications. You can fill the circle with a solid color to create a disk with the strokeColor
and strokeOpacity
properties.drawGeodesicCircle()
function, which approximates a circle by creating a polygon with 500 vertices, all on the circumference of the underlying circle. The function performs simple trigonometric calculations. Okay, they aren’t simple because the circle is drawn on a sphere, but it’s not much different from calculating points along the circumference of a circle on a flat surface. Most readers who need this function will simply paste it in their script, so I won’t get into advanced trigonometric topics here.drawGeodesicCircle()
function accepts two arguments, the circle’s center and its radius, and returns an array of LatLng
objects, the points
array, which you can use to draw a polygon that approximates the equivalent circle. You may have noticed that the name of the function is drawGeodesicCircle()
and not drawCircle()
. A geodesic circle is a perfect circle on the sphere, not on the flat map. At smaller scales, the two circles are identical because small sections of the earth’s surface are practically flat. At large scales, when the circle’s radius exceeds 1,000 kilometers, for example, the perfect circle on the sphere is not nearly as perfect when projected onto the flat map. The topic of geodesics, along with the distortion introduced by projecting large earth features on a flat map, is discussed in detail in Chapter 10. In the same chapter, you will find more interesting examples of circles drawn on a Google map.Circle
object’s radius in pixels; this will cause you problems if you’re trying to use a circle as a marker on the map. Because the circle’s radius is defined in meters, its actual size will follow the zoom level. If you zoom out far enough, the circle will practically disappear, and if you zoom too deeply, the circle may fill the entire map.path
property of the polylines and polygons is an array of geo-locations; you can manipulate the shape’s outline by manipulating the elements of the path. However, changing an element’s value won’t have an immediate effect on the shape that has been rendered on the map. You must create a new object based on the revised path and place it on the map, in place of the previous version of the same shape.MVCArray
, which can be bound to another entity. As you recall from Chapter 4, MVC stands for Model-View-Controller. It’s a design pattern that allows the same data to be presented in different manners. As soon as the data is updated, the view of the data is also updated. The MVCArray
object can be bound to a path or other object and update the object it’s linked to. If you use a MVCArray
to store the shape’s path and then assign this object to the shape’s path
property, the path
property will remain bound to the MVCArray
. This means that every time you change an element in the MVCArray
, or add a new vertex to the path, the shape will be updated automatically. MVCArrays
are extremely useful in programming with the Google Maps API because they allow you to focus in your code, and not the plumb work required to keep the map up-to-date. If you stored the vertices of a path to a regular array and then assigned this array to the path
property of a polygon, the polygon would assume the shape of the path the moment of the assignment. After that, you could change the array without affecting the shape; you would have to reassign the array to the path
property to change the polygon’s shape.Editable PolyLine.html
application; the script that drives the functionality of this page is discussed in the following section.MVCArray
object with the path’s vertices. This array is used as the shape’s path so that when a vertex is moved and the code updates the selected vertex’s location, the shape changes instantly.Polyline
and Polygon
objects. What if you want to create a new shape by tracing a feature on the map, such as a train track, a delivery route, a network of cables, and so on? Wouldn’t it be useful to be able to edit the polyline right on the map? Indeed, this is one of the most basic features of a GIS system, and you’re going to build an application for tracing maps in Chapter 9. In this chapter, you will find all the information you need for designing editable polylines and polygons. The Editable PolyLine.html
page shown in Figure 8-7 demonstrates a technique for editing a polyline interactively. Each vertex of the polyline is identified by a marker and users can edit the polyline by dragging the markers with the mouse. This isn’t exactly an editable polyline because this technique doesn’t allow users to delete vertices or insert new ones, but it’s the first step in designing truly editable polylines.GoldenLine
, contains the coordinates of the metro stations, which are also the polyline’s vertices, and the other one, the stations
array, contains the names of the stations. Here are the constructors of the two arrays:linePath
variable. The linePath
variable holds the coordinates that define the polyline based on the GoldenLine
array. However, the script generates an MVCArray
object based on the GoldenLine
array. It does so by passing the GoldenLine
array as an argument to the constructor of an MVCArray
object. The linePath
array is then used to specify the polyline’s path in the Polyline
object’s constructor. The following two statements place on the map a polyline that represents the Golden line of the Los Angeles Metro:GoldenLine
array and generates a marker for each station. The statement that generates the markers is straightforward; just note that the draggable property is set to true
so that the users of the application can edit the polyline by dragging around the markers that correspond to the polyline’s vertices:markers
array so that the script can access the markers later. If you don't store the individual markers to an array, you won't be able to manipulate them after they have been placed on the map.drag
event listener. In the listener’s code, you must find out the index of the marker being dragged in the markers
array and use this index to access the corresponding vertex in the path to change its coordinates. Here’s the statement that adds the appropriate listener to each marker’s drag
event:markers
array and compares each marker to the marker that fired the event (identified by the keyword this
). When the marker being dragged is located, the code changes the value of the corresponding item in the path
array and sets it to the marker’s current location, which is given by the expression this.getPosition()
. The code is short and simple, but thanks to the ability of the elements of the MVCArray
object to remain bound to the polyline’s vertices, the polyline’s shape is changed as soon as you change the location of any vertex. Listing 8-5 shows the complete script of the Editable PolyLine.html
page (the ellipses in the two array initializers stand for additional elements omitted from the listing).Editable Polyline.html
page and test-drive it. You have a truly interactive map that allows you to position markers very precisely with the mouse. To create an editable polygon, you need only change the definition of the Polyline
object to a Polygon
object. The polygon you specify with the same path will remain closed at all times as you edit its path, without any additional code.path
array changes, the polygon is redrawn on the map and its new shape is dictated by the current locations of its path’s vertices. The short application you created in this section is the beginning of an interactive map drawing application. This is the topic of the next chapter, where you will implement an application that allows users to edit shapes interactively on top of the map.Highway 5.html
page, included in this chapter’s support material.path
property, is one of the predefined symbols that come with the API, and it’s a circle. The predefined symbols are members of the google.maps.SymbolPath
enumeration, which includes the following: CIRCLE
, BACKWARD_CLOSED_ARROW
, FORWARD_CLOSED_ARROW
, BACKWARD_OPEN_ARROW
, and FORWARD_OPEN_ARROW
. In addition to the API’s predefined symbols, you can create your own symbols using the SVG (Scalable Vector Graphics) notation, which is discussed briefly in Chapter 20 and Appendix A. The symbol created with the preceding statements is a white circle with a medium-width red outline. The scale
attribute is very important: It’s the scaling factor of the symbol in pixels. The default size of the symbol (when scale is 1) is the same as the polyline’s stroke width. Because their sizes are specified in pixels, symbols are not affected by the zoom level: They have the same size at all zoom levels.Polyline
object’s icons
property. This property is an array of symbols, and it usually contains a single item. In addition to the symbol, you must also specify one of the offset
or repeat
properties. The offset
property is the distance from the beginning of the line, where the symbol will appear; it can be expressed either in pixels or as a percentage. This property’s default value is “100%” (it’s a string). The repeat
property is used when you want to repeat the symbol along the line’s path; its value is the distance between successive symbols on the line and it can be expressed either in pixels or a percentage. The following is a typical icons
property definition:Polyline
object’s constructor, the specified symbol will appear at the beginning of the line. A more interesting example is the following, which repeats the symbol every 25 pixels along the path, as shown in Figure 8-8.US5
holds the vertices of the path, and the icons
property determines the symbol and how it will be repeated along the path. The symbol is a circle icon, identified by the dotSymbol
variable created earlier, and it’s repeated every 25 pixels. All these settings are combined in the constructor of the US5Route
object, which is a Polyline
object.repeat
attribute is specified in pixels, not as a percentage. The distance between consecutive symbols in Figure 8-9 is the same, even though the second map of Figure 8-9 covers a tiny segment of the highway.18.191.157.197