Use New Bing Maps Road Imagery In Silverlight Map Control (Unofficially and Unsupported)

19. August 2010

NewBingMapsRoadImageryFromSilverlight Recently the consumer facing Bing Maps site changed the map imagery that is displayed for the Road map mode. This map imagery has not been officially made available for Bing Maps for Enterprise developers to use within their applications. The imagery is only officially available to the Bing Maps consumer website. Unofficially, you can still access the imagery if you know the URL format to get it. Below is an example of using the URL format for the new imagery to display it within the Bing Maps Silverlight Control.

I have not seen anything official from Microsoft on this, so I assume that doing this is against their terms of use. You assume any and all responsibility in violating their terms of use if you use the below code. Sorry for the disclaimer, but I don’t want you to tell Microsoft I told you it was ok. Basically, do not use this in a production application, unless you get consent from Microsoft.

Now the code…

Displaying the Map (XAML):

<UserControl x:Class="Silverlight_NewBingMapsRoadImagery_2010.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Silverlight_NewBingMapsRoadImagery_2010"
xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
xmlns:mCore="clr-namespace:Microsoft.Maps.MapControl.Core;assembly=Microsoft.Maps.MapControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">

<m:Map NavigationVisibility="Collapsed">
<m:Map.Mode>
<!-- Do Not Display Default Bing Maps Imagery -->
<mCore:MercatorMode />
</m:Map.Mode>
<m:Map.Children>
<m:MapTileLayer>
<m:MapTileLayer.TileSources>
<!-- Show the new Bing Maps imagery -->
<local:NewBingMapsTileSource />
</m:MapTileLayer.TileSources>
</m:MapTileLayer>
</m:Map.Children>
</m:Map>

</Grid>
</UserControl>

