September 2008 Entries

Developing with Virtual Earth Web Services

Need a mapping solution for your application?  Why not try out Virtual Earth.  They just released the ability to do with Web Services on top of their normal offering. 

Shortly I’ll integrate this in with a mobile client, however I’m sharing the code for a Win32 client to help out early adopters.  You can download my source for this over at Peace Love Code.  The solution has both VB and C#.  All you need to do is get an account for the account information needed.

image image

Where to start?  Lets knock on the door.

With most web services, you need an account.  Here is the process for Virtual Earth:

  1. Go to https://mappoint-css.live.com/mwssignup and sign up with a Windows Live ID
  2. You’ll receive an email, click the link to confirm your sign up.
  3. Get a cup of coffee to wait for another email.
  4. You’ll receive another email with additional infromation.  The big one is the URL for the VE Platform Customer Service Site (VEP CSS)
    https://mappoint-css.live.com/CSCV3/ go there.
  5. Set up your password.

Account secure, now on to Visual Studio.  We’ll have to add in the web service for the token.  If you don’t know how to do this, here are the steps in Visual Studio 2008.

  1. Go to Project->Add Web Reference
  2. Use https://staging.common.virtualearth.net/find-30/common.asmx as your URL.
    image 
  3. You’ll be prompted for a login and password, use your VE credentials for this.  This is not your Live ID.
  4. Click Ok

Now that we have the Common Service, we’ll look at the code to get the token from the service.  First we’ll have to make a reference to the web service by having a using / import statement.  And we’ll create a Authentication class.  Since I’ll be using a Win32 client, I’ll have to go about a different way about getting my IP.

C#:

public class Authentication
{
    public static string strVEWSToken;

    public static string Authenticate()
    {
        // for web pages
        // Page.Request.UserHostAddress
        var ipToken = "127.0.0.1";
        var ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
        foreach (var ip in ips)
        {
            if (ip.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                continue;

            // got a valid IPv4  address
            ipToken = ip.ToString();
            break;
        }

        return Authenticate(ipToken);
    }

    public static string Authenticate(string strIP)
    {
        var commonService = new CommonService
            {
                Url = "https://staging.common.virtualearth.net/find-30/common.asmx",
                Credentials = new NetworkCredential("SomeNumber", "SomePassword")
            };

        // Create the TokenSpecification object to pass to GetClientToken. 
        var tokenSpec = new TokenSpecification
            {
                // Use the Page object to retrieve the end-client’s IPAddress. 
                ClientIPAddress = strIP,
                // The maximum allowable token duration is 480 minutes (8 hours). 
                // The minimum allowable duration is 15 minutes. 
                TokenValidityDurationMinutes = 480
            };

        // Now get a token from the Virtual Earth Platform Token service. 
        strVEWSToken = commonService.GetClientToken(tokenSpec);
        return strVEWSToken;
    }
}

vb.net:

Public Class Authentication
    Public Shared strVEWSToken As String

    Public Shared Function Authenticate() As String
        ' for web pages
        ' Page.Request.UserHostAddress
        Dim ipToken As String = "127.0.0.1"
        Dim ips As var = Dns.GetHostEntry(Dns.GetHostName()).AddressList
        For Each ip As IPAddress In ips
            If ip.AddressFamily <> System.Net.Sockets.AddressFamily.InterNetwork Then
                Continue For
            End If

            ' got a valid IPv4  address
            ipToken = ip.ToString()
            Exit For
        Next

        Return Authenticate(ipToken)
    End Function

    Public Shared Function Authenticate(ByVal strIP As String) As String
        Dim commonService As New CommonService()
        commonService.Url = "https://staging.common.virtualearth.net/find-30/common.asmx"
        commonService.Credentials = New NetworkCredential("SomeNumbers", "YourPassword")

        ' Create the TokenSpecification object to pass to GetClientToken. 
        Dim tokenSpec As New TokenSpecification()

