Imagery Metadata and Reverse Geocoding with the Virtual Earth AJAX Control

I couldn't think of a good way to break these apart, so I developed both imagery metadata (date of photography / imagery acquisition) and reverse geocoding (converting coordinates to the nearest address) together. Actually, I can think of many ways to get the reverse geocoding working without the imagery metadata - I did so in my "Getting Addresses Using Aerial Photos" post, but with the introduction of imagery metadata in our latest release, that is, getting some date information about when the ortho photography (not Bird's Eye) was captured I had to make something cool. So here it is. I need a place to host my ASP .Net applications to show them off. I'm frustrated. When I figure out what's wrong with my Win ISP account, I'll post them - I promise.


The application I wrote allows you to right-click anywhere on the map and (with the aerial map style) you can get the date photography/imagery was captured (as a range), the latitude and longitude coordinates of the pixel clicked, and subsequently the geocodes reversed to an address including the precision type - either interpolated or rooftop.

image A couple things you should know. (1) The imagery metadata method will only work if you have a valid production token - staging tokens will not work. (2) In order to have production access you must have a licensed version of Virtual Earth - send mail to the Virtual Earth Licensing Team to get a license. This means there are now 3 methods that only work if you're licensed to use Virtual Earth and NOT part of the free API (traffic flow data and exposed route geometry, being the other two).

With that in mind, let's do this. I have 3 files, but I'm going to show you 2. The third file is Authentication.cs which gets my token and you can read all about in my Authentication and Tokens with Virtual Earth post. The other two files are Metadata.aspx (for the UI) and Metadata.aspx.cs (as the code behind - filled with magic).


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MetaData.aspx.cs" Inherits="MetaData" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">

<html xmlns="">
<!-- saved from url=(0014)about:internet -->
      <title>CP's Meta Data & Reverse Geocoding Page</title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

      <script type="text/javascript" src=""></script>

      <script type="text/javascript">
         var map = null;
         var locations = null;
         var pixel = null;
         var clickEvent = null;
         var LL = null;
         //Get token from code behind
         var token = "<%=strClientToken %>";

        //Geocoding for the input box
        function FindLocation()
         map.Find(null, txtGeocode.value);

        //Get Map & Token
        function GetMap()
             map = new VEMap('myMap');
             //Load Map Onto Map
             map.LoadMap(new VELatLong(33.79965996889632, -116.42153441905977), 17, VEMapStyle.Hybrid);
             //Set token
             //Attach an event for when user right clicks map
             map.AttachEvent("onclick", PixelClick);
             map.onLoadMap = GetVintageInfo();

         //Right click event handler
         function PixelClick(e) {
             if (e.eventName == 'onclick') {
                 if (e.rightMouseButton) {
                     var x = e.mapX;
                     var y = e.mapY;
                     pixel = new VEPixel(x, y);
                     LL = map.PixelToLatLong(pixel);
                     map.FindLocations(LL, GetResults);

         //Callback for getting vintage information
         var vintageStart;
         var vintageEnd;
         var vintage;
         function GetVintageInfo() {
             vintage = function(data) { var v = data; vintageStart = data.DateRangeStart; vintageEnd = data.DateRangeEnd; };
             //Set options for imagery data
             var vintageOptions = new VEImageryMetadataOptions();
             vintageOptions.LatLong = new VELatLong();
             vintageOptions.LatLong = LL;
             //Make request to service to get vintage information
             map.GetImageryMetadata(vintage, vintageOptions);
         //Reverse Geocoding & Vintage Information - Magical Delight
         function GetResults(locations) {
             //strOutput is the string info in the pushpin popup
             var strOutput = "";
             //precision is a string info for geocode type
             var precision = "";
             if (locations) {
             strOutput += "Imagery Capture Date Range: <br/>" + vintageStart + " -<br/>" + vintageEnd + "<br/><br/>";
                //Loop through results and place them in the pushpin popup
                for(var i=0;i<locations.length;i++) {
                    if (locations[i].Precision == VELocationPrecision.Interpolated) {
                        precision = "Interpolated";
                    else {
                        precision = "Rooftop";
                   strOutput += "Address:  ";
                   strOutput += locations[i].Name;
                   strOutput += "<br/>Lat: " + LL.Latitude + "<br>Long:" + LL.Longitude
                   strOutput += "<br/>Geocoded Method: " + precision;
                   strOutput += "<hr/>";
                   var myPushpin = new VEShape(VEShapeType.Pushpin, LL);
                   myPushpin.Title = strOutput;
                   myPushpin.SetDescription = strOutput;
                   //Get Vintage information based on Lat/Long of clicked point
               alert('No Result found.');


        //Clear all pins from the map
        function ClearPins()

   <body onload="GetMap();">
    Geocode: <input type="text" name="txtGeocode" size="30"/><input type="button" value="Find!" onclick="FindLocation();"/>
      <div id="myMap" style="position:relative; width:1000px; height:600px;"></div>
      <br/><input type="button" value="Clear Pins" onclick="ClearPins();"/><br/>


using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using VEWSStaging;

public partial class MetaData : System.Web.UI.Page
    public string strClientToken;
    protected void Page_Load(object sender, EventArgs e)
        strClientToken = Authentication.Authenticate(Page.Request.UserHostAddress);


As you can see, most of the code happens in the ASPX file, as opposed to the ASPX.CS file because we're using the AJAX control. My recent VEWS posts are the flip of that allowing .Net developers to leverage their knowledge and still get maps and aerial photos for their applications without worrying about JavaScript. Or, you could be cool like me and do both! Someone called me a narcissist today!!! WTH??

The code is fairly well documented, but feel free to ping me with questions. For those who want to know, yes the imagery data and reverse geocoding are both available in the Virtual Earth Web Service, as well (production only, for metadata).


Comments (0)

Skip to main content