NewBingMapTileSource (C#):

public class NewBingMapsTileSource : Microsoft.Maps.MapControl.TileSource
{
public NewBingMapsTileSource()
: base("{UriScheme}://ecn.t{subdomain}.tiles.virtualearth.net/tiles/r{quadkey}?g=530&mkt=EN-US&lbl=l1&stl=h&shading=hill&n=z")
{ }
}

Bing Maps, C#, Silverlight , ,

Loading Microsoft CDN Hosted OpenStreetMap Imagery in Silverlight Bing Maps Control

19. August 2010

MSHostedOpenStreetMapImageryFromSilverlight Recently a new Bing Maps App for OpenStreetMap (OSM) was released. This new feature of the Bing Maps consumer facing website is hosting the OpenStreetMap imagery using the Azure CDN, rather than relying on OpenStreetMap’s server. There has been some question as to whether developers can use the Microsoft hosted OpenStreetMap imagery within their own applications.

I have not seen anything official from Microsoft on this, so I assume that doing this is against their terms of use. You assume any and all responsibility in violating their terms of use if you use the below code. Sorry for the disclaimer, but I don’t want you to tell Microsoft I told you it was ok. Basically, do not use this in a production application, unless you get consent from Microsoft.

Now the code…

Displaying the Map (XAML):

<UserControl x:Class="Silverlight_NewBingMapsRoadImagery_2010.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Silverlight_NewBingMapsRoadImagery_2010"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    xmlns:mCore="clr-namespace:Microsoft.Maps.MapControl.Core;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">

        <m:Map NavigationVisibility="Collapsed">
            <m:Map.Mode>
                <!-- Do Not Display Default Bing Maps Imagery -->
                <mCore:MercatorMode />
            </m:Map.Mode>
            <m:Map.Children>
                <m:MapTileLayer>
                    <m:MapTileLayer.TileSources>
                        <!-- Display OpenStreetMap Imagery -->
                        <local:OpenStreetMapTileSource />
                    </m:MapTileLayer.TileSources>
                </m:MapTileLayer>
            </m:Map.Children>
        </m:Map>
        
    </Grid>
</UserControl>

OpenStreetMapTileSource (C#):

public class OpenStreetMapTileSource : Microsoft.Maps.MapControl.TileSource
{
    public OpenStreetMapTileSource()
        : base()
    {
        this.UriFormat = "{UriScheme}://{3}.osm.virtualearth.net/{2}/{0}/{1}.png"
            .Replace("{UriScheme}", OpenStreetMapTileSource.PageUriScheme); // <-- set "http" or "https" appropriately
    }

    public override System.Uri GetUri(int x, int y, int zoomLevel)
    {
        // Randomly decide which server to use
        string server;
        var rnd = new Random();
        var i = rnd.Next(0, 2);
        switch (i)
        {
            case 1:
                server = "b";
                break;
            case 2:
                server = "c";
                break;
            default:
                server = "a";
                break;
        }

        // Format Uri for the desired map tile image
        return new Uri(string.Format(this.UriFormat, x, y, zoomLevel, server));
    }

    internal static string PageUriScheme
    {
        get
        {
            if ((HtmlPage.IsEnabled && (HtmlPage.Document != null)) && (HtmlPage.Document.DocumentUri != null))
            {
                return HtmlPage.Document.DocumentUri.Scheme;
            }
            return "HTTP";
        }
    }
}

Bing Maps, C#, Silverlight , ,

Silverlight Bing Maps: Draw Circle Around a Latitude/Longitude Location

28. June 2010

SLBingMaps_DrawCircles Over 2 years ago I posted an example of how to draw circles on the Bing Maps JavaScript control. I thought it was about time to update that post to demonstrate how to do this using the Bing Maps Silverlight control. This is basically a C# port of the original JavaScript code.

Sometimes this can be really useful, but it isn’t built into the Sivleright Bing Maps Control. So you need to implement it yourself in order to do it. However, instead of writing from scratch, feel free to copy the code from this post.

Download Full Source: SLBingMaps_DrawCircle.zip

 

 

Here’s example usage:

// Draw circle at the location where the user clicked
private void Map_MouseClick(object sender, Microsoft.Maps.MapControl.MapMouseEventArgs e)
{
// Get the location the mouse clicked
var mousePoint = e.ViewportPoint;
var clickedLocation = MainMap.ViewportPointToLocation(mousePoint);

// Calculate the points to make up a circle with radius of 200 miles
var locations = GeoCodeCalc.CreateCircle(clickedLocation, 200, DistanceMeasure.Miles);

// Add Red Polyline to the Map
var poly = new MapPolyline();
poly.Locations = locations;
poly.Stroke = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
poly.StrokeThickness = 2;
MainMap.Children.Add(poly);
}

 

And, here’s the code that calculates the location points to make up the circle:

/*
* Written by Chris Pietschmann (http://pietschsoft.com)
* This code is derived from the code posted in the following location:
* http://pietschsoft.com/post/2010/03/02/Bing-Maps-JS-Calculate-Area-of-Circle-and-Draw-Circle-on-Map.aspx
*/
using System;
using System.Collections.Generic;
using Microsoft.Maps.MapControl;

namespace SLBingMaps_DrawCircle
{
public enum DistanceMeasure
{
Miles,
Kilometers
}

public class GeoCodeCalc
{
public const double EarthRadiusInMiles = 3956.0;
public const double EarthRadiusInKilometers = 6367.0;

public static double ToRadian(double degrees)
{
return degrees * (Math.PI / 180);
}

public static double ToDegrees(double radians)
{
return radians * (180 / Math.PI);
}

public static LocationCollection CreateCircle(Location center, double radius, DistanceMeasure units)
{
var earthRadius = (units == DistanceMeasure.Miles ? GeoCodeCalc.EarthRadiusInMiles : GeoCodeCalc.EarthRadiusInKilometers);
var lat = ToRadian(center.Latitude); //radians
var lng = ToRadian(center.Longitude); //radians
var d = radius / earthRadius; // d = angular distance covered on earth's surface
var locations = new LocationCollection();

for (var x = 0; x <= 360; x++)
{
var brng = ToRadian(x);
var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
var lngRadians = lng + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));

locations.Add(new Location(ToDegrees(latRadians), ToDegrees(lngRadians)));
}

return locations;
}
}
}

