Google Mapplets開発
社内有志の英語勉強会での発表資料です。Google Mapplets開発の説明です。
Key features
- Google Maps
- Google Maps API
- KML
- My Maps
- Mapplets
- Geocoding
Key features; Google Maps
- A Web application (service).
- The base technology of the following story
Key features; Google Maps API
- JavaScript API
- With this API, you can use Google Maps service on your site.
Key features; Google Maps API; architecture
- sequence diagrams (shows only data transfer)
- assume 'you' as a developer
your web end-user google server server (browser) +-+ | | html ====> +-+ (js1) src="http://www.google.com/maps" +-+ <==== | | js2 +-+ js runs - js1 calls js2's API js2 does - load images <=== images... - draw them on html
Key features; KML
KML: Keyhole Markup Language
Data format definition (XML)
Quote from http://code.google.com/apis/kml/
KML is a file format used to display geographic data in an earth browser, such as Google Earth, Google Maps and Google Maps for mobile.
@fyi Keyhole: a company name (bought by Google)
Key features; My Maps
- A service on Google Maps
- You can create your own maps by GUI
- without programming
- without keyboard
- without brain
Key features; My Maps (cont.)
- 'My Maps' is an graphical editor of KML.
- This is not my original idea.
- see http://as-is.net/blog/archives/001235.html
An example of KML output
Key features; Mapplets
- A service on Google Maps
- A new API of Google Maps
- It is also JavaScript API
- What you need for application development is just creating one XML file (although it would contain JavaScript and partial HTML)
- A Mapplet is a special type of Google Gadget (in the future, I'll talk about it)
Key features; Mapplets; architecture
- sequence diagrams (shows only data transfer)
- assume 'you' as a developer
your web end-user google server server (browser) <=== http://maps.google.co.jp contents(html,images,...) add your mapplet(URL-m) URL-m +-+ | | ====================> XML(Mapplet application) +-+ <=== js,html
Key features; Geocoding
- You can get the location from address
- Geocoding is not a Google specific
- You can try as follows (the below is an incomplete API key)
Google Maps API vs. Mapplets API
If you just want to play with Google Maps programming, use Mapplets API. Because
- it's easier to develop
- it's much easier to deploy
If you want to develop a whole site using Google Maps, use Google Maps API. Because
- you can do everything you want
- you could make a better site than Google Maps site (in contrast, you're on Google Maps site with Mapplets)
2.
Let's play with Mapplets API.
How to start
- get an account of Google Maps (If you're Arieler, you can use gmap@ariel-networks.com)
- add the following two Mapplets
- Mapplet Scratch Pad
- Developer Mapplet
A simple example
Let's map hello, world!
This is from the official tutorial,
<?xml version="1.0" encoding="UTF-8"?> <Module> <ModulePrefs title="Hello World" description="Displays a Hello World message in the left panel" author="ariel" author_email="gmap@ariel-networks.com" height="150"> <Require feature="sharedmap"/> </ModulePrefs> <Content type="html"><![CDATA[ <h2>Hello World!</h2> ]]></Content> </Module>
Paste this code to 'Mapplet Scratch Pad', and press 'Preview' button.
A simple example (cont)
<?xml version="1.0" encoding="UTF-8"?> <Module> <ModulePrefs title="Hello World" description="Displays a Hello World message in the left panel" author="ariel" author_email="gmap@ariel-networks.com" height="150"> <Require feature="sharedmap"/> </ModulePrefs> <Content type="html"><![CDATA[ <script type="text/javascript"> function init() { alert('init called'); } function onClick() { alert('onClick called'); _gel('content').innerHTML = 'innerHTML is our friend'; // _gel() is document.getElementById() } _IG_RegisterOnloadHandler(init); </script> <style type="text/css"> .highlight { color: red } </style> <h2 class="highlight" onclick="onClick()">Hello World!</h2> <div id="content"/> ]]></Content> </Module>
we love prototype.js
Mapplets requires many asynchronous calls.
Personally, I cannot write JavaScript code without prototype.js. prototype.js is huge, so that I use part of it (handling asynchronous calls).
Copy&paste the following code from prototype.js to our Mapplets.
// derived from prototype.js =begin var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var $A = Array.from = function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0, length = iterable.length; i < length; i++) results.push(iterable[i]); return results; } } Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } } // derived from prototype.js =end
We have GEvent.bind, but...
- GEvent.bind
- GEvent.bindDom
It looks good, but it's insufficient.
We need general 'bind()' like prototype.js for other asynchronous calls.
Development process
- Select 'Mapplet Scratch Pad'
- Paste the following skelton code
- Add your code
- Press 'Preview' button
- debug your code
Class based skelton code
<?xml version="1.0" encoding="UTF-8"?> <Module> <ModulePrefs title="Hello World" description="Displays a Hello World message in the left panel" author="ariel" author_email="gmap@ariel-networks.com" height="150"> <Require feature="sharedmap"/> </ModulePrefs> <Content type="html"><![CDATA[ <script type="text/javascript"> // derived from prototype.js =begin var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var $A = Array.from = function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0, length = iterable.length; i < length; i++) results.push(iterable[i]); return results; } } Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } } // derived from prototype.js =end var MyMapplet = Class.create(); MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); // ADD YOUR CODE(initialization) }, // ADD YOUR CODE debug: function(msg) { // this is your only friend _gel('msg').innerHTML += '<div>' + msg + '</div>'; } } function init() { new MyMapplet(); } _IG_RegisterOnloadHandler(init); </script> <div id="msg"/> ]]></Content> </Module>
Note
From here, we see only inside of MyMapplet.prototype.
A simple asynchronous call
Shows the center latitude and longitude (only once).
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); this.map.getCenterAsync(function(latlng) { this.debug('lat = ' + latlng.lat() + ', lng = ' + latlng.lng()); }.bind(this)); }, debug: function(msg) { _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
A simple asynchronous call (cont.)
Shows the center latitude and longitude on every move.
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); GEvent.addListener(this.map, 'dragend', this.onMove.bind(this)); }, onMove: function() { this.map.getCenterAsync(function(latlng) { this.debug('lat = ' + latlng.lat() + ', lng = ' + latlng.lng()); }.bind(this)); }, debug: function(msg) { _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
Marker
Shows markers on every click.
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); GEvent.addListener(this.map, 'click', this.onClick.bind(this)); }, onClick: function(overlay, point) { if (!overlay) { this.map.addOverlay(new GMarker(point)); } }, debug: function(msg) { // this is your only friend _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
InfoWindow
Shows pretty girls on every click.
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); GEvent.addListener(this.map, 'click', this.onClick.bind(this)); }, onClick: function(overlay, point) { if (point) { // point could be null if (!overlay) { this.map.openInfoWindowTabsHtml(point, [new GInfoWindowTab('tozawa', '<img src="http://www.ariel-networks.com/company/employee/images/tozawa.jpg"/>'), new GInfoWindowTab('takatera', '<img src="http://www.ariel-networks.com/company/employee/images/takatera.jpg"/>'), new GInfoWindowTab('tien', '<img src="http://www.ariel-networks.com/company/employee/images/tien.jpg"/>')]); } } }, debug: function(msg) { // this is your only friend _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
InfoWindow(cont.)
Ariel Area on Google Maps.
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); GEvent.addListener(this.map, 'click', this.onClick.bind(this)); }, onClick: function(overlay, point) { if (point) { this.map.openInfoWindowHtml(point, '<iframe src="http://dev.ariel-networks.com"/>'); } }, debug: function(msg) { // this is your only friend _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
For plotting, use KML
If you want to plot static locations, use KML. Don't write a code (loop).
MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); this.map.setCenter(new GLatLng(35.644307, 139.699157), 15); this.map.addOverlay(new GGeoXml("http://maps.google.co.jp/maps/ms?hl=ja&gl=jp&ie=UTF8&msa=0&msid=107061927227382858092.0004436b779d2ca57b1a9&z=17&om=1&output=kml")); this.map.addOverlay(new GGeoXml("http://maps.google.co.jp/maps/ms?ie=UTF8&hl=ja&msa=0&msid=107466427548098455555.00043a52ff3965b48e83c&z=17&om=1&output=kml")); }, debug: function(msg) { _gel('msg').innerHTML += '<div>' + msg + '</div>'; } }
It makes Nakameguro center, and plot two KML data.
KML data are created by 'My Maps'.
Play with Geocoding
All code;
<?xml version="1.0" encoding="UTF-8"?> <Module> <ModulePrefs title="Hello World" description="Displays a Hello World message in the left panel" author="ariel" author_email="gmap@ariel-networks.com" height="150"> <Require feature="sharedmap"/> </ModulePrefs> <Content type="html"><![CDATA[ <script type="text/javascript"> // derived from prototype.js =begin var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var $A = Array.from = function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0, length = iterable.length; i < length; i++) results.push(iterable[i]); return results; } } Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } } // derived from prototype.js =end var MyMapplet = Class.create(); MyMapplet.prototype = { initialize: function() { this.map = new GMap2(); this.geocoder = new GClientGeocoder(); this.geo_input = _gel('geo-input'); GEvent.bindDom(this.geo_input, 'change', this, this.onInput); }, onInput: function(ev) { var address = this.geo_input.value; this.geocoder.getLatLngAsync( address, function(latlng) { if (!latlng) { alert(address + " not found"); } else { this.map.setCenter(latlng, 15); var marker = new GMarker(latlng); this.map.addOverlay(marker); marker.openInfoWindowHtml(address); } }.bind(this) ); }, debug: function(msg) { _gel('msg').innerHTML += '<div>' + msg + '</div>'; } } function init() { new MyMapplet(); } _IG_RegisterOnloadHandler(init); </script> <input type="text" id="geo-input"/> <div id="msg"/> ]]></Content> </Module>
Play with external Web API
Example:
Homework
Create a new mapplet.