Google Map

The WebView is created in activity_main.xml. See the WebView tutorial, Debugging Web Apps, and Google Maps Android API v2.

The WebView has two client objects. The WebViewClient has a method that is called when the WebView has finished loading its web page. The WebChromeClient has method that is called whenever the web page calls the JavaScript alert function.

When the page is loaded, the onPageFinished method of the WebViewClient calls the JavaScript function mapFunction, which fills the div element with a Google Map.

If we forget to set the height of the body and html elements, the div will have a height of zero. I wonder why we didn’t have to set the widths, too.

Source code in Map.zip

  1. MainActivity.java
  2. activity_main.xml contains a WebView.
  3. map.html in the assets folder.
  4. AndroidManifest.xml: a WebView requires uses-permission INTERNET.

Create the project

Create the assets folder

The map.html to which the URL refers must be in the assets folder in the Android Studio project view. Select the app folder at the top of the project view. Pull down
File → New… → Folder → Assets Folder
Finish

Remember to add the uses-permission to AndroidManifest.xml.

Create the map.html file

Select the assets folder in the left pane of Android Studio.
File → New… → File
Enter a new filename: map.html
OK

Things to try

  1. Uncomment the JavaScript in map.html that calls alert. This will call the onJsAlert method of the WebChromeClient.

  2. Uncomment the JavaScript in map.html that creates the Marker. Why can’t we get the marker’s title to appear?

    Point your desktop browser at map2.html. It’s exactly the same as the map.html in the project, except that no Android app is necessary to see the map. (As soon as map2.html is loaded into your desktop browser, the JavaScript mapFunction is called automatically by the onLoad attribute of the BODY element in map2.html.) When you hover over the marker (i.e., hold your mouse there without clicking), the marker’s title will appear after a few seconds. And in fact the documentation for title defines the title as “rollover text”. Maybe the title doesn’t appear in an Android app simply because there is no way to do a rollover on a mobile app.

    An alternative is to give the marker a label instead of a title. After the first SCRIPT element, include this one too:

    <SCRIPT SRC = "http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.0.1/src/markerwithlabel.js">
    </SCRIPT>
    
    Create the marker like this:
    	var style =
                      'color: white;'
                    + 'text-shadow:'
                    + '-1px -1px 3px black,'
                    + '1px -1px 3px black,'
                    + '-1px 1px 3px black,'
                    + '1px 1px 3px black;';
    
            var labelContent = '<SPAN STYLE = "' + style + '">Woolly Building</SPAN>';
    
            options = {
                    position: position,   //We created this variable earlier.
                    map: map,             //We created this variable earlier.
                    icon: 'http://maps.google.com/mapfiles/ms/micons/red-dot.png',
                    labelContent: labelContent,
                    labelAnchor: new google.maps.Point(22, 0),
                    labelClass: 'labels', //the CSS class for the label
                    labelStyle: {opacity: 0.75}
            };
    
            var marker = new MarkerWithLabel(options);
    

  3. Uncomment the JavaScript in map.html that creates the InfoWindow.

  4. In the mapFunction in map.html, change HYBRID to ROADMAP, SATELLITE, or TERRAIN.

  5. Add the traffic layer to the map. Make sure the map type is ROADMAP or HYBRID. Decrease the zoom level to 12 in the onPageFinished method of the WebViewClient. Then add the following statements to the mapFunction in map.html, immediately after the statement that creates the map variable.
            var trafficLayer = new google.maps.TrafficLayer();
            trafficLayer.setMap(map);
    
    What about the bicycling layer?
  6. Map tiles

    A Google map is made of tiles. Each tile is a separate image file, usually of 256 × 256 pixels. Each tile has a URL. The URL of the following tile at zoom level 0 is http://mt1.google.com/vt/x=0&y=0&z=0

    At zoom level 1, the map is 2 tiles wide and 2 tiles high.


    mt1.google.com/vt/x=0&y=0&z=1

    mt1.google.com/vt/x=1&y=0&z=1

    mt1.google.com/vt/x=0&y=1&z=1

    mt1.google.com/vt/x=1&y=1&z=1

    The Moon

    The URLs of the following tiles begin with http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/ (for Clementine). For example, the URL of the first tile is http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/0/0/0.jpg.

    At zoom level 0, the entire map is 1 tile wide and 1 tile high.

    0/0/0.jpg the corresponding tile for the Earth

    At zoom level 1, the map is 2 tiles wide and 2 tiles high.

    1/0/1.jpg 1/1/1.jpg
    1/0/0.jpg 1/1/0.jpg

    At zoom level 2, the map is 4 tiles wide and 4 tiles high.

    2/0/3.jpg 2/1/3.jpg 2/2/3.jpg 2/3/3.jpg
    2/0/2.jpg 2/1/2.jpg 2/2/2.jpg 2/3/2.jpg
    2/0/1.jpg 2/1/1.jpg 2/2/1.jpg 2/3/1.jpg
    2/0/0.jpg 2/1/0.jpg 2/2/0.jpg 2/3/0.jpg

    To see Tycho, the most conspicuous crater on the Moon, change the contents of the map.html file to the following.

    <!DOCTYPE html>
    <html style = "height: 100%;">
    <head>
    <meta charset = "utf-8">
    
    <script type = "text/javascript" src = "http://maps.google.com/maps/api/js?sensor=false">
    </script>
    
    <script type = "text/javascript">
    
    function MoonMapType() {}	//constructor
    MoonMapType.prototype.name = "Moon";
    MoonMapType.prototype.alt = "Clementine Moon Map";
    MoonMapType.prototype.minZoom = 0;
    MoonMapType.prototype.maxZoom = 9;
    MoonMapType.prototype.radius = 1738000;	//of Moon in meters
    MoonMapType.prototype.tileSize = new google.maps.Size(256, 256);
    
    MoonMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
    	//The map is n by n tiles, where n == 2 ** zoom.
    	var n = 1 << zoom;
    
    	//Can't go above the north pole or below the south pole,
    	var y = coord.y;
    	if (y < 0 || y >= n) {
    		return null;
    	}
    
    	//but can go round and round forever in the east/west direction.
    	var x = coord.x % n;
    	if (x < 0) {
    		x += n;
    	}
    
    	var img = ownerDocument.createElement("img");
    	img.src = "http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1"
    		+ "/clem_bw/" + zoom + "/" + x + "/" + (n - y - 1) + ".jpg";
    	return img;
    };
    
    //This function is called by the onPageFinished method of the
    //WebViewClient.
    
    function mapFunction(latitude, longitude, zoom) {
    	var options = {
    		center: new google.maps.LatLng(latitude, longitude),
    		mapTypeControl: false,
    		scaleControl: true,
    		zoom: zoom
    	};
    
    	var map = new google.maps.Map(document.getElementById("map"),
    		options);
    	map.mapTypes.set("moon", new MoonMapType());	//add to map's registry
    	map.setMapTypeId("moon");
    }
    </script>
    </head>
    
    <body style = "height: 100%; margin: 0px; padding: 0px;">
    <div id = "map" style = "width: 100%; height: 100%;">
    </div>
    </body>
    </html>
    

    Change the latitude to -43.0f, the longitude to -11.2f, and the zoom level to 4.

    The Sky

    To see Orion the Hunter, the most conspicuous constellation in the sky, change img.src to

    	img.src = "http://mw1.google.com/mw-planetary/sky/skytiles_v1/"
    		+ coord.x + "_"
    		+ coord.y + '_'
    		+ zoom + '.jpg';
    
    Change the maximum zoom level to 13 and the radius to I don’t know what. Does the sky even have a radius? Change the latitude to -1.0f, the longitude to 6.44f * 360.0f / 24.0f, and the zoom level to 5.

    Betelgeuse is to the upper left, Rigel to lower right. Latitude and longitude are called declination and right ascension when you’re up in the sky. The right ascension of Orion should be about 5½ hours. I don’t understand why I had to ask for 6.44.

    Mars

    The URL of each tile begins with http://mw1.google.com/mw-planetary/mars/visible/. For example, the URL of the first tile is http://mw1.google.com/mw-planetary/mars/visible/t.jpg. The qrst names of the tiles make the JavaScript more complicated. At zoom level 0, the map is 1 tile wide and 1 tile high.

    t.jpg

    At zoom level 1, the map is 2 tiles wide and 2 tiles high.

    tq.jpg tr.jpg
    tt.jpg ts.jpg

    At zoom level 2, the map is 4 tiles wide and 4 tiles high.

    tqq.jpg tqr.jpg trq.jpg trr.jpg
    tqt.jpg tqs.jpg trt.jpg trs.jpg
    ttq.jpg ttr.jpg tsq.jpg tsr.jpg
    ttt.jpg tts.jpg tst.jpg tss.jpg

    To see Olympus Mons, the most conspicuous volcano on Mars, change the contents of the map.html file to the following. Change the latitude to 18.4f, the longitude to 226.75f, and the zoom to 7.

    <!DOCTYPE html>
    <html style = "height: 100%;">
    <head>
    <meta charset = "utf-8">
    
    <script type = "text/javascript" src = "http://maps.google.com/maps/api/js?sensor=false">
    </script>
    
    <script type = "text/javascript">
    
    function MarsMapType() {}	//constructor
    MarsMapType.prototype.name = "Mars";
    MarsMapType.prototype.alt = "JPL Mars Map";
    MarsMapType.prototype.minZoom = 0;
    MarsMapType.prototype.maxZoom = 9;
    MarsMapType.prototype.radius = 3396200;	//of Mars in meters
    MarsMapType.prototype.tileSize = new google.maps.Size(256, 256);
    
    MarsMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
    	//The map is n by n tiles, where n == 2 ** zoom.
            var n = 1 << zoom;
    
    	//Can't go above the north pole or below the south pole,
            var y = coord.y;
            if (y < 0 || y >= n) {
                    return null;
            }
    
    	//but can go round and round forever in the east/west direction.
            var x = coord.x % n;
            if (x < 0) {
                    x += n;
            }
    
    	var quadrant = new Array(
    		new Array("q", "r"),
    		new Array("t", "s")
    	);
    
    	var img = ownerDocument.createElement("img");
    	img.src = "http://mw1.google.com/mw-planetary/mars/visible/t";
    
    	var x = coord.x;
    	var y = coord.y;
    
    	for (var n = 1 << zoom - 1; n > 0; n >>= 1) {
    		img.src += quadrant[Math.floor(y / n)][Math.floor(x / n)];
    		x %= n;
    		y %= n;
    	}
    
    	img.src += ".jpg";
    	return img;
    };
    
    //This function is called by the webViewDidFinishLoad: method of the
    //UIWebViewDelegate.
    
    function mapFunction(latitude, longitude, zoom) {
    	var options = {
    		center: new google.maps.LatLng(latitude, longitude),
    		mapTypeControl: false,
    		scaleControl: true,
    		zoom: zoom
    	};
    
    	var map = new google.maps.Map(document.getElementById("map"), options);
    	map.mapTypes.set("mars", new MarsMapType());	//add to map's registry
    	map.setMapTypeId("mars");
    }
    </script>
    </head>
    
    <body style = "height: 100%; margin: 0px; padding: 0px;">
    <div id = "map" style = "width: 100%; height: 100%;">
    </div>
    </body>
    </html>