Bing Maps, Silverlight , ,

Display Google Maps Imagery using Bing Maps Silverlight Control

14. June 2010

I’ve gotten a couple emails asking how to show Google Maps imagery using the Bing Maps Silverlight Control. I previously post how to display OpenStreetMaps and Yahoo maps imagery using the control, but is it possible to show Google Maps imagery too?? Yes, absolutely; well technically, but the Google Maps Terms of Use does prohibit it. Actually, the Google Maps Terms of Use prohibits the direct access of the map tile images, and does not specifically prohibit using them with the Bing Maps Silverlight Control.

SLBingMapsControl_GoogleMapsImagery

You may only continue reading if you are willing to break the Google Maps Terms of Use, and see how similar the two mapping platforms really are.

Remember the below code most likely violates the Google Maps Terms of Use, and probably shouldn’t be used in a production system; unless of course you really don’t mind breaking the terms of use.

The below code follows suite after the code I posted a while back on displaying OpenStreetMap and Yahoo Maps imagery within the Bing Maps Silverlight Control.

First, here’s some XAML usage samples using the Google Maps TIle Source classes listed further down. The first one is showing the Google Maps Road/Terrain imagery, the second shows the Google Maps Aerial/Satellite imagery:

<!-- Display Google Maps Road/Terrain Imagery -->
<m:Map CopyrightVisibility="Collapsed" LogoVisibility="Collapsed">
<m:Map.Mode>
<!-- Do Not Display Bing Maps Imagery -->
<mCore:MercatorMode></mCore:MercatorMode>
</m:Map.Mode>
<m:Map.Children>
<m:MapTileLayer>
<m:MapTileLayer.TileSources>
<local:GoogleMapsRoadTileSource></local:GoogleMapsRoadTileSource>
</m:MapTileLayer.TileSources>
</m:MapTileLayer>
</m:Map.Children>
</m:Map>

<!-- Display Google Maps Aerial/Satellite Imagery with Labels -->
<m:Map CopyrightVisibility="Collapsed" LogoVisibility="Collapsed">
<m:Map.Mode>
<!-- Do Not Display Bing Maps Imagery -->
<mCore:MercatorMode></mCore:MercatorMode>
</m:Map.Mode>
<m:Map.Children>
<m:MapTileLayer>
<m:MapTileLayer.TileSources>
<local:GoogleMapsAerialTileSource></local:GoogleMapsAerialTileSource>
<local:GoogleMapsLabelsTileSource></local:GoogleMapsLabelsTileSource>
</m:MapTileLayer.TileSources>
</m:MapTileLayer>
</m:Map.Children>
</m:Map>

 

If you notice, the second example showing the usage of the Google Maps Aerial/Satellite imagery is displaying 2 tile sources. This is because Google Maps has separate tile sources for both the Aerial/Satellite imagery and the Labels. This example shows the Labels tile source over the top of the Aerial/Satellite tile source to give the desired effect of “Aerial with Labels.”

Now, here’s the main attraction of this article; the actual Google Maps TileSource objects referred to above:

public class GoogleMapsRoadTileSource : GoogleMapsTileSourceBase
{
public GoogleMapsRoadTileSource()
: base("http://mt{0}.google.com/vt/lyrs=m@128&hl=en&x={1}&y={2}&z={3}&s=")
{ }
}

public class GoogleMapsAerialTileSource : GoogleMapsTileSourceBase
{
public GoogleMapsAerialTileSource()
: base("http://khm{0}.google.com/kh/v=62&x={1}&y={2}&z={3}&s=")
{ }
}

