Subscribe Now: Feed Icon

Tuesday, July 12, 2011

Silverlight: Adding Google Streets View, part 2

Continued from Adding Google Streets View, using Google API V3 instead of V2

Or actually “ops…”

During the testing stage for our application my Team Leader installed the application on another server and tried to run it. He got this pop-up for his trouble:

Google-API-V2-key-warning

Message from webpage

This web site needs a different Google Maps API key. A new key can be
generated at http://code.google.com/apis/maps/signup.html.

Now while using the original code I had a problem with one line of code:

  1. <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=DioG342lPJG3WTn3zmQqebsjVg" type="text/javascript"></script>

More precisely the part of: key=DioG342lPJG3WTn3zmQqebsjVg

But I thought to myself – “It works…”

Well the bite in the ass has come…

So what is this key?

It seems Google Map API V2 requires each domain to be registered and then to use an API key. I didn’t want to do that because we had too many servers with different domains (with new environments added all the time).

Getting such a key though is as easy as registering here, but be sure to first read the FAQ (more on that in my previous post).

 

Google API V3 to the rescue…

In Google API V3 they removed the need for API key, but they also changed the API so that the current JavaScript code doesn’t work.

So I rewrote it to this:

Code Snippet
  1. <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
  2. <script type="text/javascript">
  3.  
  4.     function streetView(lat, lng) {
  5.         var client = new google.maps.StreetViewService();
  6.         var streetViewLocation = new google.maps.LatLng(lat, lng);
  7.         client.getPanoramaByLocation(streetViewLocation, 50, function (result,
  8.             status) {
  9.             if (status == google.maps.StreetViewStatus.OK) {
  10.                 var panoramaOptions = {
  11.                     position: streetViewLocation,
  12.                     pov: {
  13.                         heading: 34,
  14.                         pitch: 10,
  15.                         zoom: 1
  16.                     },
  17.                     visible: true,
  18.                     addressControl: true
  19.                 };
  20.                 document.getElementById("GoogleStreetViewContainer").style.display = 'block';
  21.                 var panorama = new google.maps.StreetViewPanorama(document.getElementById("GoogleStreetviewObject"), panoramaOptions);
  22.             }
  23.         });
  24.     }
  25. </script>

Much more simple code:

getPanoramaByLocation – returns status OK for coordinates that have Street View 50 (set in a parameter) meters from them.

Be sure though to place (line 20):

  1. document.getElementById("GoogleStreetViewContainer").style.display = 'block';

Before calling StreetViewPanorama or you will get an “Out of stack space” error (I opened a bug on this but since the work around as fairly simple it probably won’t be fixed any time soon…).

One last warning the functionality is almost the same. The only difference I could find is that V3 hasn’t implemented Full Screen mode yet (unknown when it will be added or if it will) where V2 has it out of the box.

V2:

GoogleStreetViewV2

V3:

GoogleStreetViewV3

Seems almost the same…

 

Code sample can be found in my CodePlex Project, or directly here.

 

Edit (13/09/2011): Fixed Firefox bug in JavaScript

 

Keywords: Silverlight, Map, Google Street View, Google Map API

Silverlight: Adding Google Streets View

While in drafting stage this post was originally called “ArcGIS Silverlight API: Adding Google Streets” but the only thing ArcGIS Silverlight API adds to the mix is the extraction of the coordinates from the map. For a regular Silverlight application just pass the coordinates from another source. For a version with Google Map API V3 see part 2.

My team leader asked me to try and add Google Street View to our map (that uses ArcGIS Silverlight API). The idea that he wanted me to look at was simple: when the user right-clicks on the map a new window will open with Google Street View. There was just a minor problem with this – Israel doesn’t have a Google Street View yet and our development environment is using the map of Israel .

But it was minor because I just downloaded ESRI’s Interactive Samples and worked on those. I decided to work on the simplest of the samples – Mapping –> ArcGIS Tiled Layer:

Mapping-ArcGIS-Tiled-Layer

(for a live demo of the sample click here)

The code is in ArcGISSilverlightSDK\Map\Map.xaml and just contains:

  1. <UserControl x:Class="ArcGISSilverlightSDK.Map"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:esri="http://schemas.esri.com/arcgis/client/2009">
  5.     <Grid x:Name="LayoutRoot" >
  6.  
  7.                     <esri:Map x:Name="MyMap">
  8.             <esri:ArcGISTiledMapServiceLayer ID="MyLayer"
  9.                 Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" />
  10.         </esri:Map>
  11.  
  12.     </Grid>
  13. </UserControl>

End result:

ESRI-silverlight-with-Google-streetview

(that after clicking on London)

Most of the code here uses a sample I found here (it’s not in English, without explanations, but it’s mostly code so I managed).

 

