Ability for Map to bind to a Location and/or a Route

4 posts, 0 answers
  1. Alex
    Alex avatar
    10 posts
    Member since:
    Nov 2010

    Posted 30 Jun 2011 Link to this post

    I'm using MVVM in my project and have a datagrid with multiple Locatios (Lat/Lon) and also has trips with multiple points (various Lat/Lon points for a MapPolyline). All with "MapIt" buttons for when the user wants to either see where something happened or what route they took on the map.

    I'm trying to figure out how I can bind my informationLayer to my VM to either display a MapPinPoint or a MapPolyline but am failing miserable. :(

    Here is what I have so far:

    Map Control:
    <telerik:RadMap x:Name="HistoryMapping">
                            <telerik:RadMap.Provider>
                                <telerik:BingMapProvider ApplicationId="TakenOutForSecurityPurposes" />
                            </telerik:RadMap.Provider>
                            <telerik:InformationLayer x:Name="historyInformationLayer" ItemsSource="{Binding MapSource}">
            
                            </telerik:InformationLayer>
                        </telerik:RadMap>

    VM:
    private ObservableCollection<object> mapSource;
            public ObservableCollection<object> MapSource
            {
                get
                {
                    return mapSource;
                }
                set
                {
                    mapSource = value;
                    RaisePropertyChanged("MapSource");
                }
                  
            }
     
    MapItDataGridCommand = new RelayCommand<object>((e) =>
                {
                    IsAppBusy = true;
                    StoryboardManager.PlayStoryboard("CloseHistoryGridAnimation", (o) =>}, null);
                    try
                    {
                        BingRouteProvider routeProvider = new BingRouteProvider();
                        routeProvider.ApplicationId = "TakenOutForSecurityPurposes";
                        routeProvider.MapControl = new RadMap();

                        routeProvider.RoutingCompleted += new EventHandler<RoutingCompletedEventArgs>(routeProvider_RoutingCompleted);
     
                        TripSummary trp = (TripSummary)e;
                        RouteRequest request = new RouteRequest();
                        Location loc1 = new Location(trp.TripStart.Lat, trp.TripStart.Lon);
                        Location loc2 = new Location(trp.TripEnd.Lat, trp.TripEnd.Lon);
                        request.Waypoints.Add(loc1);
                        request.Waypoints.Add(loc2);
                        routeProvider.CalculateRouteAsync(request);
                         
                    }
                    catch (Exception)
                    {
                        //LocationPoint locPt = (LocationPoint) e;
                        //MapPinPoint pnpoint = new MapPinPoint();
                        //Location loc = new Location(locPt.Lat,locPt.Lon);
                         
                    }
                    
                });
            }
     
            void routeProvider_RoutingCompleted(object sender, RoutingCompletedEventArgs e)
            {
                RouteResponse response = e.Response as RouteResponse;
                if (response != null)
                {
                    if (response.Result == null) return;
                    if (response.Result.RoutePath != null)
                    {
                        MapPolyline route = new MapPolyline();
                        route.Points = response.Result.RoutePath.Points;
                        route.Stroke = new SolidColorBrush(Colors.Red);
                        route.StrokeThickness = 3;
                        mapSource = new ObservableCollection<object>();
                        MapSource.Add(route);
       IsAppBusy = false;

                    }
                }
            }


    Thanks in advance!


  2. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 05 Jul 2011 Link to this post

    Hello Alex,

    When you assign the mapSource field in your code then the PropertyChanged event is not raised. You should use the MapSource property instead of mapSource. In this case the PropertyChanged event will be raised correctly.

    Greetings,
    Andrey Murzov
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  3. DevCraft banner
  4. Alex
    Alex avatar
    10 posts
    Member since:
    Nov 2010

    Posted 05 Jul 2011 Link to this post

    Hello Andrey,

    Thanks for the correction. Typos, gotta love em'!.

    Another question related to the same piece of code. I'm trying to make the center and zoom level dynamic as well, so I've set two properties in my VM for the map to bind to.

    private Location mapCenter;
            public Location MapCenter
            {
             get { return mapCenter; }
                set { mapCenter = value;
                RaisePropertyChanged("MapCenter");}
     
            }
     
            private int mapZoom;
            public int MapZoom
            {
                get { return mapZoom; }
                set { mapZoom = value;
                RaisePropertyChanged("MapZoom");}
            }

    And I've also added the two last lines of code below:
    void routeProvider_RoutingCompleted(object sender, RoutingCompletedEventArgs e)
            {
                RouteResponse response = e.Response as RouteResponse;
                if (response != null)
                {
                    if (response.Result == null) return;
                    if (response.Result.RoutePath != null)
                    {
                        MapPolyline route = new MapPolyline();
                        route.Points = response.Result.RoutePath.Points;
                        route.Stroke = new SolidColorBrush(Colors.Red);
                        route.StrokeThickness = 3;
                        MapSource = new ObservableCollection<object>();
                        MapSource.Add(route);
                        MapCenter = response.Result.Summary.BoundingRectangle.Center;
                        MapZoom = response.Result.Summary.BoundingRectangle.ZoomLevel;
                    }
                }
            }

    The map is set to bind to the two properties above but the zoom level and/or center never seems to change. In checking what comes back from the RoutingCompleted it says the ZoomLevel coming back is "-2147483648". That doesn't seem right? Why is such a bogus Zoom level coming back in the result?

    Also, In the catch block of the initial code that I posted I'm trying to set a MapPinPoint if the conversion to TripSummary Fails. But am not sure how to set a MapPinPoint from my VM since I can't set lat/lon for it in the VM and don't have access to the map control from it.
  5. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 08 Jul 2011 Link to this post

    Hi Alex,

    First of all, I've inspected your code for the MapItDataGridCommand once again, and found following line of code there:

    routeProvider.MapControl = new RadMap();

    You must not do it this way. In all routing, geocode or search requests MapControl must point to the real control. Otherwise many things will not work. So I would recommend that you move this code from VM to the UI part of code. 

    LocationRect structure can calculate zoom level for the best view when its MapControl property is set. So you should use bounding rectangle this way:
    LocationRect rect = response.Result.Summary.BoundingRectangle;
    rect.MapControl = this.HistoryMapping;
    MapCenter = rect.Center;
    MapZoom = rect.ZoomLevel;

    Also, we don't calculate bounding rectangle in our code, but uses one is returned from Bing routing service.

    You are using MapPolyline in your code, which is visual control, so you should be able to use MapPinPoint as well. But if you want to follow the MVVM pattern, then you shouldn't use any of them in your VM. If you'd like strong follow the MVVM pattern, then I would recommend you following:

    1. Use several information layers (at least 2): 1 for polylines and 1 for pinpoints (for example).
    2. Don't use ObservableCollection<object> in your VM and remove single MapSource property.
    3. Create 2 separate properties:
        a. Polylines as ObservableCollection<LocationCollection>. Will be used to show polylines. 
        b. Markers as ObservableCollection<Location>. Will be used to show pushpins.
    4. Add BoundingRectangle property to your VM. Handle property changed event from your VM in your UI code, check when bounding rectangle is changed and set map viewport at this moment.

    Prospective code could looks like the following:

    <UserControl x:Class="CalculateBestViewInTheViewModel.MainPage"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 mc:Ignorable="d"
                 d:DesignHeight="300" d:DesignWidth="400">
        <UserControl.Resources>
            <DataTemplate x:Key="Polyline">
                <telerik:MapPolyline Points="{Binding}" Stroke="Red" StrokeThickness="2" />
            </DataTemplate>
             
            <DataTemplate x:Key="Pushpin">
                <telerik:Pushpin telerik:MapLayer.Location="{Binding}" />
            </DataTemplate>
        </UserControl.Resources>
            <Grid x:Name="LayoutRoot" Background="White">
            <telerik:RadMap x:Name="radMap"
                            Provider="{Binding Provider}"
                            Center="40,-100"
                            ZoomLevel="3"
                            MapMouseClick="MapMouseClick">
                <telerik:InformationLayer x:Name="informationLayer"
                              ItemsSource="{Binding Polylines}"
                              ItemTemplate="{StaticResource Polyline}"/>
                <telerik:InformationLayer x:Name="markersLayer"
                              ItemsSource="{Binding Markers}"
                              ItemTemplate="{StaticResource Pushpin}"/>
            </telerik:RadMap>
        </Grid>
    </UserControl>



    public class RadMapViewModel : ViewModelBase
    {
        public RadMapViewModel()
        {
                this.Markers = new ObservableCollection<Location>();
                this.Polylines = new ObservableCollection<LocationCollection>();
                this.Provider = new OpenStreetMapProvider();
        }
     
        private MapProviderBase mapProvider;
        public MapProviderBase Provider
        {
            get
            {
                return this.mapProvider;
            }
     
            set
            {
                this.mapProvider = value;
                this.OnPropertyChanged("Provider");
            }
        }
     
        private LocationRect boundingRect;
        public LocationRect BoundingRectangle
        {
            get
            {
                return this.boundingRect;
            }
     
            set
            {
                this.boundingRect = value;
                this.OnPropertyChanged("BoundingRectangle");
            }
        }
     
        public ObservableCollection<Location> Markers
        {
            get;
            private set;
        }
     
        public ObservableCollection<LocationCollection> Polylines
        {
            get;
            private set;
        }
    }

    public partial class MainPage : UserControl
    {
        private RadMapViewModel model = new RadMapViewModel();
     
        public MainPage()
        {
            InitializeComponent();
     
            this.model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.model_PropertyChanged);
        }
     
        private void model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "BoundingRectangle")
            {
                LocationRect rect = this.model.BoundingRectangle;
                rect.MapControl = this.radMap;
                this.radMap.SetView(rect);
            }
        }
     
        void routeProvider_RoutingCompleted(object sender, RoutingCompletedEventArgs e)
        {
            RouteResponse response = e.Response as RouteResponse;
     
            if (response != null)
            {
                if (response.Result == null) return;
     
                if (response.Result.RoutePath != null)
                {
                    this.model.Polylines.RemoveAll();
                    this.model.Polylines.Add(response.Result.RoutePath.Points);
                    this.model.BoundingRectangle = response.Result.Summary.BoundingRectangle;
                }
            }
        }
     
        MapItDataGridCommand = new RelayCommand<object>((e) =>
        {
            // I've romoved part of your code just to make
            // code snippet short.
            ...
            try
            {
                BingRouteProvider routeProvider = new BingRouteProvider();
                routeProvider.ApplicationId = "TakenOutForSecurityPurposes";
                routeProvider.MapControl = this.radMap;
      
                ...
            }
            catch (Exception)
            {
                this.model.Markers.RemoveAll();
                LocationPoint locPt = (LocationPoint) e;
                this.model.Markers.Add(new Location(locPt.Lat,locPt.Lon));
            }
        });
      
    }

    Regards,
    Andrey Murzov
    the Telerik team

    Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!

Back to Top