public class GoogleMapsLabelsTileSource : GoogleMapsTileSourceBase
{
public GoogleMapsLabelsTileSource()
: base("http://mt{0}.google.com/vt/lyrs=h@128&hl=en&x={1}&y={2}&z={3}&s=")
{ }
}

public abstract class GoogleMapsTileSourceBase : Microsoft.Maps.MapControl.TileSource
{
public GoogleMapsTileSourceBase(string uriFormat)
: base(uriFormat)
{ }

public override System.Uri GetUri(int x, int y, int zoomLevel)
{
return new Uri(string.Format(this.UriFormat, new Random().Next() % 4, x, y, zoomLevel));
}
}

 

Disclaimer: Use the above posted code at your own risk. The usage of it may be in violation of the Google Maps Terms of Use.

Bing Maps, Silverlight , ,

Resizing and Auto-Scaling Pushpin in Bing Maps Silverlight

4. June 2010

Plotting pushpins on the Bing Maps Silverlight control is really simple when using the Pushpin control that comes with the control. But, what if you want to change the size of the Pushpin? It doesn't work to just change the Pushpin.Height and Pushpin.Width properties. This is actually because those properties pertain to the controls Content property. So, how exactly do you go about changing the size of the Pushpin if the Height and Width properties don't work?

Use ScaleTransform to Resize Pushpins

The answer is simple. All you need to do is use a ScaleTransform. Below is a couple examples, one making the pushpin smaller and the other bigger.

<m:Pushpin Location="0,-15">
    <m:Pushpin.RenderTransform>
        <ScaleTransform ScaleX=".5" ScaleY=".5" CenterX="17" CenterY="35"></ScaleTransform>
    </m:Pushpin.RenderTransform>
</m:Pushpin>
<m:Pushpin Location="0,15">
    <m:Pushpin.RenderTransform>
        <ScaleTransform ScaleX="2" ScaleY="2" CenterX="17" CenterY="35"></ScaleTransform>
    </m:Pushpin.RenderTransform>
</m:Pushpin>

You may have noticed that not only does the above example set the ScaleX and ScaleY properties of the ScaleTransform, but the CenterX and CenterY are also set. This is because if we don't set the CenterX and CenterY appropriately the Pushpin will get "moved" from its correct location when the Transform is performed.

How did I decide to set CenterX to 17 and CenterY to 35? Well, the Pushpins "default" size sets the Width equal to 34 and Height to 35, and the PositionOrigin to BottomCenter. Since the PositionOrigin is set to BottomCenter, in order to get the pushpin to stay in its correct position on the map we need to set the CenterX to half the Width or 17, and CenterY to the Height or 35.

The image to the right shows an example of this in action.

Auto Scale Pushpin with Map Zoom Level

A neat feature that becomes available with the Bing Maps Silverlight control once you start using ScaleTransforms to modify the size of Pushpins is the ability to change the size of the Pushpins when the Map Zoom Level is changed. All it takes is a little help from a custom IValueConverter and a little Data Binding.

The image to the right shows this in action. Both sides of the image were not resized at all, the difference in size of the Pushpins is due to the usage of the following code.

First, we'll add some Pushpins to a Map and set the Pushpins RenderTransform property binding to bind it to the Map's ZoomLevel property and set it to use a custom converter.

<m:Map Height="200" x:Name="myMap">
    <m:Map.Children>
        <m:Pushpin Location="0,-15"
            RenderTransform="{Binding ZoomLevel, ElementName=myMap, Converter={StaticResource PushpinScaleTransform}}"></m:Pushpin>
        <m:Pushpin Location="0,15"
            RenderTransform="{Binding ZoomLevel, ElementName=myMap, Converter={StaticResource PushpinScaleTransform}}"></m:Pushpin>
    </m:Map.Children>
</m:Map>