        ' Use the Page object to retrieve the end-client’s IPAddress. 
        tokenSpec.ClientIPAddress = strIP
        ' The maximum allowable token duration is 480 minutes (8 hours). 
        ' The minimum allowable duration is 15 minutes. 
        tokenSpec.TokenValidityDurationMinutes = 480

        ' Now get a token from the Virtual Earth Platform Token service. 
        strVEWSToken = commonService.GetClientToken(tokenSpec)
        Return strVEWSToken
    End Function
End Class

You have a token?  Come in, Come in!

With a token, now we’ll be able to some querying.  For this example, we’ll just fetch imagery, however you can get geographical data, routing and searching data following a similar route taken in this example.  To find out where to get these WDSLs and more about it, MSDN has a full list along with a detailed break down.

For this example, we’ll add a service reference to https://staging.dev.virtualearth.net/webservices/v1/imageryservice/imageryservice.svc.  You can either go Project->Add Service Reference or Add Web Reference.  I used a service reference for this example.  I also named it ImageryService as I’m extremely creative. 

This example’s primary goal is to show how to get bird’s eye data back.  For that, we’ll have to use GetImageryMetadata.  If you just want overhead data with or without roads overlaid, you can use GetMapUri instead.

A quick UX design.

imageI have crazy user interface skills (typically me calling up Jeff for a pow-wow).  I have a quick and dirty interface here for getting some needed information.

  1. With Bird’s eye, you can shift your orientation.
  2. Setting what view you want.
  3. What zoom level, 1 to 21.
  4. What is your device, mobile or desktop

You’ll note I left off latitude and longitude, that is this was just a demo application, I wanted to get up and running quick so I’ve hardcoded my latitude and longitude to Forest Park in St. Louis, MO.

To populate the combo boxes, we’ll use a quick trick.  We’ll use Enum.GetValues to set the data source of the combo boxes.  To fetch the data, we’ll use SelectedValue on the combo box and cast to the needed data type.

C#:

cmbView.DataSource = Enum.GetValues(typeof(MapStyle));
cmbDeviceType.DataSource = Enum.GetValues(typeof(DeviceType));

vb.net:

cmbView.DataSource = [Enum].GetValues(GetType(MapStyle))
cmbDeviceType.DataSource = [Enum].GetValues(GetType(DeviceType))

Making the secret knock for images

After getting what the user wanted, we need to do a request.

C#:

var imageryService = new ImageryServiceClient();

var request = new ImageryMetadataRequest
      {
        Style = mapStyle,
          Options = new ImageryMetadataOptions
                        {
                            Location = new Location {Latitude = 38.637593, Longitude = (-90.270843)},
                            ZoomLevel = zoomLevel,
                            Heading = new Heading {Orientation = orientation}
                        },
          Credentials = new Credentials {Token = token},
          UserProfile = new UserProfile {DeviceType = deviceType}
      };

var metaDataReturn = imageryService.GetImageryMetadata(request);

vb.net:

Dim imageryService As New ImageryServiceClient()

Dim request As New ImageryMetadataRequest()
request.Style = mapStyle
request.Options = New ImageryMetadataOptions()
request.Options.Location = New Location()
request.Options.Location.Latitude = 38.637593
request.Options.Location.Longitude = (-90.270843)
request.Options.ZoomLevel = zoomLevel
request.Options.Heading = New Heading()
request.Options.Heading.Orientation = orientation
request.Credentials = New Credentials()
request.Credentials.Token = token
request.UserProfile = New UserProfile()
request.UserProfile.DeviceType = deviceType

Dim metaDataReturn As ImageryMetadataResponse = imageryService.GetImageryMetadata(request)

Data Fetched but we need a full out URI

We have our data but depending on the source, we’ll have to do some additional transformation.  Depending on if you do a overhead or bird eye view, you’ll have to manipulate the URI to include subdomain, culture, and tile id.  Once again, we’re assuming a fat client application.  For a website, we’d grab the culture information differently just like we did with the IP.

C#:

private static string createBirdEyeUri(string imageUri, int tileId, string[] subdomain, string token)
{
    //{tileId}, {subdomain}, {token}
    if (subdomain.Length > 0)
        imageUri = imageUri.Replace("{tileId}", tileId.ToString()).
            Replace("{subdomain}", subdomain[0]).
            Replace("{token}", token);

    return imageUri;
}

private static string createOverheadUri(string imageUri, string token)
{
    //{culture}, {token}
    imageUri = imageUri.Replace("{culture}", System.Threading.Thread.CurrentThread.CurrentCulture.IetfLanguageTag).
            Replace("{token}", token);

    return imageUri;
}

vb.net:

Private Shared Function createBirdEyeUri(ByVal imageUri As String, ByVal tileId As Integer, ByVal subdomain As String(), ByVal token As String) As String
    '{tileId}, {subdomain}, {token}
    If subdomain.Length > 0 Then
        imageUri = imageUri.Replace("{tileId}", tileId.ToString()).Replace("{subdomain}", subdomain(0)).Replace("{token}", token)
    End If

