通过 Google Maps API 找到你自己
我不认为有一天我会不使用 Google 来查找东西,而且最近,我看到越来越多的企业在其网站上发布 Google 地图以显示其位置。 有时,这些公司甚至会在地图上放置小图钉来指示其每个位置,这对于像我这样在导航方面有困难的人来说非常方便。
我想可以对 Google 地图进行屏幕截图并将其发布在公司网站上。 我也猜想您可以在 GIMP 中打开该图像并手动添加一堆图钉。 此外,我猜想您可以使用图像地图使图钉可点击和交互。 是的,您可以这样做,在紧急情况下,这可能是有道理的,但它肯定不如使用 Google Maps API 并正确地完成它有趣。
使用 Google Maps API,您可以创建以特定位置为中心的地图。 您可以在地图上的任何位置放置彩色图钉,并且可以在地图上绘制几何形状。 也许您甚至想在交付区域或学区周围绘制边框。 Google Maps API 非常强大,而我只是触及了表面。
但是,在开始之前,您需要通过在 Google 注册来获取 API 密钥。 此注册是免费的,您会立即收到您的密钥。 但是,您必须同意一些使用限制。 大多数限制看起来是合理的。 唯一的意外是使用 Google Maps API 的网站必须是公开可访问的; 它不能位于内联网上,也不能受密码保护。 如果您需要创建无法公开访问的应用程序,您可以与 Google 另行安排。 要注册密钥,请在浏览器中访问 code.google.com/apis/maps/signup.html。 系统会要求您提供您网站的域名,并且每个域名都需要一个单独的密钥。
获得 API 密钥后,您就可以开始编码了。 首先,您需要创建一个简单的网页来显示您的地图(清单 1)。
清单 1. Google 地图的 HTML
1 <html> 2 <head> 3 <title>My Google Map</title> 4 5 <style> 6 #map { 7 position: relative; 8 left: 5px; 9 top: 5px; 10 width: 764px; 11 height: 520px; 12 } 13 </style> 14 15 <script src="http://maps.google.com/ ↪maps?file=api&v=3&key=thisisasecret&sensor=false" 16 type="text/javascript"> 17 </script> 18 19 <script type="text/javascript" src="/main.js"></script> 20 21 </head> 22 23 <body onload="initialize('map');" onunload="GUnload();"> 24 25 <div id=map> 26 Map goes here. 27 </div> 28 29 <p> 30 31 SW Corner: <span id=debug_sw> </span><br> 32 NE Corner: <span id=debug_ne> </span><br> 33 Zoom: <span id=debug_zoom> </span><br> 34 35 </body> 36 </html>
第 1-14 行是简单的样板 HTML。 请注意,我为名为“map”的 div 容器包含了一些内联样式。 在这里,我主要对设置结果矩形的大小感兴趣。
第 15-17 行是您加载 Google Maps API 的位置。 URI 中看起来像“v=3”的部分表示我正在使用 API 的版本 3。 这也是您包含之前获得的 API 密钥的位置。 最后,您会看到 URI 的“sensor=false”部分。 这表示我没有使用任何类型的位置传感器(例如 GPS)来选择合适的地图。 Google Maps API EULA 要求准确配置此字段。
我在第 19 行加载了我编写的用于加载和操作地图的 JavaScript 程序。 在第 23 行,我安排在我编写的初始化函数在页面完成加载时调用,以及 Google 提供的另一个函数在关闭页面时调用。 我稍后会讨论 initialize() 函数。
HTML 的其余部分只是创建一个容器(前面提到过)来容纳地图,以及一些其他容器来容纳调试信息。 您可能不想在生产应用程序中显示此信息,但了解 API 可以提供哪些类型的信息以及程序员可以使用哪些方法来保持显示与用户与地图交互时保持最新是很有指导意义的。
地图的其余部分是在 JavaScript 中创建的,所以让我们看一下清单 2。
清单 2. Google 地图的 JavaScript
1 var map; 2 var default_map = "35.181804,-105.40625,8"; 3 4 function initialize (el) { 5 if (!GBrowserIsCompatible()) { 6 document.getElementById(el).innerHTML = "Incompatible Browser"; 7 return; 8 } 9 10 map = new GMap2(document.getElementById("map")); 11 12 var l = default_map.split(","); 13 map.setCenter(new GLatLng(parseFloat(l[0]), parseFloat(l[1])) ↪,parseInt(l[2])); 14 update_gui(); 15 16 map.addControl(new GMapTypeControl()); 17 map.addControl(new GSmallMapControl()); 18 map.setMapType(G_HYBRID_MAP); 19 20 GEvent.addListener(map, "mousemove", function () {update_gui();}); 21 22 ajax_get("/markers.xml", "parse_markers"); 23 24 ajax_get("/zones.xml", "parse_zones"); 25 26 update_gui(); 27 } 28 29 function parse_markers (e) { 30 var i, lon, lat; 31 var assets = e.getElementsByTagName("asset"); 32 33 for (i=0; i<assets.length; i++) { 34 lon = parseFloat(assets[i].getAttribute("long")); 35 lat = parseFloat(assets[i].getAttribute("lat")); 36 37 var marker = new GMarker(new GLatLng(lat,lon)); 38 39 marker.id = assets[i].getAttribute("id"); 40 marker.name = assets[i].getAttribute("name"); 41 marker.desc = assets[i].getAttribute("desc"); 42 43 marker.long = lon; 44 marker.lat = lat; 45 46 map.addOverlay(marker); 47 48 //GEvent.addListener(marker, "mouseover", function () ↪{marker_mouseover(this);}); 49 GEvent.addListener(marker, "click", function () ↪{marker_click(this);}); 50 } 51 } 52 53 function parse_zones (e) { 54 var i,j; 55 var containers = e.getElementsByTagName("container"); 56 57 for (i=0; i<containers.length; i++) { 58 var bounds = new Array; 59 60 var id = containers[i].getAttribute("id"); 61 var name = containers[i].getAttribute("name"); 62 var desc = containers[i].getAttribute("description"); 63 64 var points = containers[i].getElementsByTagName("point"); 65 for (j=0; j<points.length; j++) { 66 var p = new Object; 67 68 p.long = points[j].getAttribute("long"); 69 p.lat = points[j].getAttribute("lat"); 70 71 bounds.push(new GLatLng(p.lat,p.long)); 72 } 73 74 var container = new GPolygon(bounds, "#ff0000", ↪5, .5, "00ff00", .2); 75 76 container.id = id; 77 container.name= name; 78 container.desc = desc; 79 80 map.addOverlay(container); 81 GEvent.addListener(container, "click", function () ↪{zone_click(this);}); 82 83 } 84 } 85 86 function marker_mouseover(who) { 87 map.openInfoWindow(new GlatLng(who.lat,who.long), who.name); 88 } 89 90 function marker_click(who) { 91 map.openInfoWindow(new GLatLng(who.lat,who.long), who.desc); 92 } 93 94 function zone_click(who) { 95 map.openInfoWindow(new GLatLng(who.lat,who.long), who.desc); 96 } 97 98 function update_gui () { 99 var sw = map.getBounds().getSouthWest(); 100 var ne = map.getBounds().getNorthEast(); 101 102 document.getElementById("debug_sw").innerHTML= sw.toString(); 103 document.getElementById("debug_ne").innerHTML= ne.toString(); 104 document.getElementById("debug_zoom").innerHTML= map.getZoom(); 105 }
在第 1 行和第 2 行中,我创建了一个全局变量来保存 API 将创建的“map”对象。 我还配置了纬度和经度来指向地图。
initialize() 函数位于第 4-27 行,负责创建地图的所有工作。 在第 5-10 行中,我测试以确保用户的 Web 客户端能够显示地图,如果可以,则创建地图对象。 第 12-18 行配置地图。 首先,我选择地图要显示的位置。 然后,我添加地图类型和地图导航控件。 地图类型控件允许用户在简单地图、卫星地图或混合地图之间进行选择。 地图导航控件允许用户平移地图并放大和缩小。 最后,我将地图配置为默认显示混合地图。
第 14、20 和 26 行中引用的 update_gui() 函数只是更新地图下方的调试信息,可能不会在生产应用程序中使用。 第 20 行很有趣,因为它演示了当用户滚动或缩放地图到其他位置时,您的应用程序如何做出反应。 在这种情况下,应用程序只是更新地图下方的纬度/经度坐标。 我稍后会更详细地讨论 update_gui() 函数。
此时,如果您什么都不做,您将拥有一个用户可以与之交互的地图。 他们将能够选择地图类型,移动它并放大和缩小。 但是,让我们更进一步。
第 22 行和 24 行调用的 ajax_get() 函数未包含在清单 2 中,但编写起来相对容易。 此函数只是接受一个 URL 和一个 JavaScript 函数的名称作为参数。 然后,该函数发出 AJAX 调用并获取给定 URL 处的数据。 此数据假定为 XML,并传递给指示的函数。
第 22 行引用的 parse_markers() 函数接受一个 XML 字符串,该字符串描述了在地图上放置标记的位置。 此 XML 类似于清单 3。 如您所见,它只是一个资产列表; 每个资产都有一个 ID、名称、描述和纬度/经度位置。
清单 3. 标记的 XML 清单
<assets> <asset id="1" name="Home" desc="The home base" lat="35" ↪long="-105"></asset> <asset id="2" name="BC Site" desc="The off-site site" ↪lat="34" long="-106"></asset> </assets>
清单 2 第 24 行的 parse_zones() 函数的工作方式类似,并描述了要在地图上绘制的形状。 相应的 XML 看起来像清单 4 中所示。
清单 4. 区域的 XML 清单
<containers> <container id="1" name="HQ" description="This is HeadQuarters"> <point lat="35.0" long="-105.0" /> <point lat="35.0" long="-106.0" /> <point lat="36.0" long="-106.0" /> <point lat="35.0" long="-105.0" /> </container> <container id="2" name="OffSite" ↪description="This is the Offsite Site"> <point lat="37.0" long="-104.0" /> <point lat="37.0" long="-105.0" /> <point lat="38.0" long="-105.0" /> <point lat="37.0" long="-104.0" /> </container> </containers>
在这里,您会看到两个容器和一个定义其边界的纬度/经度点列表。 因此,凭借您目前拥有的以及一些略有不同的数据,您将获得一张类似于图 1 的地图。 默认情况下,地图以新墨西哥州阿尔伯克基郊外的位置为中心。 您会看到一个填充了绿色的红色三角形。 该三角形是半透明的,因此您可以通过它看到地图。 您还会在三角形的右下角看到其中一个标记。 在图 1 中,我单击了标记以演示信息窗口,我稍后会对此进行更详细的讨论。
让我们仔细看看清单 2 的第 29-51 行中定义的 parse_markers() 函数。 此函数非常简单,只是循环遍历资产列表。 对于每个资产,该函数解析标记的纬度/经度坐标,并使用它们创建一个标记对象(第 34-37 行)。 请注意,在第 37 行,我必须使用 lat 和 long 变量来创建一个对象以传递给标记构造函数。 接下来,我为每个标记设置了一些额外的属性以供以后使用。 然后,在第 46 行,我将标记添加到地图中。 第 48 行和第 49 行很有趣 - 它们允许用户单击(或鼠标悬停在)标记上并显示其他信息。 我注释掉了其中一行,因为它似乎无法很好地同时处理鼠标悬停和鼠标单击事件。 我可能需要一些额外的逻辑,但您明白了。
parse_zones() 函数稍微复杂一些,因为它必须从为每个容器列出的点构建一个 GPolygon 对象。 第 53-62 行与 parse_marker() 函数的第一部分类似。 主要区别在于第 64-72 行,我在其中循环遍历划定区域边界的每个点,为每个点创建一个 GLatLng 对象,并将该对象推送到 bounds 数组中。 然后,在第 74 行,我使用此点数组创建一个 GPolygon 对象。 GPolygon 构造函数还允许您指定边框颜色和大小,以及填充颜色和不透明度设置。 我在第 80 行将多边形添加到地图中。 我在第 81 行添加了一个单击事件处理程序,以便当用户单击给定区域时,应用程序可以提供有关该区域的更多信息。
第 86-96 行中定义的事件处理程序几乎相同且非常简单,因此请允许我对它们一概而论。 它们都调用地图对象的 openInfoWindow 方法来打开小消息气泡并显示消息。 在这些情况下,我只是显示用户单击的对象的名称或描述。 在实际应用中,您可以使用 id 属性来对服务器端数据库进行 AJAX 回调并执行一些非常酷的事情。
最后,第 98-105 行的 update_gui() 函数负责更新地图底部的一些显示信息。 为此,该函数使用地图对象的一些方法来获取地图西南角和东北角的坐标。 然后,将这些坐标转换为字符串并放置在网页上的相应容器内。 地图的缩放因子以非常相似的方式处理。
如您所见,使用 Google Maps API 非常容易。 该 API 非常直观,并且在 Google 上有详尽的文档记录。 正如我在本文开头提到的,绘制企业办公室位置或客户位置非常容易。 但是,这是 Web,它应该很有趣。 来一场使用真实地图的地理知识问答游戏怎么样? 或者一个类似 Risk 的游戏,或者任何数量的在虚拟地球上进行的军事模拟? 我记得玩过一款赛车游戏,它允许您驾驶汽车在 Google 地图上行驶。 Google Maps API 简单而强大,还有许多有趣的事情等着用它来完成。
Mike Diehl 是新墨西哥州阿尔伯克基市的合同程序员和顾问。 Mike 与他的妻子和三个年幼的儿子住在一起,可以通过电子邮件 mdiehl@diehlnet.com 联系到他。