Next, make sure you declare the PushpinScaleTransform static resource in the UserControl, Page or App. Also, don't forget to include the namespace, in the below example I named it "local".

<UserControl.Resources>
    <local:PushpinScaleTransform x:Key="PushpinScaleTransform"></local:PushpinScaleTransform>

</UserControl.Resources>

Finally, add the PushpinScaleTransform custom IValueConverter object to the project. This custom IValueCoverter will recieve the ZoomLevel and "convert" it to a ScaleTransform with ScaleX, ScaleY and CenterX, CenterY properties set appropriately to Auto Scale the Pushpin with the Map ZoomLevel. Below is an example of this that I found works quite well.

using System;
using System.Windows.Data;
using System.Windows.Media;

namespace SLBingMapsScalePushpins
{
    public class PushpinScaleTransform : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double currentZoomLevel = (double)value;

            // Calculate the scale to use. This is just a simple algorithm that
            // I found works nicely.
            var scaleVal = (0.05 * (currentZoomLevel + 1)) + 0.3;

            var transform = new ScaleTransform();
            transform.ScaleX = scaleVal;
            transform.ScaleY = scaleVal;

            // Set the transform center X and Y so the Pushpin stays at the correct location.
            // The Default Pushpin's height is 35 and width is 34
            // Since the Default Pushpin's PositionOrigin is set to BottomCenter, we need to
            // set the CenterX to half the width (17), and CenterY to the height (35).
            transform.CenterX = 17;
            transform.CenterY = 35;

            return transform;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

Download Code

Here's a link to download a small Silverlight project that implements the above code examples.

SLBingMapsScalePushpins.zip (446.52 kb)

Bing Maps, Silverlight ,

Draggable Pushpins using Bing Maps Silverlight Control

30. May 2010

Using a map to visualize data within an application is great, but you must first get the location of the data to be displayed. If you have the address you can geocode it using the Bing Maps Web Services, but "What if you can't geocode it?" Or, "What if the geocoding can't find the address?" Well, if your user knows where the location is, then you can have them point it out by clicking on the map. Creating Pushpins in response to a users click is nice, but wouldn't it be even nicer if they could "Click and Drag" the Pushpin around to define/edit/change the location of the data entity?

I have even seen this discussed a bit in regards to the Bing Maps Silverlight Control, and it isn't something that is built into the map control directly. However it isn't too difficult to implement, if you know what to do. So I decided to create and post a simple "DraggablePushpin" object deriving from the "Microsoft.Maps.MapControl.Pushpin" object that implements Dragging in a nice, self contained fashion. There's no need to wire up any events, you simple add a "DraggablePushpin" to you Map, and the user can drag it around.

Here's the code for the "DraggablePushpin":

public class DraggablePushpin : Microsoft.Maps.MapControl.Pushpin
{
    private bool isDragging = false;
    EventHandler<MapMouseDragEventArgs> ParentMapMousePanHandler;
    MouseButtonEventHandler ParentMapMouseLeftButtonUpHandler;
    MouseEventHandler ParentMapMouseMoveHandler;

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        // Check if the Map Event Handlers have been created/attached to the Map
        // If not, then attach them. This is done in the "Pushpin.OnMouseLeftButtonDown"
        // event because we don't know when the Pushpin is added to a Map or MapLayer, but
        // we do konw that when this event is fired the Pushpin will already have been added.
        var parentLayer = this.Parent as MapLayer;
        if (parentLayer != null)
        {
            var parentMap = parentLayer.ParentMap;
            if (parentMap != null)
            {
                if (this.ParentMapMousePanHandler == null)
                {
                    this.ParentMapMousePanHandler = new EventHandler<MapMouseDragEventArgs>(ParentMap_MousePan);
                    parentMap.MousePan += this.ParentMapMousePanHandler;
                }
                if (this.ParentMapMouseLeftButtonUpHandler == null)
                {
                    this.ParentMapMouseLeftButtonUpHandler = new MouseButtonEventHandler(ParentMap_MouseLeftButtonUp);
                    parentMap.MouseLeftButtonUp += this.ParentMapMouseLeftButtonUpHandler;
                }
                if (this.ParentMapMouseMoveHandler == null)
                {
                    this.ParentMapMouseMoveHandler = new MouseEventHandler(ParentMap_MouseMove);
                    parentMap.MouseMove += this.ParentMapMouseMoveHandler;
                }
            }
        }

