Chapter 4. Geolocation and Mapping APIs
Chapter 3 introduced us to using the W3C Geolocation API to collect location information from the user’s browser with JavaScript code. Although that is the whole point of this book, it is not extremely useful to collect geolocation information unless you, the developer, are going to do something with it. One effective application for collecting a user’s location is to place that point on a map. Let’s face it, mapping a location (or multiple locations) is a common thing to do in the world of GIS.
There are many solutions available for web-mapping applications—Google Maps JavaScript API V3, Bing Maps AJAX Control, Version 7.0, Esri ArcGIS JavaScript API 2.2, Yahoo Maps AJAX API, and OpenStreetMap API v0.6 to name a few (more than a few actually). Most of the APIs available to do web mapping are very similar in nature. Because of this, I have decided to focus on just a couple of the APIs available, and leave it up to you to pick the API that best suits your needs.
After giving you a taste of these APIs for use in your applications, we can then explore what to do with the information you have collected so that it can be referenced by other applications or replotted in the future. This is as important as being able to map the geolocations being collected, since most GIS applications are going to be interested in more than a single user’s point information. To this end, we will look at different ways to save our geolocation information so that it can be consumed by these other applications.
A Google Maps Example
The Google Maps JavaScript API lets you embed Google Maps in your own web pages. Version 3 of this API is especially designed to be faster and more applicable to mobile devices, as well as traditional desktop browser applications.[9] With this API, it is easy for a developer to embed a map that functions just like the http://maps.google.com/ web page, and to customize its look and functionality to suit the needs of the application being built. It is a very popular API for building web applications, currently being used in over 150,000 websites.[10]
The Google Maps API, Briefly
For the purposes of this book, I have included all of the code for the Google Maps application into a single HTML file to make it easier to read. If I were to create an actual application, I would break the Cascading Style Sheet (CSS) rules and JavaScript into their own files (perhaps multiple files should the application be more complex) as a better programming practice.
Take a look at the code in Example 4-1, which contains everything needed to create a simple Google Map application.
<!DOCTYPE html> <html lang="en"> <head> <title>A Simple Google Map</title> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> <meta charset="utf-8"/> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map { height: 100% } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> <script type="text/javascript"> var map; /* This is called once the page has loaded */ function InitMap() { /* Set all of the options for the map */ var options = { zoom: 4, center: new google.maps.LatLng(38.6201, -90.2003), mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, position: google.maps.ControlPosition.BOTTOM_CENTER }, panControl: true, panControlOptions: { position: google.maps.ControlPosition.TOP_RIGHT }, zoomControl: true, zoomControlOptions: { style: google.maps.ZoomControlStyle.LARGE, position: google.maps.ControlPosition.LEFT_CENTER }, scaleControl: true, scaleControlOptions: { position: google.maps.ControlPosition.BOTTOM_LEFT }, streetViewControl: true, streetViewControlOptions: { position: google.maps.ControlPosition.LEFT_TOP } }; /* Create a new Map for the application */ map = new google.maps.Map(document.getElementById('map'), options); } /* A utility object for simple event handlilng */ var Utils = { }; Utils.addEvent = (function() { return function addEvent(eventObj, event, eventHandler) { if (eventObj.addEventListener) { eventObj.addEventListener(event, eventHandler, false); } else if (eventObj.attachEvent) { event = 'on' + event; eventObj.attachEvent(event, eventHandler); } else { eventObj['on' + event] = function() { eventHandler() }; } }; }()); Utils.removeEvent = (function() { return function removeEvent(event) { if (event.preventDefault) { event.preventDefault(); event.stopProgagation(); } else { event.returnValue = false; event.cancelBubble = true; } }; }()); Utils.addEvent(window, 'load', InitMap); </script> </head> <body> <div id="map"></div> </body> </html>
The code in Example 4-1 produces a map like that shown in Figure 4-1. I will step through this code in more detail in a moment, but there are several things that should be noted right away:
The application is written in HTML5.
The Google Maps JavaScript API is included in the application by calling it from Google’s site.
There are a couple of utility JavaScript functions that aid in cross-browser compliant event handling.
A Google Map is created by specifying the container, a
<div>
element, to hold the map, and a set of options that allow the developer to customize the look of the map’s controls.
Specifying a DOCTYPE
in the
application guarantees that browsers will render in standards-compliant mode, making it more
cross-browser friendly. I chose HTML5 as it will soon be the industry
standard, but any true DOCTYPE
may be
used.
The Google Maps JavaScript API is included in the application with the following line:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
This makes the latest version of the API available for the
application. The parameter sensor
is
set to false to indicate that the map is not using
a sensor to determine the user’s location.
Note
When geolocation is added to Example 4-1,
the value of the sensor
parameter
will be changed to true so that the API knows
that a location will be gathered by a “sensor,” like the GPS locator
in a phone.
Lastly, before looking at the Google Map JavaScript API specific
code, the <meta>
element
specifying a viewport
is recognized
only by the iPhone right now, and tells it to set the application to
full-screen and not allow the user to resize the application. Other
smartphones may take advantage of this element in the future.
The Utils
variable is nothing
more than an object that holds cross-browser event handling functions
that are used to create a more flexible application. By using the
Utils.addEvent()
method, this code
can be plugged into an existing application and the developer does not
have to worry about overwriting an existing onload
function that may already be present.
If a JavaScript library like jQuery or Dojo is being used in the
application, then it will most likely have built-in methods that also
take the hassle out of cross-browser event handling.
A Map is created by instantiating a new
google.maps.Map
object and specifying
the element that will contain the map. The element is referenced using
the document.getElementById()
DOM
method. The Map object also takes an
options object that controls everything else about
the map.
Map Options
By default, the Google Maps JavaScript API provides controls
that enable basic map navigation and type switching. In addition, all
devices have keyboard handling on by default. The default controls can
be disabled using the Map’s disableDefaultUI
property, and individual
controls can be manipulated using their corresponding
properties.
In Example 4-1, the following controls were configured for the map:
- zoom
The default zoom level was set to
4
in the example. The zoom property can range from0
to21+
, where0
is a view of the whole world, and21
is down to individual buildings.- center
Defines the center of the map by a pair of coordinates.
- mapType
The Google Maps JavaScript API makes the following map types available:
ROADMAP
,SATELLITE
,HYBRID
, andTERRAIN
.- mapTypeControl, panControl, zoomControl, scaleControl, streetViewControl
These controls are toggled on or off with values of true and false. In addition, each has specific configuration options along with a position property.
For more information on options available for the Map object, visit the Google Maps JavaScript API V3 Developer’s Guide and API Reference .
Adding Geolocation to Google Maps
As we saw in Chapter 3, there are
three main components needed in order to add geolocation to the Google
map in Example 4-1: a call to getCurrentPosition()
, a
successCallback function to do something with the
position when we get it, and an errorCallback
function in case something goes wrong. We will create a function called
getLocation()
to handle checking for
the navigator.geolocation
object and
for making our initial call for a location. This function will take
advantage of a global variable called browserSupport
that will eventually let our
errorCallback function know if the error is from
the API or a lack of browser support. I am doing this so that all of our
error handling is in one function instead of having error alerts spread
throughout the code. This way, if I choose to do something more robust
with my error handling other than simply alert the user to a problem,
all of the error code is in one place.
Example 4-2 illustrates this new functionality implemented into our Google Map example. Note that changes and additions to the code are highlighted in bold for easier identification.
<!DOCTYPE html> <html lang="en"> <head> <title>Adding Geolocation to a Google Map</title> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> <meta charset="utf-8"/> <style type="text/css"> html { height: 100% } – body { height: 100%; margin: 0; padding: 0 } #map { height: 100% } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script> <script type="text/javascript"> var map; var browserSupport = false; var attempts = 0; /* This is called once the page has loaded */ function InitMap() { /* Set all of the options for the map */ var options = { zoom: 4, center: new google.maps.LatLng(38.6201, -90.2003), mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, position: google.maps.ControlPosition.BOTTOM_CENTER }, panControl: true, panControlOptions: { position: google.maps.ControlPosition.TOP_RIGHT }, zoomControl: true, zoomControlOptions: { style: google.maps.ZoomControlStyle.LARGE, position: google.maps.ControlPosition.LEFT_CENTER }, scaleControl: true, scaleControlOptions: { position: google.maps.ControlPosition.BOTTOM_LEFT }, streetViewControl: true, streetViewControlOptions: { position: google.maps.ControlPosition.LEFT_TOP } }; /* Create a new Map for the application */ map = new google.maps.Map(document.getElementById('map'), options); /* Add Geolocation */ getLocation(); } /* * If the W3C Geolocation object is available then get the current * location, otherwise report the problem */ function getLocation() { /* Check if the browser supports the W3C Geolocation API */ if (navigator.geolocation) { browserSupport = true; navigator.geolocation.getCurrentPosition(plotLocation, reportProblem, { timeout: 45000 }); } else reportProblem(); } /* Plot the location on the map and zoom to it */ function plotLocation(position) { attempts = 0; var point = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); var marker = new google.maps.Marker({ position: point }); marker.setMap(map); map.setCenter(point); map.setZoom(15); } /* Report any errors using this function */ function reportProblem(e) { /* Is this a support issue or an API issue? */ if (browserSupport) { switch (e.code) { case e.PERMISSION_DENIED: alert('You have denied access to your position. You will ' + 'not get the most out of the application now.'); break; case e.POSITION_UNAVAILABLE: alert('There was a problem getting your position.'); break; case e.TIMEOUT: /* Three changes to get the location before a true timeout */ if (++attempts < 3) { navigator.geolocation.getCurrentPosition(plotLocation, reportProblem); } else alert('The application has timed out attempting to get ' + 'your location.'); break; default: alert('There was a horrible Geolocation error that has ' + 'not been defined.'); } } else alert('Geolocation is not supported by your browser.'); } /* A utility object for simple event handlilng */ var Utils = { }; Utils.addEvent = (function() { return function addEvent(eventObj, event, eventHandler) { if (eventObj.addEventListener) { eventObj.addEventListener(event, eventHandler, false); } else if (eventObj.attachEvent) { event = 'on' + event; eventObj.attachEvent(event, eventHandler); } else { eventObj['on' + event] = function() { eventHandler() }; } }; }()); Utils.removeEvent = (function() { return function removeEvent(event) { if (event.preventDefault) { event.preventDefault(); event.stopProgagation(); } else { event.returnValue = false; event.cancelBubble = true; } }; }()); Utils.addEvent(window, 'load', InitMap); </script> </head> <body> <div id="map"></div> </body> </html>
The first thing to note in this example is that sensor=true
when we make the call to the
Google JavaScript API, since we are using geolocation in this example.
The getLocation()
function is called
right after our map object is instantiated.
Next, we define our two callback functions: plotLocation()
and reportProblem()
. plotLocation()
will be passed a
Position object that will contain all of the
geolocation information, while reportProblem()
will be passed a
PositionError object that will contain an error
code and message.
The plotLocation()
function
creates a LatLng object based on the passed
latitude and longitude of the Position object, and
from that LatLng object a
Marker object is created. The
Marker is placed on the map, and then the map is
centered and zoomed to the current geolocation.
The reportProblem()
function,
meanwhile, simply alerts the user to the specific error the application
has, based either on the browserSupport
variable, or the
PositionError code that is passed to the function.
If the error is a timeout, the application will make three attempts at
getting the current position of the user before giving up and reporting
a problem.
Adding Geolocation for Other Browsers
The code in Example 4-2 works for browsers that support the W3C Geolocation API, but what about browsers that do not? Remember back in Chapter 1 when I discussed other browser solutions, and in particular geo-location-javascript? There are severe limitations to the geolocation functionality that this JavaScript library gives, but it is one solution that attempts cross-browser compatibility. Our Google Maps example is simple enough that we can use this library and not worry too much about the lack of functionality. Example 4-3 shows implementing a cross-browser geolocation solution using the geo-location-javascript library. Again, changes and additions to the code are highlighted in bold for easier identification.
<!DOCTYPE html> <html lang="en"> <head> <title>Adding Geolocation for Other Browsers to a Google Map</title> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> <meta charset="utf-8"/> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map { height: 100% } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script> <script type="text/javascript" src="gears_init.js"></script> <script type="text/javascript" src="geo.js"></script> <script type="text/javascript"> var map; var browserSupport = false; /* This is called once the page has loaded */ function InitMap() { /* Set all of the options for the map */ var options = { zoom: 4, center: new google.maps.LatLng(38.6201, -90.2003), mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: true, mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, position: google.maps.ControlPosition.BOTTOM_CENTER }, panControl: true, panControlOptions: { position: google.maps.ControlPosition.TOP_RIGHT }, zoomControl: true, zoomControlOptions: { style: google.maps.ZoomControlStyle.LARGE, position: google.maps.ControlPosition.LEFT_CENTER }, scaleControl: true, scaleControlOptions: { position: google.maps.ControlPosition.BOTTOM_LEFT }, streetViewControl: true, streetViewControlOptions: { position: google.maps.ControlPosition.LEFT_TOP } }; /* Create a new Map for the application */ map = new google.maps.Map(document.getElementById('map'), options); /* Add Geolocation */ getLocation(); } /* * The browser will now use whatever geolocation API is available to * it; hopefully it will be the W3C Geolocation object that is used to * get the current location. If there is no geolocation support at all, * then report the problem. */ function getLocation() { /* Check if the browser supports any geolocation API */ if (geo_position_js.init()) { browserSupport = true; geo_position_js.getCurrentPosition(plotLocation, reportProblem); } else reportProblem(); } /* Plot the location on the map and zoom to it */ function plotLocation(position) { var point = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); var marker = new google.maps.Marker({ position: point }); marker.setMap(map); map.setCenter(point); map.setZoom(15); } /* Report any errors using this function */ function reportProblem() { /* Is this a support issue or an API issue? */ if (browserSupport) alert('Could not locate your device.'); else alert('Geolocation is not supported by your browser.'); } /* A utility object for simple event handlilng */ var Utils = { }; Utils.addEvent = (function() { return function addEvent(eventObj, event, eventHandler) { if (eventObj.addEventListener) { eventObj.addEventListener(event, eventHandler, false); } else if (eventObj.attachEvent) { event = 'on' + event; eventObj.attachEvent(event, eventHandler); } else { eventObj['on' + event] = function() { eventHandler() }; } }; }()); Utils.removeEvent = (function() { return function removeEvent(event) { if (event.preventDefault) { event.preventDefault(); event.stopProgagation(); } else { event.returnValue = false; event.cancelBubble = true; } }; }()); Utils.addEvent(window, 'load', InitMap); </script> </head> <body> <div id="map"></div> </body> </html>
Calls to gears_init.js
and
geo.js
load the libraries we are
using for geolocation in this example. All of the
Map functionality remains the same as in Example 4-2.
Instead of checking for navigation.geolocation
, in this example, the
geo-location-javascript API is initialized and will return whether or
not the browser supports any of the geolocation APIs that
geo-location-javascript does. A simpler call to getCurrentPosition()
is made, without the
timeout set, but otherwise the getLocation()
function is very similar to
this same function in Example 4-2.
Nothing changed between the two geolocation examples in the
plotLocation()
function, however
there are big changes in the reportProblem()
function. First, note that
there is no PositionError object passed to the
function—geo-location-javascript does not have this functionality. The
error handling is very simplistic, and this is one of the biggest
drawbacks of this API. As I said earlier, this works adequately
because we are using a simple example. However, should the geolocation
needs be more complex, a lot of additional coding will be needed to
get the application working correctly.
An ArcGIS JavaScript API Example
Esri’s ArcGIS API for JavaScript allows the developer to take advantage of all the mapping, editing, geocoding, and geoprocessing services that Esri offers. With this API, a developer is able to embed a map that functions, like those on http://www.arcgis.com/, and to customize its look and functionality to satisfy the needs of the application being built. The JavaScript API is hosted on ArcGIS Online and is freely available for use. Many sites use this API for their GIS needs, especially when their desktop and server GIS needs are met using Esri enterprise software. At the time of this writing, the current version of the API is 2.2.
The ArcGIS JavaScript API, Briefly
Again, for this book, I have included all of the code for the ArcGIS JavaScript Map application into a single HTML file to make it easier to read. In a production application, I would break the CSS and JavaScript into their own files.
Take a look at the code in Example 4-4, which contains everything needed to create a simple ArcGIS JavaScript Map application.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=7"/> <meta http-equiv="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/> <title>A Simple Esri ArcGIS Map</title> <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/ \ 2.2/js/dojo/dijit/themes/claro/claro.css"/> <style type="text/css"> html, body { height: 100%; margin: 0; padding: 0; width: 100%; } #map { height: 100%; width: 100%; } </style> <script type="text/javascript"> var djConfig = { parseOnLoad: true }; </script> <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.2"> </script> <script type="text/javascript"> dojo.require('esri.map'); var map; var initialExtent = { xmin: -119.3324, ymin: 26.3156, xmax: -72.3568, ymax: 55.0558, /* * Web Mercator (102113), or WGS 84 (4326) - these are the * only two that support continuous pan across the date line */ spatialReference: { wkid: 4326 } }; var startExtent; var basemap; function initApp() { var startExtent = new esri.geometry.Extent(initialExtent); map = new esri.Map('map', { extent: startExtent, wrapAround180: true }); basemap = new esri.layers.ArcGISTiledMapServiceLayer( 'http://server.arcgisonline.com/ArcGIS/rest/services/' + 'ESRI_StreetMap_World_2D/MapServer'); map.addLayer(basemap); } dojo.addOnLoad(initApp); </script> </head> <body class="claro"> <div id="map"></div> </body> </html>
The code in Example 4-4 produces a map like that shown in Figure 4-2. I will step through this code in more detail in a moment, but there are several things that should be noted right away:
The application is written in HTML5.
The Esri ArcGIS API for JavaScript is included in the application by calling it from ArcGIS Online.
The Dojo Toolkit, with its immense functionality, is also included in the API call to ArcGIS Online.
An ArcGIS JavaScript Map is created by specifying the container, a
<div>
element, to hold the map, and a set of inline options that define aspects of the map being created.
As with the Google Map examples, I chose to write this application
in HTML5, as it will soon be the industry standard. Plus this will give
it additional functionality for creating more impressive maps in the
future. Any true DOCTYPE
may, of
course, be used and the application will run fine.
The Esri ArcGIS JavaScript API and Dojo Toolkit are included in the application with the following line:
<script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.2"> </script>
The version of the API you wish to use is specified in the querystring of the API call, in this case the latest version: 2.2.
Once again, I am using a <meta>
element to specify the viewport
, telling it to set the application to
full-screen and not allow the user to resize the application—this is an
iPhone recognizable element. There is a second <meta>
element, however, that is used
for Internet Explorer browsers, telling them to interpret and display
the application as Internet Explorer 7 would. This element should change
as usage of the IE7 browser finally disappears.
A Map is created by instantiating a new
esri.Map
object and specifying the
element that will contain the map. The element is referenced by its
id
value. The
Map object also accepts an “options” object that
controls initial extent and other map values. For example, the wrapAround180
property, which is new to the
2.2 API, tells the map whether or not to continuously pan across the
date line. In all previous versions of the JavaScript API, the map would
not scroll across the date line like other web-mapping applications
do.
For more information on options available for the Map object, or API details in general, visit the ArcGIS API for JavaScript Resource page.
Adding Geolocation to Esri Maps
You probably noticed the similarities between the map applications in Example 4-1 and Example 4-4 or, more specifically, the way in which a map was created with each API. While the applications themselves are fairly similar, the way geolocation is added to both of them is nearly identical. To add W3C Geolocation API code to the ArcGIS JavaScript application, we add the same basic code that we did for the Google Map.
First, we will create a function getLocation()
to handle checking for the
navigator.geolocation
object and for
making the call to getCurrentPosition()
. This function will once
again take advantage of a global variable called browserSupport
that will eventually let our
errorCallBack function know if the error is from
the API or a lack of browser support. Example 4-5 shows the geolocation functionality added
to our Esri ArcGIS Map example. Changes and additions to the original
code are highlighted in bold.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=7"/> <meta http-equiv="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/> <title>Adding Geolocation to an Esri ArcGIS Map</title> <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/ \ 2.2/js/dojo/dijit/themes/claro/claro.css"/> <style type="text/css"> html, body { height: 100%; margin: 0; padding: 0; width: 100%; } #map { height: 100%; width: 100%; } </style> <script type="text/javascript"> var djConfig = { parseOnLoad: true }; </script> <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.2"> </script> <script type="text/javascript"> dojo.require('esri.map'); var map; var initialExtent = { xmin: -119.3324, ymin: 26.3156, xmax: -72.3568, ymax: 55.0558, /* * Web Mercator (102113), or WGS 84 (4326) - these are the * only two that support continuous pan across the date line */ spatialReference: { wkid: 4326 } }; var startExtent; var basemap; var browserSupport = false; var attempts = 0; function initApp() { var startExtent = new esri.geometry.Extent(initialExtent); map = new esri.Map('map', { extent: startExtent, wrapAround180: true }); basemap = new esri.layers.ArcGISTiledMapServiceLayer( 'http://server.arcgisonline.com/ArcGIS/rest/services/' + 'ESRI_StreetMap_World_2D/MapServer'); map.addLayer(basemap); /* Add Geolocation */ dojo.connect(map, 'onLoad', function() { getLocation(); }); } /* * If the W3C Geolocation object is available then get the current * location, otherwise report the problem */ function getLocation() { /* Check if the browser supports the W3C Geolocation API */ if (navigator.geolocation) { browserSupport = true; navigator.geolocation.getCurrentPosition(plotLocation, reportProblem, { timeout: 45000 }); } else reportProblem(); } /* Plot the location on the map and zoom to it */ function plotLocation(position) { attempts = 0; var pointsLayer = new esri.layers.GraphicsLayer(); map.addLayer(pointsLayer); var point = new esri.geometry.Point(position.coords.longitude, position.coords.latitude, new esri.SpatialReference({ wkid: 4326 })); pointsLayer.add( new esri.Graphic( point, new esri.symbol.SimpleMarkerSymbol().setColor( new dojo.Color([255, 0, 0, 0.5])) ) ); map.centerAndZoom(point, 13); } /* Report any errors using this function */ function reportProblem(e) { /* Is this a support issue or an API issue? */ if (browserSupport) { switch (e.code) { case e.PERMISSION_DENIED: alert('You have denied access to your position. You will ' + 'not get the most out of the application now.'); break; case e.POSITION_UNAVAILABLE: alert('There was a problem getting your position.'); break; case e.TIMEOUT: /* Three changes to get the location before a true timeout */ if (++attempts < 3) { navigator.geolocation.getCurrentPosition(plotLocation, reportProblem); } else alert('The application has timed out attempting to get ' + 'your location.'); break; default: alert('There was a horrible Geolocation error that has ' + 'not been defined.'); } } else alert('Geolocation is not supported by your browser.'); } dojo.addOnLoad(initApp); </script> </head> <body class="claro"> <div id="map"></div> </body> </html>
In this example, the call to the getLocation()
function is inside an anonymous
function that will be called on an onLoad
event from the
Map. Next, we define our two callback functions:
plotLocation()
and reportProblem()
. The reportProblem()
function is exactly like its counterpart in Example 4-2, so there is no need to go into it
again. plotLocation()
, however, is much changed
as different APIs handle adding points differently.
The plotLocation()
function
first creates an esri.layers.GraphicsLayer
called pointsLayer
, which is where the new point will
be placed, and adds this layer to the map. It then creates a new
esri.geometry.Point
, point
, with the coordinates passed from the
Position object. Next, it adds a new graphic on the
pointsLayer
layer, at point
, with an esri.symbol.SimpleMarkerSymbol
. Finally, the
map is centered and zoomed to the current geolocation.
Support for Other Browsers
The code in Example 4-5 will provide geolocation support for browsers that implement the W3C Geolocation API. Once again, we need to rewrite our code to utilize the geo-location-javascript library to give us cross-browser support for our geolocation application. Example 4-6 shows an implementation of a cross-browser geolocation application using geo-location-javascript within the Esri ArcGIS JavaScript API. I have again highlighted the changes in the code in bold so they are easier to see.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=7"/> <meta http-equiv="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/> <title>Adding Geolocation for Other Browsers to an Esri Map</title> <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/ \ 2.2/js/dojo/dijit/themes/claro/claro.css"/> <style type="text/css"> html, body { height: 100%; margin: 0; padding: 0; width: 100%; } #map { height: 100%; width: 100%; } </style> <script type="text/javascript"> var djConfig = { parseOnLoad: true }; </script> <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.2"> </script> <script type="text/javascript" src="gears_init.js"></script> <script type="text/javascript" src="geo.js"></script> <script type="text/javascript"> dojo.require('esri.map'); var map; var initialExtent = { xmin: -119.3324, ymin: 26.3156, xmax: -72.3568, ymax: 55.0558, /* * Web Mercator (102113), or WGS 84 (4326) - these are the * only two that support continuous pan across the date line */ spatialReference: { wkid: 4326 } }; var startExtent; var basemap; var browserSupport = false; function initApp() { var startExtent = new esri.geometry.Extent(initialExtent); map = new esri.Map('map', { extent: startExtent, wrapAround180: true }); basemap = new esri.layers.ArcGISTiledMapServiceLayer( 'http://server.arcgisonline.com/ArcGIS/rest/services/' + 'ESRI_StreetMap_World_2D/MapServer'); map.addLayer(basemap); /* Add Geolocation */ dojo.connect(map, 'onLoad', function() { getLocation(); }); } /* * The browser will now use whatever geolocation API is available to * it; hopefully it will be the W3C Geolocation object that is used to * get the current location. If there is no geolocation support at all, * then report the problem. */ function getLocation() { /* Check if the browser supports any geolocation API */ if (geo_position_js.init()) { browserSupport = true; geo_position_js.getCurrentPosition(plotLocation, reportProblem); } else reportProblem(); } /* Plot the location on the map and zoom to it */ function plotLocation(position) { attempts = 0; var pointsLayer = new esri.layers.GraphicsLayer(); map.addLayer(pointsLayer); var point = new esri.geometry.Point(position.coords.longitude, position.coords.latitude, new esri.SpatialReference({ wkid: 4326 })); pointsLayer.add( new esri.Graphic( point, new esri.symbol.SimpleMarkerSymbol().setColor( new dojo.Color([255, 0, 0, 0.5])) ) ); map.centerAndZoom(point, 13); } /* Report any errors using this function */ function reportProblem() { /* Is this a support issue or an API issue? */ if (browserSupport) alert('Could not locate your device.'); else alert('Geolocation is not supported by your browser.'); } dojo.addOnLoad(initApp); </script> </head> <body class="claro"> <div id="map"></div> </body> </html>
Calls to gears_init.js
and
geo.js
load the libraries we are
using for geolocation in this example. The Map
functionality itself remains the same as in Example 4-5.
Instead of checking for navigator.geolocation
, the
geo-location-javascript init()
function is called, which returns whether or not the browser supports
any geolocation API. A simpler call to getCurrentPosition()
is made, without the
timeout set, but otherwise the getLocation()
function is very similar to
this same function in Example 4-5.
Nothing changed between the two Esri geolocation examples in the
plotLocation()
function, but there
are, obviously, big changes in the reportProblem()
function because of the lack
of a PositionError object with
geo-location-javascript. This is what we saw back in Example 4-3.
For more complicated cross-browser geolocation needs, additional and more complex coding will be required to get the job done. Hopefully the geo-location-javascript library will eventually add more functionality to its code base so that it better mirrors the W3C Geolocation API methods and properties. Until that day, it is up to application developers to write this functionality themselves. It is either that, or everyone needs to stop using outdated legacy browsers and phones—but unfortunately I do not see that happening for a few years still.
[9] Google Maps JavaScript API V3. http://code.google.com/apis/maps/documentation/javascript/.
[10] Mapping Success: Google Maps Case Studies. http://maps.google.com/help/maps/casestudies/.
Get HTML5 Geolocation now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.