    Return imageUri
End Function

Private Shared Function createOverheadUri(ByVal imageUri As String, ByVal token As String) As String
    '{culture}, {token}
    imageUri = imageUri.Replace("{culture}", System.Threading.Thread.CurrentThread.CurrentCulture.IetfLanguageTag).Replace("{token}", token)

    Return imageUri
End Function

Laying down the tiles, don’t forget the grout

For this example, we’ll do just bird eye as it is slightly more complex.  We’ll have to create a tile layout and programmatically add in the tiles since we don’t know how many will be there.

c#:

var result = (ImageryMetadataBirdseyeResult)metaDataReturn.Results[0];
var pics = new PictureBox[result.TilesX * result.TilesY];

int currentTop = 0;
int currentLeft = 0;

for (int y = 0; y < result.TilesY; y++)
{
    for (int x = 0; x < result.TilesX; x++)
    {
        int currentIndex = y * result.TilesX + x;
        pics[currentIndex] = new PictureBox
            {
                Top = currentTop,
                Left = currentLeft,
                Width = metaDataReturn.Results[0].ImageSize.Width,
                Height = metaDataReturn.Results[0].ImageSize.Height,
                ImageLocation =
                    createBirdEyeUri(result.ImageUri, currentIndex, result.ImageUriSubdomains, token)
            };

        currentLeft += metaDataReturn.Results[0].ImageSize.Width;
    }
    currentTop += metaDataReturn.Results[0].ImageSize.Height;
    currentLeft = 0;
}

gbImagery.Controls.AddRange(pics);

vb.net:

Dim result As ImageryMetadataBirdseyeResult = DirectCast(metaDataReturn.Results(0), ImageryMetadataBirdseyeResult)
Dim pics As PictureBox() = New PictureBox(result.TilesX * result.TilesY) {}

Dim currentTop As Integer = 0
Dim currentLeft As Integer = 0

Dim y As Integer = 0
While y < result.TilesY
    Dim x As Integer = 0
    While x < result.TilesX
        Dim currentIndex As Integer = y * result.TilesX + x
        pics(currentIndex) = New PictureBox()
        pics(currentIndex).Top = currentTop
        pics(currentIndex).Left = currentLeft
        pics(currentIndex).Width = metaDataReturn.Results(0).ImageSize.Width
        pics(currentIndex).Height = metaDataReturn.Results(0).ImageSize.Height
        pics(currentIndex).ImageLocation = createBirdEyeUri(result.ImageUri, currentIndex, result.ImageUriSubdomains, token)


