Using Google Maps with ArcGIS Silverlight

The ArcGIS API for Microsoft Silverlight/WPF (and Windows Phone) includes an extensible framework for adding geographic data from a variety of sources. The core library (ESRI.ArcGIS.Client) contains the base classes for the framework and the implementation for the primary source of data, ArcGIS Server. In the initial version of the API, the framework was utilized to support the use of Bing Maps imagery. In version 2.0, the framework was leveraged to support other data sources, such as OpenStreetMap, WMS, and GeoRSS in the toolkit data sources assembly (ESRI.ArcGIS.Client.Toolkit.DataSources.dll) . You'll notice Google is missing from that list of data sources. This brings up a question, and the purpose for this blog post:

Can you use Google Maps imagery in an ArcGIS Silverlight/WPF/Windows Phone application?

There are two answers; one technical, one legal. First, technically it can be done. The Web Mercator projection and the tiling scheme used by Google map and image tiles is virtually the same as Bing, new ArcGIS Online services, and OpenStreetMap. The URL format to access Google tiles directly can be discovered. For example, you can use Fiddler to view requests from a legitimate Google Maps application. Some folks even discuss the URL format and structure (e.g. Code Project).

Now for the legal answer. In short, no. You cannot access Google Maps imagery outside of an interface (read: APIs) provided by Google. This is mentioned in an online FAQ which references an item in the terms of service. The last statement in the terms of service appears to suggest that direct access to map tiles outside of an API is possible via an explicit agreement. In an email conversation with Thor Mitchell, Product Manager for the Google Maps API, he clarified these terms by stating that such agreements are rare and "they are generally limited to embedded device partnerships such as in-car navigation systems and in-flight entertainment systems." Thor also reminded me that the URL format to access Google tiles is an undocumented interface, so it can change at any time. And Google maintains a team that identifies and contacts application developers that use Google tiles (and services) in an unsupported way. If you try, you will likely be asked to stop, followed by more punitive measures if ignored.

 
So why doesn't Google allow direct access to map tiles?
 
There are a number of reasons. According to Google, enabling direct access prevents them from meeting their financial obligations to companies from which they license data. Also, there has been no compelling business justification given the risk of abuse. If you'd like to comment on the prospect of direct access to Google map tiles, Google's own Pamela Fox has published an issue on the topic.
 
So how do you legally use Google Maps data within an ArcGIS Silverlight application?
 
You'll need to use the Google Static Maps API. Essentially it generates snapshots of Google map tiles at an extent, image size, and scale level that matches the Google Maps tiling scheme. You can think of it as sort of a dynamic tiled layer.

Since the custom layer will generate dynamic map images from a service, it can extend the DynamicMapServiceLayer class in the ArcGIS Silverlight API. You can override the Initialize method to set a few key properties, such as the full extent and spatial reference, but you'll also want to define the tiling scheme that matches Google Maps and will be used to define the scale level at which a dynamic image will be generated. These levels of detail (lods) will be used to determine the output size of the map image generated by the Static Maps API. The primary method to override in this case is GetUrl() which provides access to the map extent, pixel width and height, and a delegate to call when URL construction is complete and a request for a new map image should be generated. At runtime, each extent change in the Map control calls GetUrl() to generate a new map image.
 
The URL to generate a Google Static Map requires a center point in geographic coordinates, a scale level, and the image size in pixels. The Static Maps API does not support projection on the fly, so the spatial reference will always be Web Mercator. This means the spatial reference of the ArcGIS Silverlight Map control must also be set to Web Mercator. So the GetUrl() method in the custom layer is provided a map extent in Web Mercator. Fortunately the ArcGIS Silverlight API includes a client-side static class, ESRI.ArcGIS.Client.Projection.WebMercator, which can be used to tranform geometry between Web Mercator and Geographic (WGS84) coordinate systems. This can be used to tranform the center point from Web Mercator to geographic coordinates.
 
The appropriate scale level is determined by matching the resolution (map units per pixel) of the Map with the level of detail in the Google Maps tiling scheme. If they match, the image size passed to the GetUrl() method does not need to change. If they do not match then the image size must be modified to account for the discrepancy. Unfortunately with the Static Maps API the image size is limited to 640x640 pixels. So while the layer will work in between scale levels, you will be able to generate larger images (up to 640 pixels on a side) more reliably if you snap to levels of the Google Maps tiling scheme. Since you cannot associate a tiling scheme with a Map control outside of adding a tiled layer, you must first add a "dummy" tiled layer with the same levels of detail used by Google Maps. The sample download below contains one such layer, MercatorSchemaTiledLayer. Be sure to set the dummy layer's visibility to false (since you'll be using the Static Map API to generate your basemap) and set the SkipToLevels property on the Map control to true.
 
    

using System;

 
    

using ESRI.ArcGIS.Client;

 
    

