<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2008 Google Inc.
Licensed under the Apache License, Version 2.0:
http://www.apache.org/licenses/LICENSE-2.0
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Maps Street View API Driving Directions Example</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAjU0EJWnWPMv7oQ-jjS7dYxSPW5CJgpdgO_s4yyMovOaVh_KvvhSfpvagV18eOyDWu7VytS6Bi1CWxw" type="text/javascript"></script>
<script type="text/javascript">
/**
* Copyright (c) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Author: Thor Mitchell
*
* Street View API Example: Following driving directions in Street View,
*
* We extract the polyline generated for the route and then attempt to
* follow links that match the direction of the line segment as we move
* along it. Sometimes we find a gap in coverage, or that a link does
* not exist in the direction we need. When this happens we jump to the
* start of the next step given in the text directions.
*/
/**
* GMap2 of the route map.
*/
var map;
/**
* GStreetviewPanorama of the Flash viewer.
*/
var pano;
/**
* GStreetviewClient used to retrieve meta data for panoramas we reach
* by following links.
*/
var svClient;
/**
* GDirections object used to submit driving direction requests.
*/
var directions;
/**
* The GRoute we are following extracted from the directions response.
*/
var route;
/**
* It turns out that the polyline generated for a driving directions
* route normally has a lot of repeated vertices. This causes problems
* when trying to determine how close we are to the next vertex, so it's
* better to collapse these duplicated vertices out.
*
* The array of route vertices with duplicates removed.
*/
var vertices;
/**
* Array that maps the polyline vertex indices to the
* index of the same point in the vertices array.
*
* For example, if the polyline vertices are
* [a, a, b, c, d, d, e], the vertices array will be
* [ a, b, c, d, e ] and the vertexMap array will be
* [ 1, 1, 2, 3, 4, 4, 5 ].
*/
var vertexMap;
/**
* Array that contains the index in the vertices array of
* the point at the start of the n'th step in the route
*/
var stepToVertex;
/**
* An array that gives the route step number that each
* point in the vertices array is part of.
*/
var stepMap;
/**
* The current position of the panorama and vehicle marker.
*/
var currentLatLng;
/**
* Metadata for the current panorama including the list of
* available links, loaded using GStreetviewClient.
*/
var panoMetaData;
/**
* boolean flag set when we are so close to the next vertex that we should
* check links in the panoramas we load for the next turning we need.
*/
var close = false;
/**
* The direction in degrees from our current location to the next
* vertex on the route. Used to select the most suitable link to follow.
*/
var bearing;
/**
* The direction from the next vertex on the route to the vertex
* after that. Used when we are close to a vertex and are looking
* for links that represent the next turn we need to make.
*/
var nextBearing;
/**
* The index of the vertex we are heading towards on the route in the
* vertices array.
*/
var nextVertexId;
/**
* GLatLng of the vertex we are heading towards on the route.
*/
var nextVertex;
/**
* An array that at any time contains the GLatLng of each vertex
* from the start of the current route step to the next vertex
* ahead of our current position. This is used to work out how
* far we are along the current step.
*/
var progressArray;
/**
* The distance in meters covered by traversing the points in the
* progressArray. By subtracting the distance from our current location
* to the next vertex from this value we find how far along the step
* we are, and use this to update the progress bar.
*/
var progressDistance;
/**
* Index of the route step we are currently on.
*/
var currentStep;
/**
* The marker on our map that shows the current location. For IE6 this
* is a standard red maps pushpin. For all other browsers it is a
* directional arrow.
*/
var carMarker;
/**
* A copy of the current step index used to unhighlight the previously
* highlighted step in the textual driving directions when the
* current step changes.
*/
var selectedStep = null;
/**
* boolean flag indicating whether we are currently driving (automatically
* following) links, or are stationary.
*/
var driving = false;
/**
* Id of the timer that adds a delay between following each link to give the
* panorama time to load. We need this to cancel the timer if the user clicks
* Stop while we are waiting to follow the next link.
*/
var advanceTimer = null;
/**
* Delay in seconds between following each link.
*/
var advanceDelay = 1;
/**
* Set up the initial view and register the various event listeners.
*/
function load() {
if (GBrowserIsCompatible()) {
var start = new GLatLng(37.090240,-95.712891);
map = new GMap2(document.getElementById("map"));
map.setCenter(start, 3);
map.addControl(new GSmallMapControl());
carMarker = getCarMarker(start);
map.addOverlay(carMarker);
carMarker.hide();
svClient = new GStreetviewClient();
pano = new GStreetviewPanorama(document.getElementById("streetview"));
GEvent.addListener(pano, "initialized", function(loc) {
panoMetaData = loc;
moveCar();
});
GEvent.addListener(pano, "error", function(errorCode) {
showStatus("The requested panorama could not be displayed");
});
directions = new GDirections(map);
GEvent.addListener(directions, "load", function() {
jumpInMyCar();
});
GEvent.addListener(directions, "error", function() {
showStatus("Could not generate a route for the current start and end addresses");
});
}
}
/**
* Create a vehicle marker suited to the user's browser
* Avoids a wierd IE6 bug that causes black backgrounds
* if we use the driving direction arrows as marker icons
* @param {GLatLng} start Initial location of the vehicle marker
* @return {GMarker}
*/
function getCarMarker(start) {
/*@cc_on @*/
/*@if (@_jscript_version < 5.7)
return new GMarker(start);
/*@end @*/
return new GMarker(start, getArrowIcon(0.0));
}
/**
* Set the vehicle marker icon based on the user's browser
* Avoids a wierd IE6 bug that causes black backgrounds
* if we use the driving direction arrows as marker icons
* @param {number} bearing The heading in degrees of the arrow we need
*/
function setCarMarkerImage(bearing) {
/*@cc_on @*/
/*@if (@_jscript_version < 5.7)
return;
/*@end @*/
carMarker.setImage(getArrowUrl(bearing));
}
/**
* Submit the driving directions request
* The load event handler calls jumpInMyCar() when this returns
*/
function generateRoute() {
var from = document.getElementById("from").value;
var to = document.getElementById("to").value;
directions.load("from: " + from + " to: " + to, { preserveViewport: true, getSteps: true });
}
/**
* It's too far to walk on your own
*/
function jumpInMyCar() {
/* Extract the one and only route from this response */
route = directions.getRoute(0);
/* Simplify the list of polyline vertices by removing duplicates */
collapseVertices(directions.getPolyline());
/* Center the map on the start of the route at street level */
map.setCenter(vertices[0], 16);
/* Display the textual driving directions */
renderTextDirections();
/* Begin checking the Street View coverage along this route */
checkCoverage(0);
}
/**
* Check that a Street View panorama exists at the start
* of this route step. This is a recursive function that
* checks every step along the route until it reaches the
* end of the route or no panorama is found for a step.
* @param {number} step The route step to check
*/
function checkCoverage(step) {
if (step > route.getNumSteps()) {
/* Coverage check across whole route passed */
hideStatus();
stopDriving();
jumpToVertex(0);
} else {
if (step == route.getNumSteps()) {
ll = route.getEndLatLng();
} else {
ll = route.getStep(step).getLatLng();
}
svClient.getNearestPanorama(ll, function(svData) {
if (svData.code == 500) {
/* Server error, retry once per second */
setTimeout("checkCoverage(" + step + ")", 1000);
} else if (svData.code == 600) {
/* Coverage check failed */
showStatus("Street View coverage is not available for this route");
} else {
/* Confirmed coverage for this step.
* Now check coverage for next step.
*/
checkCoverage(step + 1);
}
});
}
}
/**
* Jump to a particular point on the route. This is used to
* queue up the start of the route, when a user selects a step
* in the driving directions, and when there is a gap in coverage
* that we need to jump over.
* @param {number} idx The vertex number in the vertices array
*/
function jumpToVertex(idx) {
currentLatLng = vertices[idx];
nextVertex = vertices[idx + 1];
nextVertexId = idx + 1;
bearing = getBearingFromVertex(idx);
nextBearing = getBearingFromVertex(idx + 1);
setCarMarkerImage(bearing);
carMarker.setLatLng(currentLatLng);
carMarker.show();
currentStep = stepMap[idx];
constructProgressArray(idx);
setProgressDistance();
updateProgressBar(0);
map.panTo(currentLatLng, 16);
highlightStep(currentStep);
checkDistanceFromNextVertex();
pano.setLocationAndPOV(currentLatLng, { yaw:bearing, pitch:0 });
svClient.getNearestPanorama(currentLatLng, function(loc) {
if (loc.code == 500) {
setTimeout("jumpToVertex(" + idx + ")", 1000);
} else if (loc.code == 600) {
jumpToVertex(nextVertexId);
} else {
panoMetaData = loc.location;
panoMetaData.pov.yaw = bearing;
moveCar();
}
});
}
/**
* Called by the panorama's initialized event handler in
* response to a link being followed. Updates the location
* of the vehicle marker and the center of the map to match
* the location of the panorama loaded by following the link.
*/
function moveCar() {
currentLatLng = panoMetaData.latlng;
carMarker.setLatLng(currentLatLng);
map.panTo(currentLatLng);
/* Now retrieve the links for this panorama so we can
* work out where to go next.
*/
svClient.getNearestPanorama(panoMetaData.latlng, function(svData) {
if (svData.code == 500) {
/* Server error. Retry once a second */
setTimeout("moveCar()", 1000);
} else if (svData.code == 600) {
/* No panorama. Should never happen as we have
* already loaded this panorama in the Flash viewer.
*/
jumpToVertex(nextVertexId);
} else {
panoMetaData.links = svData.links;
checkDistanceFromNextVertex();
if (driving) {
advanceTimer = setTimeout("advance()", advanceDelay * 1000);
}
}
});
}
/**
* Check if we have already passed the next vertex, or if we are
* close enough to the next vertex to look out for the next turn.
*/
function checkDistanceFromNextVertex() {
close = false;
var d = currentLatLng.distanceFrom(nextVertex);
var b = getBearing(currentLatLng, nextVertex);
/* If the bearing of the next vertex is more than 90 degrees away from
* the bearing we have been travelling in, we must have passed it already.
*/
if (getYawDelta(bearing, b) > 90) {
incrementVertex();
/* If the vertices are closely spaced we may
* already be close to the next vertex
*/
if (driving) {
checkDistanceFromNextVertex();
}
} else {
/* Recalculate how far we have travelled within the current step
* and update the progress bar accordingly.
*/
updateProgressBar(progressDistance - d);
if (driving) {
updateViewerDirections(progressDistance - d);
}
/* If we are less than 10m from a vertex we consider ourself to be
* close enough to preferentially follow links that take us in the
* direction we should be going when the vertex has been passed.
*/
if (d < 10) {
close = true;
}
}
}
/**
* Move forward one link
*/
function advance() {
/* chose the best link for our current heading */
var selected = selectLink(bearing);
/* If we're very close to a vertex, also check for a
* link in the direction we should be turning next.
* If there is a link in that direction (to a
* tolerance of 15 degrees), chose that turning
*/
if (close && nextBearing) {
var selectedTurn = selectLink(nextBearing);
if (selectedTurn.delta < 15) {
selected = selectedTurn;
incrementVertex();
}
}
if (selected.delta > 40) {
/* If the chosen link is in a direction more than 40
* degrees different from the heading we want it
* will not take us in the right direction. As no
* better link has been found this implies that the
* route has no coverage in the direction we need so
* jump to the start of the next step.
*/
jumpToVertex(nextVertexId);
} else {
/* Pan the viewer round to face the direction of the
* link we want to follow and then follow the link. We
* need to give the pan time to complete before we follow
* the link for it to look smooth. The amount of time
* depends on the extent of the pan.
*/
var panAngle = getYawDelta(panoMetaData.pov.yaw, panoMetaData.links[selected.idx].yaw);
pano.panTo({ yaw:panoMetaData.links[selected.idx].yaw, pitch:0 });
setTimeout(function() {
pano.followLink(panoMetaData.links[selected.idx].yaw);
}, panAngle * 10);
}
}
/**
* Select which link in the current panorama most closely
* matches the directions we should be going in.
* @param {number} yaw The direction we are looking to move in
* @return {Object} The number of the closest link and the
* difference between it's yaw and the desired direction
*/
function selectLink(yaw) {
var Selected = new Object();
for (var i = 0; i < panoMetaData.links.length; i++) {
var d = getYawDelta(yaw, panoMetaData.links[i].yaw);
if (Selected.delta == null || d < Selected.delta) {
Selected.idx = i;
Selected.delta = d;
}
}
return Selected;
}
/**
* Called when we have reached a vertex
* and now need to head towards the next
*/
function incrementVertex() {
if (! vertices[nextVertexId + 1]) {
/* we are at the end of the route */
endReached();
} else {
nextVertexId++;
nextVertex = vertices[nextVertexId];
/* Rotate the vehicle marler to face the new bearing */
bearing = getBearingFromVertex(nextVertexId - 1);
nextBearing = getBearingFromVertex(nextVertexId);
setCarMarkerImage(bearing);
/* Check if we have reached the next step */
if (stepMap[nextVertexId - 1] == currentStep) {
/* Still on the same step so just extend the
* progressArray with the next vertex we are
* heading towards.
*/
progressArray.push(nextVertex);
} else {
/* We've moved on to the next step so start a new
* progressArray and update the text highlight and
* progress bar.
*/
currentStep = stepMap[nextVertexId - 1];
highlightStep(currentStep);
progressArray = [ currentLatLng, nextVertex ];
updateProgressBar(0);
}
setProgressDistance();
}
}
/**
* Called when the last vertex on the route is reached.
*/
function endReached() {
stopDriving();
updateProgressBar(0);
showInstruction("You have reached your destination");
document.getElementById("step" + selectedStep).style.backgroundColor = "white";
selectedStep = null;
}
/**
* Get the direction to head in from a particular vertex
* @param {number} n Index of the vertex in the vertices array
* @return {number} bearing in degrees
*/
function getBearingFromVertex(n) {
var origin = vertices[n];
var destination = vertices[n+1];
if (destination != undefined) {
return getBearing(origin, destination);
} else {
return null;
}
}
/**
* Update the in Flash viewer next step driving directions
* @param {number} distanceFromStartOfStep Distance in meters from the start
* of the current route step
*/
function updateViewerDirections(distanceFromStartOfStep) {
var lengthOfStep = route.getStep(currentStep).getDistance().meters;
var distanceFromEndOfStep = (lengthOfStep - distanceFromStartOfStep);
/* Convert from meters to feet */
distanceFromEndOfStep *= 3.2808399;
var uiDistance, unit;
/* Convert to human friendly representation */
if (distanceFromEndOfStep > 7920) {
distanceFromEndOfStep /= 5280;
uiDistance = distanceFromEndOfStep.toFixed(0);
unit = 'miles';
} else if (distanceFromEndOfStep > 4620) {
uiDistance = '1';
unit = 'mile';
} else if (distanceFromEndOfStep > 3300) {
/* Display "3/4 mile" between 5/8 and 7/8 of a mile */
uiDistance = '¾';
unit = 'mile';
} else if (distanceFromEndOfStep > 1980) {
/* Display "1/2 mile" between 3/8 and 5/8 of a mile */
uiDistance = '½';
unit = 'mile';
} else if (distanceFromEndOfStep > 660) {
/* Display "1/4 mile" between 1/8 and 3/8 of a mile */
uiDistance = '¼';
unit = 'mile';
} else {
uiDistance = (Math.round(distanceFromEndOfStep / 10)) * 10;
unit = "ft";
}
if (route.getStep(currentStep + 1) != undefined) {
showInstruction('In ' + uiDistance + ' ' + unit + ': ' + route.getStep(currentStep + 1).getDescriptionHtml());
} else {
showInstruction('In ' + uiDistance + ' ' + unit + ': You will reach your destination');
}
}
/**
* Rebuild the progressArray after jumping to a particular vertex
* @param {number} vertexId The vertex number in the vertices array
*/
function constructProgressArray(vertexId) {
progressArray = new Array();
var stepStart = stepToVertex[currentStep];
for (var i = stepToVertex[currentStep]; i <= vertexId + 1; i++) {
progressArray.push(vertices[i]);
}
}
/**
* Calculate the distance in meters from the start of this
* step to the next vertex by building a polyline from the
* intermediate points.
*/
function setProgressDistance() {
var polyline = new GPolyline(progressArray);
progressDistance = polyline.getLength();
}
/**
* Update the progress bar to reflect our position within this step
* @param {number} progress Distance in meters travelled
* since the start of this step
*/
function updateProgressBar(progress) {
progress = (progress < 0 ? 0 : progress);
var stepLength = route.getStep(currentStep).getDistance().meters;
setProgressBarLength(1 - (progress / stepLength));
}
/**
* Size the progress bar
* @param {number} progress Progress expressed as a fraction (0 to 1)
*/
function setProgressBarLength(progress) {
var width = (636 * progress);
if (width < 0) {
width = 0;
}
document.getElementById("progressBar").style.width = width + "px";
}
/**
* Calculate the difference in degrees between two bearings.
* @param {number} a bearing in degrees
* @param {number} b bearing in degrees
* @return {number} The angle between a and b
*/
function getYawDelta(a, b) {
var d = Math.abs(sanitiseYaw(a) - sanitiseYaw(b));
if (d > 180) {
d = 360 - d;
}
return d;
}
/**
* Sometimes after following a link the yaw is > 360
* @param {number} yaw bearing in degrees
* @return {number} yaw as a value between -360 and +360
*/
function sanitiseYaw(yaw) {
if (yaw > 360 || yaw < 360) {
yaw = yaw % 360;
}
return yaw;
}
/**
* Generate a GMarker icon of an direction arrow
* @param {number} bearing Direction arrow should point
* @return {GIcon}
*/
function getArrowIcon(bearing) {
var icon = new GIcon();
icon.image = getArrowUrl(bearing);
icon.iconSize = new GSize(24, 24);
icon.iconAnchor = new GPoint(12, 12);
return icon;
}
/**
* Determine URL of correct direction arrow image to use
* @param {number} bearing Direction arrow should point
* @return {String}
*/
function getArrowUrl(bearing) {
var id = (3 * Math.round(bearing / 3)) % 120;
return "http://maps.google.com/mapfiles/dir_" + id + ".png";
}
/**
* Build the vertices, vertexMap, stepToVertex, and stepMap
* arrays from the vertices of the route polyline.
* @param {GPolyline} path The route polyline to process
*/
function collapseVertices(path) {
vertices = new Array();
vertexMap = new Array(path.getVertexCount());
vertices.push(path.getVertex(0));
vertexMap[0] = 0;
/* Copy vertices from the polyline to the vertices array
* skipping any duplicates. Build the vertexMap as we go along */
for (var i = 1; i < path.getVertexCount(); i++) {
if (! path.getVertex(i).equals(vertices[vertices.length - 1])) {
vertices.push(path.getVertex(i));
}
vertexMap[i] = vertices.length - 1;
}
stepToVertex = new Array(route.getNumSteps());
stepMap = new Array(vertices.length);
for (var i = 0; i < route.getNumSteps(); i++) {
stepToVertex[i] = vertexMap[route.getStep(i).getPolylineIndex()];
}
var step = 0;
for (var i = 0; i < vertices.length; i++) {
if (stepToVertex[step + 1] == i) {
step++;
}
stepMap[i] = step;
}
}
/**
* Because we want to be highlight text directions steps and make them clickable
* we must render them ourselves rather than let GDirections do so as normal.
*/
function renderTextDirections() {
/* Get the addresses to display at the start and end of the directions */
var startAddress = route.getStartGeocode().address;
var endAddress = route.getEndGeocode().address;
/* Write the start address title, marker, and summary */
var html = getDirectionsWaypointHtml(startAddress, "A");
html += getDivHtml("summary", "", route.getSummaryHtml());
/* Build up the textual directions step by step */
for (var n = 0; n < route.getNumSteps(); n++) {
html += '<a onclick="selectStep(' + n + ')">';
html += getDivHtml("step" + n, "dstep", route.getStep(n).getDescriptionHtml());
html += '</a>';
}
/* Write the end address title and marker */
html += getDirectionsWaypointHtml(endAddress, "B");
/* Fill in the div on the page with the generated HTML */
document.getElementById("directions").innerHTML = html;
/* Set the icons used in the address blocks using the appropriate
* technique for the browser to preserve PNG transparency */
setWaypointIcon('A');
setWaypointIcon('B');
}
/**
* Generate the HTML of the header and footer of each set of textual directions
* @param {String} address The address of this endpoint
* @param {String} letter The letter to display in the marker
* (A at the start, B at the end)
* @return {String} the generated HTML
*/
function getDirectionsWaypointHtml(address, letter) {
var content = getDivHtml('letter' + letter, 'letterIcon', "");
content += '<span class="waypointAddress">' + address + '</span>';
return getDivHtml("wayPoint" + letter, "waypoint", content);
}
/**
* Set the icons used in the textual directions address blocks
* using the appropriate technique for the browser to preserve
* PNG transparency
*/
function setWaypointIcon(letter) {
var png = 'http://maps.google.com/intl/en_us/mapfiles/icon_green' + letter + '.png';
/*@cc_on @*/
/*@if (@_jscript_version < 5.7)
document.getElementById('letter' + letter).style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + png + '", sizingMethod="scale");';
return;
/*@end @*/
document.getElementById('letter' + letter).style.backgroundImage = 'url(' + png + ')';
}
/**
* Utility function to wrap content in a div tags
* @param {String} id The id attribute value for this div element
* @param {String} cssClass The class attribute value for this div element
* @param {String} content The HTML content to wrap in this div element
* @return {String} the HTML of the generated div element
*/
function getDivHtml(id, cssClass, content) {
var div = "<div";
if (id != "") {
div += ' id="' + id + '"';
}
if (cssClass != "") {
div += ' class="' + cssClass + '"';
}
div += '>' + content + '</div>';
return div;
}
/**
* Handle a click on one of the text directions steps.
* @param {number} i The index of the step that was clicked on
*/
function selectStep(i) {
var vertex = vertexMap[route.getStep(i).getPolylineIndex()];
stopDriving();
jumpToVertex(vertex);
}
/**
* Highlight with a blue background one of the text directions steps.
* @param {number} i The index of the step to highlight
*/
function highlightStep(i) {
/* Remove highlighting from the currently highlighted step */
if (selectedStep != null) {
document.getElementById("step" + selectedStep).style.backgroundColor = "white";
}
/* Highlight the indicated step as requested */
document.getElementById("step" + i).style.backgroundColor = "#eeeeff";
selectedStep = i;
}
/**
* Update the UI for driving and start following links
*/
function startDriving() {
hideInstruction();
document.getElementById("route").disabled = true;
document.getElementById("stopgo").value = "Stop";
document.getElementById("stopgo").setAttribute('onclick', 'stopDriving()');
document.getElementById("stopgo").onclick = function() { stopDriving(); }
driving = true;
advance();
}
/**
* Stop following links and update the UI
*/
function stopDriving() {
driving = false;
if (advanceTimer != null) {
clearTimeout(advanceTimer);
advanceTimer = null;
}
document.getElementById("route").disabled = false;
document.getElementById("stopgo").disabled = false;
document.getElementById("stopgo").value = "Drive";
document.getElementById("stopgo").setAttribute('onclick', 'startDriving()');
document.getElementById("stopgo").onclick = function() { startDriving(); }
showInstruction('Press <b>Drive</b> to follow your route');
}
/**
* Change the speed at which driving progresses by adjusting the delay
* between following links from one panorama to the next
*/
function setSpeed() {
advanceDelay = document.getElementById('speed').selectedIndex;
}
/**
* Display a status message that fills the Flash viewer
* @param {String} message The message to display
*/
function showStatus(message) {
hideInstruction();
document.getElementById("status").innerHTML = message;
document.getElementById("status").style.display = "block";
document.getElementById("streetview").style.display = "none";
}
/**
* Hide the currently displayed status message
*/
function hideStatus() {
document.getElementById("status").style.display = "none";
document.getElementById("streetview").style.display = "block";
}
/**
* Display an instructional message at the bottom of the Flash viewer
* @param {String} message The message to display
*/
function showInstruction(message) {
document.getElementById("instruction").innerHTML = message;
document.getElementById("instruction").style.display = "block";
}
/**
* Hide the currently displayed instructional message
*/
function hideInstruction() {
document.getElementById("instruction").style.display = "none";
}
/* Following functions based on those provided at:
* http://www.movable-type.co.uk/scripts/latlong.html
* Copyright 2002-2008 Chris Veness
*/
/**
* Calculate the bearing in degrees between two points
* @param {number} origin GLatLng of current location
* @param {number} destination GLatLng of destination
* @return {number}
*/
function getBearing(origin, destination) {
if (origin.equals(destination)) {
return null;
}
var lat1 = origin.lat().toRad();
var lat2 = destination.lat().toRad();
var dLon = (destination.lng()-origin.lng()).toRad();
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
return Math.atan2(y, x).toBrng();
}
/**
* Convert an angle in degrees to radians
*/
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
/**
* Convert an angle in radians to degrees (signed)
*/
Number.prototype.toDeg = function() {
return this * 180 / Math.PI;
}
/**
* Convert radians to degrees (as bearing: 0...360)
*/
Number.prototype.toBrng = function() {
return (this.toDeg()+360) % 360;
}
</script>
<style>
body {
position: relative;
}
#content {
width: 640px;
padding: 8px;
background-color: #eeeeff;
border: 1px solid #000066;
}
#svPanel {
width: 638px;
height: 319px;
position: relative;
}
#status {
position: absolute;
top: 120px;
left: 0px;
width: 638px;
text-align: center;
font: 32pt sans-serif;
color: #666666;
background-color: white;
}
#instruction {
position: absolute;
top: 295px;
left: 0px;
width: 638px;
text-align: center;
font: 16pt sans-serif;
color: #eeeeee;
display: none;
}
#svPanel, #directions, #map {
border: 1px solid black;
background-color: white;
}
#streetview {
position: absolute;
top: 0px;
left: 0px;
width: 638px;
height: 319px;
}
#progressBorder {
position: relative;
width: 638px;
height: 10px;
margin: 2px 0px 2px 0px;
border: 1px solid #000066;
background-color: white;
overflow: hidden;
}
#progressBar {
position: absolute;
background-color: #000066;
width: 636px;
height: 8px;
top: 1px;
right:1px;
}
#map {
width: 317px;
height: 317px;
margin-right: 1px;
overflow: hidden;
}
#directions {
width: 317px;
height: 400px;
margin-left: 1px;
position: relative;
overflow: auto;
}
.waypoint {
position: relative;
background-color: #eeeeee;
border: 1px solid #666666;
padding: 6px;
margin: 4px;
font: 10pt sans-serif;
}
.letterIcon {
width: 24px;
height: 38px;
background-image: none;
}
.waypointAddress {
position: absolute;
top: 17px;
left: 32px;
}
#summary {
padding: 4px;
font: 10pt sans-serif;
}
.dstep {
border-top: 1px solid #666666;
padding: 4px;
padding-left: 8px;
font: 10pt sans-serif;
margin-left: 4px;
margin-right: 4px;
cursor: pointer;
background-color: white;
}
.label {
width: 52px;
text-align: right;
font: 12pt sans-serif;
float: left;
position: relative;
top: 4px;
margin-right: 5px;
}
.input {
float: left;
width: 252px;
text-align: left;
}
.controls {
clear: both;
padding: 4px;
}
#speed {
float: left;
}
#buttons {
float: right;
}
table {
border-collapse: collapse;
}
td {
vertical-align: top;
}
</style>
</head>
<body onload="load();" onunload="GUnload();">
<div id="content">
<table cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
<div id="svPanel">
<div id="streetview" style="width: 638px; height: 319px;"></div>
<div id="status">Enter your start and end addresses and click <b>Route</b></div>
<div id="instruction"></div>
</div>
<div id="progressBorder">
<div id="progressBar"></div>
</div>
</td>
</tr>
<tr>
<td>
<div id="map"></div>
<div class="controls">
<div class="label">From</div>
<div class="input"><input id="from" size="30" value="stanyan st, san francisco"/></div>
</div>
<div class="controls">
<div class="label">To</div>
<div class="input"><input id="to" size="30" value="twin peaks blvd, san francisco"/></div>
</div>
<div class="controls">
<div class="label">Speed</div>
<div id="actions">
<select id="speed" onchange="setSpeed()">
<option value="0">Fast</option>
<option value="1" SELECTED>Medium</option>
<option value="2">Slow</option>
</select>
<div id="buttons">
<input type="button" value="Route" id="route" onclick="generateRoute()" />
<input type="button" value="Drive" id="stopgo" onclick="startDriving()" disabled />
</div>
</div>
</div>
</div>
</td>
<td>
<div id="directions"></div>
</td>
</tr>
</table>
</div>
</body>
</html>
No comments:
Post a Comment