        // Enable Dragging
        this.isDragging = true;

        base.OnMouseLeftButtonDown(e);
    }

    #region "Mouse Event Handler Methods"

    void ParentMap_MousePan(object sender, MapMouseDragEventArgs e)
    {
        // If the Pushpin is being dragged, specify that the Map's MousePan
        // event is handled. This is to suppress the Panning of the Map that
        // is done when the mouse drags the map.
        if (this.isDragging)
        {
            e.Handled = true;
        }
    }

    void ParentMap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        // Left Mouse Button released, stop dragging the Pushpin
        this.isDragging = false;
    }

    void ParentMap_MouseMove(object sender, MouseEventArgs e)
    {
        var map = sender as Microsoft.Maps.MapControl.Map;
        // Check if the user is currently dragging the Pushpin
        if (this.isDragging)
        {
            // If so, the Move the Pushpin to where the Mouse is.
            var mouseMapPosition = e.GetPosition(map);
            var mouseGeocode = map.ViewportPointToLocation(mouseMapPosition);
            this.Location = mouseGeocode;
        }
    }

    #endregion
}

Bing Maps, Silverlight

Awarded 2010 Microsoft MVP - Windows Live Platform

1. April 2010

Microsoft Most Valuable Professional Award"Congratulations! We are pleased to present you with the 2010 Microsoft® MVP Award!"

I'm happy to announce that I've been awarded the Microsoft MVP award for the third year in a row. Just like last year, I've been awarded within the "Windows Live Platform" category. The "Windows Live Platform" category includes the Bing Maps SDK (both JavaScript and Silverlight Map Controls) which is the product/technology that I work with and help out others with the most in this category.

Congratulations to all other April MVP's, both new and renewed!

If you're not familiar with what the Microsoft MVP Program is you can find more information about it here: http://mvp.support.microsoft.com/gp/mvpawardintro

Bing Maps, General

Bing Maps JS: Calculate Area of Circle and Draw Circle on Map

2. March 2010

Something that can be usefull at times in being able to calculate the total Area of a circle, especially when plotting it on a map. So, I decided to slightly modify my "Draw a Circle Radius Around Lat/Lng Point" to make it also calculate the Area of the circles and display that value within the TItle of the Circle Shapes Pushpin.

Remember, back in Trig class, it's fairly simple to calculate the area of a circle. You just Multiply Pi by the Radius Squared.

Area = Pi * (radius * radius)

Anyway, I know this isn't a very advanced math problem. If fact it's much simpler than calculating out the point that make up the circle in the first place. However, I thought I'd post it since it may be usefull to someone that maybe doesn't quite remember this little trick from Trig class.

Here's the full, modified source code that contains this:

DrawRadiusCalcArea_JS_BingMaps.zip (3.28 kb)

Bing Maps , ,

Community Coding Contest 2010 - Looking for Input and Prizes

4. January 2010

Now that it's 2010, and over a year since the first Community Coding Contest came to an end, I'm thinking about running the contest again in 2010.

Last time it ran for 3 months and we had 6 really great entries. This time around I'm thinking that it may be better to accept entries for 6 months, and have the voting run for a full month. This will give much more time for entries to be submitted, and for people to work on their entries before submitting and/or voting begins.

These are just some initial thoughts, and I'm going to need input on everything from YOU (the community) before I can make a final decision as to when and how to run the contest again. I'd really appreciate if you could fill out the following survey and tell us what you think.