Changes to the html/aspx file:

  1. <body>
  2.     <div id="DllShepherd_GoogleStreetViewContainer" style="position:absolute;z-index:10;bottom:0px;right:0px; height: 325px; width: 300px;background-color:#888888;display:none;">
  3.         <input type="button" style="position:relative;z-index:1000;height: 25px; width: 60px;left:180px;top:5px"
  4.         value="Close" onclick="document.getElementById('DllShepherd_GoogleStreetViewContainer').style.display='none';"/>
  5.         <div id="DllShepherd_GoogleStreetviewObject" style="position:relative;height: 290px; width: 290px;left:5px;top:5px"></div>
  6.     </div>
  7.     <form id="form2" runat="server" style="height:100%">
  8.     <div id="silverlightControlHost">
  9.         <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
  10.           <param name="source" value="ClientBin/DllShepherd.SilverlightGoogleMaps.xap"/>
  11.           <param name="onError" value="onSilverlightError" />
  12.           <param name="background" value="white" />
  13.           <param name="minRuntimeVersion" value="4.0.50826.0" />
  14.           <param name="autoUpgrade" value="true" />
  15.                 <param name="windowless" value="true" />
  16.                      <asp:Literal ID="ParamInitParams" runat= "server"></asp:Literal>
  17.           <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
  18.               <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
  19.           </a>
  20.         </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
  21.     </form>
  22. </body>
  23. </html>

(the sample used JavaScript to dynamically create multiply Google Street View containers, I only need one)

Added the GoogleStreeViewContainer and the most important part is this change to the Silverlight object:

  1. <param name="windowless" value="true" />

Without it the Silverlight object will always be on top (took me a few hours since the original sample was with the Silverlight asp control).

 

  1. <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YourGoogleApiKey" type="text/javascript"></script>
  2. <script src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js" type="text/javascript"></script>
  3. <script type="text/javascript">
  4.     $(document).ready(function () {
  5.         pan = new GStreetviewPanorama(document.getElementById("DllShepherd_GoogleStreetviewObject"));
  6.         GEvent.addListener(pan, "error", StreetViewError);
  7.     });
  8.  
  9.     var pan;
  10.  
  11.     //Edited original sample (simplified)
  12.     function streetView(lat, lon) {
  13.  
  14.         var latlng = new GLatLng(lat, lon);
  15.         var svp = new GStreetviewClient();
  16.         svp.getNearestPanoramaLatLng(latlng,
  17.              function (newPoint) {
  18.                  if (newPoint == null) {
  19.                      //Street View Not Available For This Location (alerts just annoy me)
  20.                      return;
  21.                  }
  22.                  document.getElementById("DllShepherd_GoogleStreetViewContainer").style.display = 'block';
  23.                  panoramaOptions = { latlng: newPoint };
  24.                  var myPov = { yaw: 180, pitch: 0 };
  25.                  pan.setLocationAndPOV(latlng, myPov);
  26.  
  27.              });
  28.     }
  29.  
  30.     //Taken from sample
  31.     function StreetViewError(errorCode) {
  32.         if (errorCode == 603) {
  33.             alert("Error: Flash doesn't appear to be supported by your browser");
  34.             return;
  35.         }
  36.         if (errorCode == 600) {
  37.             alert("Street View Not Available For This Location");
  38.             return;
  39.         }
  40.     }
  41. </script>

Added some JavaScript that adds the flash object to the Div tag GoogleStreeViewObject.

Be sure to change the Google Map API Key (YourGoogleApiKey) in:

  1. <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YourGoogleApiKey"

Without the API key the only place this site will work will be in localhost. The key is domain specific but the FAQ provides a way to concatenate several API keys.

You can get a key from Google Map API V2 site. Before registering be sure to read this FAQ first, will help you in choosing the best base site so that all the sub domains will be valid.

 

From the Silverlight application calling the JavaScript function StreetView:

  1. private void MyMap_MouseClick(object sender, ESRI.ArcGIS.Client.Map.MouseEventArgs e)
  2. {
  3.     var p = e.MapPoint;
  4.     HtmlPage.Window.Invoke("streetView", p.Y, p.X);
  5. }

(in a general Silverlight application just pass the coordinate here)

Since it’s a simple sample I just changed the map’s url and added a code behind mouse click event. The reason I changed the map is that Google Street View expects a spatial reference of Geographic WGS84 (see my other post Geographic Coordinates Systems) which the original map didn’t have (in a real application you will either use this coordinate or will have to convert the coordinates).

  1. <esri:Map x:Name="MyMap" MouseClick="MyMap_MouseClick">            
  2.     <esri:ArcGISTiledMapServiceLayer ID="MyLayer"
  3.         Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" />
  4. </esri:Map>

 

And that’s it.

Code sample can be found in my CodePlex Project, or directly here.

 

Edit: Added a part on the Google Map API v2 Key.

Edit (13/09/2011): Fixed Firefox bug in JavaScript

Continued in part 2.

 

Resources:

Geocoding & StreetView Google per ESRI API Silverlight – not in English but it’s mostly code…

ESRI’s Interactive Samples

 

Keywords: ESRI, Silverlight, Map, Google Street View