        currentLeft += metaDataReturn.Results(0).ImageSize.Width
        System.Math.Max(System.Threading.Interlocked.Increment(x),x - 1)
    End While
    currentTop += metaDataReturn.Results(0).ImageSize.Height
    currentLeft = 0
    System.Math.Max(System.Threading.Interlocked.Increment(y),y - 1)
End While

gbImagery.Controls.AddRange(pics)

Go Do’s!

For more information and examples about Virtual Earth and their services, visit the Virtual Earth blog on MSDN.  You have the ability to play with geographical, routing, and searching data on top of just the extreme high resolution earth data.

You can download my source for this over at Peace Love Code.  The solution has both VB and C#.  All you need to do is get an account for the account information needed.

Deploying a finished application to a Windows Mobile Device

So you’ve gotten your toy all prepped and ready to rock.  Great.  You can test out the application on as I outlined on the physical device but the issue is as soon as you untether the device, BAM, the application exits.

For me and the mass amounts of rocking requires an install for my device, so how do we do that?  MSDN has a nice write up for deploying Windows Mobile applications but I’ll walk through my deployment for you too.

We’ll right click on the solution in the solution explorer or we can go to File->New->Project.  If you do the file menu route, you’ll have a screen that is slightly different than mine.  At the bottom you’ll have a drop down menu where you’ll want to change it from “Create a new solution” to “Add to Solution”.

image

Since I want a CAB install, I’ll go to the “Other Project Types” then to “Setup and Deployment” and select a Smart Device CAB Project”.

From there, right click on “Application Folder” and go to “Add”.  Select “Primary Output” and hit OK.

image

So with the output added in now, we’ll need to do some more magic.  In the “File System” window, we’ll right-click on “File System on Target Machine” and go “Add Special Folder” then to “Start Menu Folder”.

Now that we have our start menu, we’ll go back to the “Application Menu” and right click on the “Primary output from Cowbell”.  Select “Create Shortcut” and drag that shortcut to your start menu folder.  I suggest you rename it too.

Then you can build it, drag the CAB over and install on your device.

Excuse me while I go use my HTC Diamond as a cow bell now.

Why I think c# is better than VB – Part 2

So as expected, Alfred responded to my first post on why I think c# is better.  As always, I will point out I openly respect people’s opinion for programming languages and that each language has excellent uses for different things.

So with the disclaimer out of the way so I won’t have an angry mob attacking me, on to why I love c# so much.

Alfred brought up the need for the semi colon to terminate a line.  I think this the best idea ever.  We do it in our written, why is it such a big deal?  More so, I can do this:

updated after Erik yelled at me to provide a more legit example:

isUserPrepped = (angle < safeAngle && angle > -safeAngle)
    && (gyro < safeGyro && gyro > -safeGyro)
    && userIsOnBoard;

while VB has to do this:

isUserPrepped = (angle < safeAngle AndAlso angle > -safeAngle) _
    AndAlso (gyro < safeGyro AndAlso gyro > -safeGyro) _
    AndAlso userIsOnBoard

I can have 1 line of code expand out multiple physical lines.  VB I have to use a _ to say it is an extra line!  That is an extra thing I have to worry about when coding with meaningful variable names (sorry, X, Y, and Z only count if I’m doing something in 3D).

As Jeff pointed out in a comment also, C style languages allow me to do this

functionFoo(); functionBar(value); stringBuilder.Append("Clint Rutkas is awesome");

while in VB it would be this:

functionFoo()
functionBar(value)
stringBuilder.Append("Clint Rutkas is awesome")

This is a poor example due to the functions, but there are multiple times where you’ll want everything on one line.

Another great feature of c# is how close it is to other languages.  If you run across something in Java, C, C++, Perl, PHP or JavaScript, there is an excellent chance you can reverse engineer what they did with very little effort.  With VB, unless it is c# where you’ll be able to use a transcoder, you have to manually go and do a line by line correction.  This could directly lead to introducing an error in the code!

Alfred VS Clint: C# versus VB debate

Disclaimer:  I respect everyone’s right to use their own programming language.  I also respect the accomplishments of certain languages and realize not one language can suite all the needs in the world.  That is why we have functional languages and SQL and so forth.  This is my opinion and my opinion only.

Sir Alfred has challenged me to a nerd off. On twitter, (http://twitter.com/crutkas), I smarted off about my love for C style languages and my coworker, Alfred Thompson(http://blogs.msdn.com/alfredth/), responded in his always well thought out, respectful manner disagreeing with me.

Long story short, I ran into a code translation issue that caused a CLS error when I translated working, non-erroring C# to VB.Net and it got me worked up again on why I dislike VB.

So let us go into why C style languages are the bomb.  Here is some c#

void foo()
{
    if(bar() == 10)
    {
        return;
    }
    else
        doMoreWork();
}
VB:
Sub foo()
    If bar() = 10 Then
        Return
    Else
        doMoreWork()
    End If
End Sub

Very quickly looking at this pseudo code, we know clearly where it ends and starts due to brackets.  If I wanted to make the code tighter, I can move the brackets up (this is another coding style war debate).  I don’t need the brackets if I choose to in the example of doMoreWork().  My source isn’t cluttered with “EndFor” and “EndIf”.  I see a bracket, I know I’m at the end.  No “if () then endif”. 

By enforcing things like parenthesis for if statements, you get a clear reading for what is inside.  I know (bar() == 10) is the whole statement.  I can’t misread it since I know that there will be a ) then a {.

Also, by being more “airy”, I can read it quicker.  It doesn’t read like the Tale of Two Cities.  I can skim and poke, rather than put on my reading glasses and think “it was the best of times, it was the worst of times” and then want to fall asleep no mater how many Dr. Peppers I drank.

When I do run across VB only examples, I transcode them using a few different converters.  Right now I think the Telerik convertor is one of the better ones out there.  http://converter.telerik.com/

While I know Alfred will respond since he won’t let a young whipper snapper like myself off the hook but if anyone does agree or disagree, feel free to comment.

Excel trick for parsing email

A friend of mine for work needed a way to parse out a domain name from a user’s email address in Excel.  Normally I would have used Regular Expressions to do this but I couldn’t easily find them so I did the old school string find / substring route.  Expensive but effective.

So if I have lets say my Microsoft email address of crutkas@microsoft.com in the excel spreadsheet at row A1, I can do some neat stuff like the SEARCH and MID commands.

Lets define exactly what this nifty commands do.

  • MID(text, start_index, num_characters): returns the characters from the middle of a text string, given a starting position and length
  • SEARCH(find_text, within_text, start_index):  returns the index of the character for the first instance of the desired text reading from left to right.  This is not a case-sensitive search.

So to get “crutkas” returned, we’ll do this:

=MID(A1, 1, SEARCH("@", A1) - 1)

Excel’s index starts at 1 along with we need to subtract 1 to remove the @ sign.  Now lets go for the microsoft.com result.

This command is a bit longer since we need to start off at the @ symbol and move back.

=MID(A1, SEARCH("@", A1)  + 1, LEN(A1) - SEARCH("@", A1) )

So we get the length of the string and subtract where we find the @ symbol.

Simple, right.

Deploying an application directly to your phone

An emulator can only do so much.  Now we’d want to directly deploy to our cell phone instead of the emulator.

Jon Box has a nice write up on doing just this.  At first, I was all like, “whatever, I do what I want” (complete with Cartman voice) and attempted to launch it without following his steps and lost.  It was worse than losing a match of Slap Face (a new Olympic sport for the Fall along with air hockey).

Here is the option:

image

But first, as Jon pointed out, you need to do some certs and stuff on the phone.

Connect the device via ActiveSync or Windows Mobile Device Center (Vista). Sorry for the obvious.

  1. Copy the Certs.CAB file (C:\Program Files\Windows Mobile 6 SDK\Tools\Security\SDK Development Certificates) into the Temp folder on the phone, I typically use my memcard.
  2. Copy the SecurityOff.CPF (C:\Program Files\Windows Mobile 6 SDK\Tools\Smartphone\SecurityConfiguration) into the Temp folder on the phone.
  3. On the phone, click on the Certs.CAB to launch install from your File Explorer.
  4. Repeat with SecurityOff.CPF
  5. Reboot the device (you seriously need to do this, I did verify).
  6. Select “Windows Mobile 6 Standard Device” from deployment menu (see picture at the top of this blog post).
  7. Test to verify

Yup, super easy.