Community Coding Contest 2010 Pre-Contest Survey

Also, we will need some prizes donated before the contest will be able to go on. If you or your company are interested in donating prizes or monetary support (web hosting fees, mailing expenses, etc.) for the contest, please contact the contest directly here: http://communitycodingcontest.org/contact.aspx

You can view the official contest website here: http://communitycodingcontest.org

Thanks, and I look forward to hearing more about what everyone thinks!

asp.net, ASP.NET MVC, Bing Maps, C#, Silverlight, vb.net , , , ,

Prototype of OpenStreetMap Silverlight Control using DeepEarth and Bing Maps SDK

13. November 2009

I’ve decided to expand a little on using OpenStreetMap imagery with the new Bing Maps Silverlight Control in response to the following comment posted by John O’Brien on my previous “Display OpenStreetMap Imagery using Bing Maps Silverlight Control v1” post:

“Very close Chris but you will still need to enter a Bing Maps AppID.
If however you create your own map from MapCore and don't use the Bing Maps services then you don't need creditials”

Yes, it is true that by just displaying the OpenStreetMap imagery on the Bing Maps Silverlight Control using a custom TileSource you still need to provide the control a Bing Maps Key (App ID). However, what if you inherited from the “MapCore” base class (the same one that the Bing Maps “Map” object inherits) and built out a full OpenStreetMap Map control?

Custom “OpenStreetMap” Control

I built out a test “OpenStreetMap” object that inherits from “MapCore” that automatically sets the Map Mode to a “OpenStreetMapMode” object that loads up the OpenStreetMap imagery automatically. This was some simple code to write, basically just extending only a little bit on top of what I posted in the previous post.

Here’s the code for the “OpenStreetMap”, “OpenStreetMapMode” and “OpenStreetMapTileSource” objects:

using System;
using Microsoft.Maps.MapControl;
using Microsoft.Maps.MapControl.Core;

namespace SilverlightApplication1
{
    public class OpenStreetMap : MapCore
    {
        public OpenStreetMap()
            : base()
        {
            this.Mode.SetView(new Location(), 2.0, 0.0, 0.0, false);
            this.Mode = new OpenStreetMapMode();
        }
    }

    public class OpenStreetMapMode : RoadMode
    {
        public OpenStreetMapMode()
            : base()
        {
            var tileLayer = (MapTileLayer)this.Content;
            tileLayer.TileSources.Clear();
            tileLayer.TileSources.Add(new OpenStreetMapTileSource());
        }
    }

    public class OpenStreetMapTileSource : TileSource
    {
        public OpenStreetMapTileSource()
            : base("http://tile.openstreetmap.org/{2}/{0}/{1}.png")
        {
        }

        public override System.Uri GetUri(int x, int y, int zoomLevel)
        {
            return new Uri(string.Format(this.UriFormat, x, y, zoomLevel));
        }
    }
}

 

And, here’s an example of using this new “OpenStreetMap” control:

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:map="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    xmlns:local="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <local:OpenStreetMap Name="map">
            <local:OpenStreetMap.Children>
                <map:MapLayer>
                    <Image Source="BluePin.png" Opacity="0.8" Stretch="None" map:MapLayer.Position="42.16, -95"></Image>
                </map:MapLayer>
            </local:OpenStreetMap.Children>
        </local:OpenStreetMap>
    </Grid>
</UserControl>

 

What About Navigation Controls?

One side effect of creating this completely custom Map Control based off MapCore is that the other controls that the “Map” control includes automatically do not get displayed.

The “Map” control displays the “MapControlNavigationBar” control via the “MapForeground” object, however due to the MapForeground constructor that lets you tell it what MapBase object to attach to being marked “internal” is seems that it can’t be easily reused.

However, with a little help from the newest controls in developments within the DeepEarth project, you can fairly simply modify the Bing Maps Navigation control within the DeepEarth project to be used with the new “OpenStreetMap” control.