using ESRI.ArcGIS.Client.Geometry;

 
    

 

 
    

namespace CustomLayers

 
    

{

 
    

    public class GoogleStaticMapLayer : DynamicMapServiceLayer

 
    

    {

 
    

        private static ESRI.ArcGIS.Client.Projection.WebMercator mercator =

 
    

                new ESRI.ArcGIS.Client.Projection.WebMercator();

 
    

 

 
    

        private const double cornerCoordinate = 20037508.3427892;

 
    

        private const int WKID = 102100;

 
    

 

 
    

        public GoogleStaticMapLayer() : base(){ }

 
    

 

 
    

        private Lod[] Lods { get; set;}

 
    

       

 
    

        public override void Initialize()

 
    

        {

 
    

            this.FullExtent =

 
    

            new ESRI.ArcGIS.Client.Geometry.Envelope(-cornerCoordinate, -cornerCoordinate, cornerCoordinate, cornerCoordinate)

 
    

            {

 
    

                SpatialReference = new SpatialReference(WKID)

 
    

            };

 
    

 

 
    

            this.SpatialReference = new SpatialReference(WKID);

 
    

           

 
    

            Lods = new Lod[21];

 
    

            double resolution = cornerCoordinate * 2 / 256;

 
    

            for (int i = 0; i < Lods.Length; i++)

 
    

            {

 
    

                Lods[i] = new Lod() { Resolution = resolution };

 
    

                resolution /= 2;

 
    

            }

 
    

 

 
    

            base.Initialize();

 
    

        }

 
    

 

 
    

        public override void GetUrl(ESRI.ArcGIS.Client.Geometry.Envelope extent, int width, int height,

 
    

            DynamicMapServiceLayer.OnUrlComplete onComplete)

 
    

        {

 
    

            MapPoint geogCenterPoint = null;

 
    

            string mapURL = null;           

 
    

           

 
    

            try

 
    

            {

 
    

                if (width > 640 || height > 640)

 
    

                    throw new Exception("Width or height greater than 640");

 
    

               

 
    

                double currentResolution = extent.Width / width;

 
    

                int currentLodIndex = 0;

 
    

                int requestWidth = 0;

 
    

                int requestHeight = 0;

 
    

                for (int i = 0; i < Lods.Length; i++)

 
    

                {

 
    

                    Lod lod = Lods[i];

 
    

                    currentLodIndex = i;

 
    

                    if ((int)lod.Resolution <= (int)currentResolution)

 
    

                    {

 
    

                        requestWidth = (int)(extent.Width / lod.Resolution);

 
    

                        requestHeight = (int)(extent.Height / lod.Resolution);

 
    

                        break;

 
    

                    }

 
    

                }

 
    

 

 
    

                if (requestWidth > 640 || requestHeight > 640)

 
    

                    throw new Exception("Request width or height greater than 640");               

 
    

 

 
    

                geogCenterPoint = mercator.ToGeographic(extent.GetCenter()) as MapPoint;

 
    

 

 
    

                mapURL = string.Format

 
    

                  ("http://maps.google.com/maps/api/staticmap?center={0},{1}&zoom={2}&size={3}x{4}&maptype=roadmap&sensor=false",

 
    

                   geogCenterPoint.Y, geogCenterPoint.X, currentLodIndex, requestWidth, requestHeight);

 
    

 

 
    

            }

 
    

            catch (Exception ex)

 
    

            {

                // Message box just for
The screen shot below shows the Google Static Maps API in action. A Static Map is used as a basemap for an ArcGIS Server dynamic map service. Note the Google logo and copyright text at the bottom of the map.

 
There are a few legal implications to keep in mind when using the Static Maps API:
  • The application must run in a Web browser (see 10.8). This means you cannot use it in a WPF, Silverlight out-of-browser, or Windows Phone application.
  • All branding and attribution must remain visible at all times, and can not be obscured by overlays or UI elements in any way (see 7.4d). Although the Google copyright text and ESRI logo are close in an ArcGIS Silverlight application, you can still see\read the Google copyright details.
  • The application should not attempt to stitch multiple static map images together to display a map that is larger than permitted in the Maps APIs Documentation (see 10.2). Basically you can't generate a bunch of dynamic images and stitch them together on the client for map sizes greater than 640 pixels on a side. Even if you could legally, you'll get a Google stamp on each image, which might show up in the middle of your map - it could get messy.
 
Download the sample solutionto see how to work with the Google Maps Static API in ArcGIS Silverlight.
 
Special thanks to Thor Mitchell, Product Manager for the Google Maps API, for providing detailed information on the legal use of Google Maps data and APIs.
 
转自:http://rexdotnet.blogspot.com/2010/09/use-google-maps-with-arcgis-silverlight.html

你可能感兴趣的:(silverlight)