Here’s the code I worked up for a DeepEarth NavigationPanel for the “OpenStreetMap” control created earlier:

using System;
using System.Windows;
using DeepEarth.Client.BingMaps.Convertors;
using DeepEarth.Client.Common;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using Microsoft.Maps.MapControl;
using Point = System.Windows.Point;
using Microsoft.Maps.MapControl.Core;

namespace NewDeepEarth.NavigationPanel
{
    public class NavigationPanel : DeepEarth.Client.Controls.NavigationPanel.NavigationPanel, IMapControl<MapCore>
    {
        private string mapName;

        public NavigationPanel()
        {
            Loaded += CoordinatePanel_Loaded;
        }

        public override double ZoomLevel
        {
            get
            {
                if (MapInstance != null)
                {
                    return MapInstance.ZoomLevel;
                }
                return 0;
            }
            set
            {
                if (MapInstance != null)
                {
                    MapInstance.ZoomLevel = value;
                }
            }
        }

        public override ICoordinate Center
        {
            get
            {
                if (MapInstance != null)
                {
                    return CoordinateConvertor.Convert(MapInstance.Center);
                }
                return new Coordinate(0, 0);
            }
            set
            {
                if (MapInstance != null)
                {
                    MapInstance.Center = CoordinateConvertor.ConvertBack(value);
                }
            }
        }

        #region IMapControl<MapCore> Members

        public string MapName
        {
            get { return mapName; }
            set
            {
                mapName = value;
                setMapInstance(MapName);
            }
        }

        public MapCore MapInstance { get; set; }

        public new void Dispose()
        {
            MapInstance = null;
            base.Dispose();
        }

        public override void PanMap(int deltaX, int deltaY)
        {
            if (MapInstance != null)
            {
                NewDeepEarth.Client.BingMaps.Utilities.Pan(deltaX, deltaY, MapInstance);
            }
        }

        #endregion

        private void CoordinatePanel_Loaded(object sender, RoutedEventArgs e)
        {
            setMapInstance(MapName);
        }

        private void setMapInstance(string mapname)
        {
            MapInstance = Utilities.FindVisualChildByName<MapCore>(Application.Current.RootVisual, mapname);
        }

    }
}

Also, hers a small utility method that I needed to modify slightly within the DeepEarth project to get the above NavigationPanel to work:

using Microsoft.Maps.MapControl.Core;
using Point = System.Windows.Point;

namespace NewDeepEarth.Client.BingMaps
{
    public static class Utilities
    {
        // Convert to accept Bing Maps "MapCore" instead of "Map" object
        public static void Pan(double deltaX, double deltaY, MapCore map) //Map map)
        {
            Point center = map.LocationToViewportPoint(map.Center);
            center.X = center.X + deltaX;
            center.Y = center.Y + deltaY;
            map.Center = map.ViewportPointToLocation(center);
        }
    }
}

 

Suggestion for the DeepEarth Preview Controls

One big suggestion I have for the DeepEarth projects new Preview Controls is to make the Bing Maps objects/libraries within the project use/accept “MapCore” or “MapBase” where ever it can instead of “Map”. This way as much of the DeepEarth code can be reused, without modification, when building a completely custom Map Control using the Bing Maps Silverlight SDK.

A perfect example of this are the modifications I needed to make to the NavigationPanel object and Utilities.Pan method posted above. By simply changing them from referencing/using “Map” to “MapCore” instead, they can not be used with any Map Control built using the Bing Maps Silverlight SDK.

Conclusion

Even though the above code works, it’s really just a prototype of what can be done using the current Preview controls within the DeepEarth project along with the new Bing Maps Silverlight Control. There is definitely some cool stuff to be done with both the Bing Maps Silverlight SDK and the DeepEarth project!

Anyway, here’s a download link to the full code of the project for the above code:

 

Bing Maps, C#, Silverlight , , , ,