23 August 2015

Windows 10 maps part 4 - color scheme and options

Intro
This part of the mapping series is the shortest from a code perspective - it actually only handles six properties of the Map control, but these give you access to some pretty powerful features of the map. The properties in question are:

  • imageBusinessLandmarksVisible
  • LandmarksVisible
  • PedestrianFeaturesVisible
  • TrafficFlowVisible
  • TransitFeaturesVisible
  • ColorScheme (the map 'theme')

To see this features in action, you will need to use the controls displayed to the right, and I will take you to the center of the (in)famous capital city of the Netherlands - Amsterdam. I have zoomed to this location manuallyimage

So here we have a base map, looking on the center of Amsterdam, from a point above the actual center to the North. This shows a view of the Central Station and the river IJ, where at the moment of this writing Sail Amsterdam is taking place.

image

This maps shows when LandmarksVisible is set to true. Basically every building is displayed in 3D outline, as well as some well-known landmarks, most notably the Central Station.

image

If BusinessLandmarksVisible is set to true, all kinds of restaurants and other businesses light up. This can be handy when making an app targeted toward tourists. A major differentiator towards competing map providers is that these business features can be turned on and off at will - and are not flung into your face by default. Display is determined by whether or not it makes sense to your app, not whether or not it's useful for the commercial purposes of the map provider.

image

A very Dutch map shows up when PedestrianFeaturesVisible is set to true. All kinds of little ways and roads that are only sketchily displayed in 'normal' mode - or not at all - show up now. The map now shows roads and alleyways that are not accessible to vehicles, but can be used perfectly when strolling through the city. Very neat.

image

When TrafficFlowVisible is set to true, live traffic data is displayed on top of the map, which shows it's pretty busy now at the area in front of the Amsterdam Central Station. Which is kind of like saying it's pretty darn cold in Antarctica, but it proves the point. Notice also color intensity gives an indication of actual traffic density - red is very dense, yellow a little less dense.

image

Another very Dutch maps shows up when TransitFeaturesVisible is set to true. Now all kinds of stops for trains, streetcars, the subway and other forms of public transport show up. Once again very handy for apps targeted toward tourists or other 'getting around' style apps.

image

Now when you set ColorScheme to MapColorScheme.Dark you get the following "Amsterdam by night" view

image

And of course you can combine all these options,  this is Amsterdam with all the options turned on, now looking to the south (the Central station is now seen from the back, and on the lower right corner of the map).

The actual code involved in all these great images is actually very, very little:

private void ToggleOption(object sender, RoutedEventArgs e)
{
  MyMap.BusinessLandmarksVisible = ToggleBusiness.IsChecked.Value;
  MyMap.LandmarksVisible = ToggleLandMark.IsChecked.Value;
  MyMap.PedestrianFeaturesVisible = TogglePedestrian.IsChecked.Value;
  MyMap.TrafficFlowVisible = ToggleTraffic.IsChecked.Value;
  MyMap.TransitFeaturesVisible = ToggleTransit.IsChecked.Value;
}

private void ThemeToggle_Toggled(object sender, RoutedEventArgs e)
{
  MyMap.ColorScheme = ThemeToggle.IsOn ? MapColorScheme.Light : MapColorScheme.Dark;
}

which goes to show you have a very powerful and visually compelling map toolbox at your disposal that is very easy to use. And of course, if also works on a phone, 3D landmarks and all

image

with zero code change, of course, for this is a Universal Windows App. Things like this make a very compelling case for tourist guiding apps indeed.

A tip, finally - if you want an app making use of this features, you might want to include a suggestion for you users to check if they have downloaded the map of the area they are going to to their device. Although the maps themselves - including the 3D data - are not overly large, having to download them in a busy city with potentially a lot of network congestion may lead to user experience degrading delays, which you of course want to avoid if possible.

Get the sample app to show you how things work in real life - in your own city.

20 August 2015

Windows 10 maps part 3 - querying the map & attaching business objects to map shapes

Intro
In my previous post in this series I showed how to draw lines, polygons and icons on the map. What I did not show was how you could query the map for the shapes present at a location when you tap on it, and more importantly - how you find the business objects back that were used to create this shapes in the first place. In previous times, specifically the Visual Basic 6.0 era, people used to do something like putting something like an id in a Tag property of a UI element and use that go back into the database. I recently even encountered an approach using a Tag in a Window Store app *shudder* ;) Fortunately in these day and age things can be done quite differently.

To be honest - this article does not describe brand new knowledge, nor it is really specific to Windows 10 (I used this trick in Windows Phone 8.0 already), and if you dig through my blog and/or samples of the past year you might already distill it. Yet, I get the question about relating map shapes to business objects quite often, as map shapes (thankfully) do not have a Tag property, people apparently still wonder how to connect shapes and business objects so I thought it best to make this a separate article.

Attaching business objects to map shapes
This is actually pretty easy, and if you have been peeking at the sample app you might have seen that there is actually one extra statement in all three methods that draw a shape as I write in my previous post. For example, I take line drawing method:

private void DrawLines(object sender, RoutedEventArgs e)
{
  if (!DeleteShapesFromLevel(2))
  {
    var strokeColor = Colors.Green;
    strokeColor.A = 200;

    //Drawing lines, notice the use of Geopath. Consists out of BasicGeopositions
    foreach (var dataObject in PointList.GetLines())
    {
      var shape = new MapPolyline
      {
        StrokeThickness = 9,
        StrokeColor = strokeColor,
        StrokeDashed = false,
        ZIndex = 2,
        Path = new Geopath(dataObject.Points)
      };
      shape.AddData(dataObject);

      MyMap.MapElements.Add(shape);
    }
  }
}

There is an AddData method that you won't find in any Microsoft API documentation because it's an extension method I wrote myself. The key thing to understand is that a map shape descends from DependencyObject. And thus, while it does not have a Tag, it can support attached dependency properties.

So over in the MappingUtilities folder there is a class hosting a simple attached dependency property plus some helper methods:

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls.Maps;

namespace MappingUtilities
{
  /// <summary>
  /// Helper class to attach data to map shapes. Well, to any 
  /// DependencyObject, but that's to make it fit into a PCL
  /// </summary>
  public static class MapElementData
  {
    #region Attached Dependency Property ObjectData
    public static readonly DependencyProperty ObjectDataProperty =
       DependencyProperty.RegisterAttached("ObjectData",
       typeof(object),
       typeof(MapElementData),
       new PropertyMetadata(default(object), null));

    public static object GetObjectData(MapElement obj)
    {
      return obj.GetValue(ObjectDataProperty);
    }

    public static void SetObjectData(
       DependencyObject obj,
       object value)
    {
      obj.SetValue(ObjectDataProperty, value);
    }
    #endregion

    public static void AddData(this MapElement element, object data)
    {
      SetObjectData(element, data);
    }

    public static T ReadData<T>(this MapElement element) where T : class
    {
      return GetObjectData(element) as T;
    }
  }
}

Attaching a business object to a shape is as simple as calling shape.SetObjectData(businessObject) and getting it back again from the shape is done by using ReadData<T>. Which brings me to the subject of

Querying the map
On any GIS (or simple map based system) you want to be able to tap an element on the map to review it's properties. On the Windows 10 map, it's not possible to select individual elements. In stead, you can tap the map and check what's there. I call that the "skewer method" - you basically stick a skewer in the map on the tap location and see what sticks :). Nothing wrong with that, by the way.

If you tap the map, you get either this message -  if you tap on a place where there are no shapes to be found
image

...or something like this when you tap on a place where there are map shapes indeed
image Either way, this method is called:

 private async void OnMapElementClick(MapControl sender, 
                                      MapElementClickEventArgs args)
{
  var resultText = new StringBuilder();

  // Yay! Goodbye string.Format!
  resultText.AppendLine($"Position={args.Position.X},{args.Position.Y}");
  resultText.AppendLine(
    $"Location={args.Location.Position.Latitude:F9},{args.Location.Position.Longitude:F9}");

  foreach (var mapObject in args.MapElements)
  {
    resultText.AppendLine("Found: " + mapObject.ReadData<PointList>().Name);
  }
  var dialog = new MessageDialog(resultText.ToString(),
    args.MapElements.Any() ? "Found the following objects" : "No data found");
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    async () => await dialog.ShowAsync());
}

This is great new event that makes searching for map elements on the location where is tapped or clicked. This is like actually applying the skewer - you stick it in the map and whatever is there, ends up in args.MapElements. Then it's simply a matter of processing all shapes in there and retrieve the business object using the ReadData method. The generic parameter conveniently casts it to the right type. Thus we can extract the Name property of every business objects from which the map shape originates and display it in a dialog. Or, presumably, do something more useful with it in a real life situation ;)

Hooking up the control to the click event is simple, like this

<maps:MapControl x:Name="MyMap" MapElementClick="OnMapElementClick" />

Concluding remarks
Using an attached dependency property to keep track of business objects and their map representation is pretty easy. So look no further for a tag (or some other weird trick) - just use the great things XAML offers us out of the box. Once again I refer to the sample app to show you how things work in real life.

19 August 2015

Windows 10 maps part 2 - manipulation and drawing

Intro
In my previous post in this series I have talked about how to get a map control in a Universal Windows App and how to set it up with a proper key, now it's time to get the thing to actually do something. In this episode I am going to show how to perform basic map manipulations (i.e. control what location it's looking at, and how) and how to draw shapes on the map. I am going to refer to a sample app that has already been published on GitHub and has all the moving parts for the whole series.

Manipulation
When I talk about manipulation of a Windows 10 Universal Windows App map (there's a mouthful), I mean three basic things that can be manipulated - that is, when we consider the map as a traditional 2D map, i.e.  a flat surface where you look upon from right above.

  • View area
  • Heading (aka rotation)
  • Pitch

As we will see later on in this series, in Windows 10 this is actually a bit too limiting a statement, but I have to start somewhere. We will also see the view area is actually determined by not one but two factors - the center location and the zoom level.

View area / zoom level
The sample app has a couple of 'button' controls that control location and zoom level. If you tap the "Initial location", it will zoom from the default view (the whole world) to a location around my house. It will not just jump to the location, but zoom out a little, then zoom in all the way to this location - animating all the way. The same goes for the "Zoom" in and "Zoom out button", the map will zoom in, animating from one zoom level to another.

image
The methods doing this can be found in MainPage.xaml.cs:

private async void InitialLocation(object sender, RoutedEventArgs e)
{
  await MyMap.TrySetViewAsync(
    new Geopoint(new BasicGeoposition { Latitude = 52.181427, 
                                        Longitude = 5.399780 }),
 16, RotationSlider.Value, PitchSlider.Value, MapAnimationKind.Bow);

  //MyMap.Center = 
  //  new Geopoint(new BasicGeoposition { Latitude = 52.181427, 
                                          Longitude = 5.399780});
  //MyMap.ZoomLevel = 16;
}

private void ZoomOut(object sender, RoutedEventArgs e)
{
  MyMap.TryZoomOutAsync();
 // MyMap.ZoomLevel -= MyMap.ZoomLevel > MyMap.MinZoomLevel ? 1 : 0;
}

private void ZoomIn(object sender, RoutedEventArgs e)
{
   MyMap.TryZoomInAsync();
  //MyMap.ZoomLevel += MyMap.ZoomLevel < MyMap.MaxZoomLevel ? 1 : 0;
}

As you see, there is a lot of commented out code. This is because I want to show there is more than one way to achieve the same result. Personally I find the MyMap.TrySetViewAsync method the most recommendable, as this animates to a location and a zoom level and also (as can be seen from the code) can set a rotation and a pitch (explained below). You can also just set zoom level and center location in one go, but that will make the map 'jump;' to a location, which is a way less desirable user experience IMHO. Remember the old mantra - 'fast and fluid' ;)

The same goes for zoom in and zoom out. The TryZoomOutAsync and TryZoomInAsync methods give a fluid animation going from one zoom level to another, and have the added bonus of not crashing when you try to zoom below 0 or over the maximum zoom level (or the need of creating a complex expression to prevent that).

This is what you need when you use a simple 2D map. As we will see later, when we go to 3D maps other factors come into play.

Aside - coordinates
For some reason that is not clear to me, the map API has two types that depict a location. The first one is a struct BasicGeopositionthat only holds latitude, longitude and altitude, For creating shapes, this is usually enough. Yet, for manipulating the map we need Geopoint. You create a Geopoint from a BasicGeoposition. A Geopoint has some extra capabilities, like SpatialReferenceId (that to my knowledge still has no function yet, and and AltitudeReferenceSystem that I never needed to use as of yet. Geopoint is a class. Maybe this is some optimization thing.The key takeaway is that there are two coordinate types, and sometimes you need the one, and sometimes the other. Intellisense is your friend here.

Heading
Every cartographer or GIS person - and everyone who used to travel before the ubiquitous affordable GPS systems became available (we had paper maps back then, kids, can you believe it) knows one golden rule: "North Is Up". Unfortunately 99.5% of the population does not care about proper GIS rules, nor does it want to use a compass, and it particularly does not want to deal with mentally rotating a map while navigating a complex and busy highway intersection - they follow the mapping school of "I Just Want To Get There". So GPS systems tend to show 'up' as the direction you are travelling in. And the map control supports that too. Of course you can simply apply a rotation transformation to the control, but that will show all labels at an angle too. This is clearly not what we want, and that's what Microsoft knows too.

image
Here you see the same map as above, but rotated 90° counter-clockwise. Notice how all labels are still readable.

There are quite some ways to accomplish that. I did it by data binding the maps Heading property to the slider's value:

<Slider x:Name="RotationSlider" VerticalAlignment="Top"
    Minimum ="-180" Maximum="180" 
    Value="{Binding ElementName=MyMap, Path=Heading, Mode=TwoWay}" />

This has the advantage slider and map heading stay synchronized when the user rotates the map with a two-finger gesture. Unfortunately, although the map accepts -180° to 180° it really wants to use 0° to 360° so if you rotate the map like that you will see the slider jump to the right. There is some more code necessary to keep that in sync. There are no less than three ways to set the heading, as you can see in this unused method in the code:

// Not used now - replaced by databinding
private void RotationSliderOnValueChanged(object sender, 
             RangeBaseValueChangedEventArgs e)
{
  MyMap.TryRotateToAsync(e.NewValue);

  //Same result, sans the cool animation.
  MyMap.Heading = e.NewValue;

  //Rotates BY a certain value.
  MyMap.TryRotateAsync(e.NewValue);
}

TryRotateToAsync rotates the map to a set angle, and do this animated. If you set "Heading" directly, it will have the same effect, but it will just jump to the nieuw display. TryRotateAsync finally does not rotate to an angle, but by an angle, so if you repeatedly call this method  with "10" it will rotate your map ten degrees counter clockwise for every call.

Pitch
This is like a fake 3D view, like the effect you get on GPS systems - or when you rotate the top of a paper map backwards from you:
image
This is the same map a the previous two, but now with a pitch of 60. A pitch may be anywhere from 0 to 75. The is a catch - pitch values is only effective at zoom level  7 and down. Above that, you can set pitch to any value you like but you get the same result as when you set it to 0. Which makes sense - otherwise your viewpoint is so 'high' above the map that pitching it would mean you/d mainly see the empty space 'above' and 'beyond' the actual flat map. Setting the map pitch from the slider is also done by data binding. Note that I need to bind to the DesiredPitch property:

<Slider x:Name="PitchSlider" Grid.Column="1" Grid.Row="1" 
        Orientation="Vertical" Margin="0,0,0,12" Minimum="0" Maximum="75" 
        Value="{Binding ElementName=MyMap, Path=DesiredPitch, Mode=TwoWay}" />

as the map might just refuse you to give the pitch you want because to are zoomed out too much. You can also do this from code:

private void PitchSliderOnValueChanged(object sender, 
  RangeBaseValueChangedEventArgs e)
{
  //Only effective on ZoomLevel 7 and higher!
  MyMap.DesiredPitch = e.NewValue;

  // Does not work - read only
  // MyMap.Pitch = e.NewValue;
}

and there is in fact the Pitch property on the map, but that is read only. DesiredPitch is the pitch you request, Pitch holds the pitch you actually get.

Manipulation by canvas controls
A new way of enabling the user control the map zoom, pitch is making use of the new canvas control properties. These are not used in the code sample, but you can make controls visible like this:

<maps:MapControl x:Name="MyMap" 
     ZoomInteractionMode   ="GestureAndControl" 
     RotateInteractionMode ="GestureAndControl" 
     TiltInteractionMode  = "GestureAndControl" />

This will result on these four controls on the right side of the map control

image

imageOn a tablet or PC, where there is usually enough room for external toolbars and sliders these are a very nice touch already, but if you want to use the map on a smaller device like a phone or mini tablet they are actually a godsend, for a you can see on the illustration on the right the sliders actually use up a lot of the valuable screen real estate on a phone. In a real application I would probably write an adaptive trigger to do away with the sliders on a phone and/or small screen device.

You can set the value of these three properties actually to five different values:

  • Auto
  • Disabled
  • GestureOnly
  • ControlOnly
  • GestureAndControl

And setting "Disabled" to all properties has the added bonus of being able to lock down the map, which can be useful on a phone. Who has not accidentally swiped the map way, operating the phone one-handed, looking for a parking spot or something. There is, by the way, a fourth map property to lock down even the panning: PanInteractionMode, but that one has no control option, and only supports the values "Auto" and "Disabled".

Notice the power of Universal Windows Apps by the way: the only thing I had to change for my app was to point Visual Studio to the phone emulator in stead of Windows itself. Deploying it to my phone would require only pointing it to a device and selecting ARM in stead of x86/x64 compile. ZERO code change.

Drawing (and deleting)
If you are coming from Windows Phone 8.1, not much has changed in Windows 10, apart from the fact things work a lot better. If you are coming from Windows 8 store apps - your cheese has moved quite a bit, but that is all for the better.We still draw polygons, lines and points but things have changed a bit.

Filling the map
If you start the sample app, click "initial location, then "draw lines", "draw shapes" and then "draw points" a couple of times, you will end up with this mess :)

image

Drawing a line
Drawing a couple lines is as simple as this:

private void DrawLines(object sender, RoutedEventArgs e)
{
  if (!DeleteShapesFromLevel(2))
  {
    var strokeColor = Colors.Green;
    strokeColor.A = 200;

    //Drawing lines, notice the use of Geopath. Consists out of BasicGeopositions
    foreach (var dataObject in PointList.GetLines())
    {
      var shape = new MapPolyline
      {
        StrokeThickness = 9,
        StrokeColor = strokeColor,
        StrokeDashed = false,
        ZIndex = 2,
        Path = new Geopath(dataObject.Points)
      };
      MyMap.MapElements.Add(shape);
    }
  }
}

Lines are created as a MapPolyLine object that have the following properties:

  • A stroke thickness
  • A color (not this is a partially translucent green color)
  • A dash (the line is dashed or not, there are no other pattern available)
  • A z-index (this determines which objects appear on top of each other - a shape with a higher z-index will appear to be 'on top' of a shape with a lower z-index
  • Most importantly - a Geopath that contains a number of points.

Shapes are then simply added to the MapELements collection of the Map.

The PointList.GetLines() I used to create data just gets a list of objects that each contain a list of points:

public static IEnumerable<PointList> GetLines()
{
  var result = new List<PointList>
  {
    new PointList
    {
      Name = "Line1",
      Points = new List<BasicGeoposition>
      {
       new BasicGeoposition{Latitude = 52.1823604591191, Longitude = 5.3976580593735},
       new BasicGeoposition{Latitude = 52.182687940076 , Longitude = 5.39744247682393},
       new BasicGeoposition{Latitude = 52.1835449058563, Longitude = 5.40016567334533},
       new BasicGeoposition{Latitude = 52.1837400365621, Longitude = 5.40009761229157}
      }
    },
    new PointList
    {
      Name = "Line2",
      Points = new List<BasicGeoposition>
      {
        new BasicGeoposition{Latitude = 52.181295119226  , Longitude = 5.39748212322593},
        new BasicGeoposition{Latitude = 52.1793784294277, Longitude = 5.39909915998578}
      }        
    }
  };

  return result;
}

Drawing shapes
This is nearly the same as drawing shapes, only this draws a closed shaped. You are not required to close the shape (the last point of the Geopath does not need to be equal to the first one, as in some geographical systems - the shape is implicitly closed. The code is nearly the same.

private void DrawShapes(object sender, RoutedEventArgs e)
{
  if (!DeleteShapesFromLevel(1))
  {
    var strokeColor = Colors.DarkBlue;
    strokeColor.A = 100;
    var fillColor = Colors.Blue;
    fillColor.A = 100;

    foreach (var dataObject in PointList.GetAreas())
    {
      var shape = new MapPolygon
      {
        StrokeThickness = 3,
        StrokeColor = strokeColor,
        FillColor = fillColor,
        StrokeDashed = true,
        ZIndex = 1,
        Path = new Geopath(dataObject.Points)
      };

      MyMap.MapElements.Add(shape);
    }
  }
}

The only difference is that we not only have a stroke color (which here is the outline of the shape), but we now also have fill color that determines how the inside of the shape is colored. Oh, and I do use a dash for the stroke here, if only to show what it looks like

Drawing icons
Windows Phone 8.1 introduced the MapIcon, a point object type that allows you to show icon type objects - with label! - that were drawn by the map itself. Compared to the 'old' way of displaying XAML elements on top of the map this made things vastly more simple and above all offered an awesome performance, compared to the 'old way'' .Unfortunately it also introduced the concept of 'best effort drawing' - that is, icons close to each other were not displayed in some seemingly randomly way to preserve performance. In practice, everyone used the 'old' way of doing things, i.e. drawing XAML elements on top of the map, which gave a horrible performance, but at least the data was displayed.

Rejoice, now, because this has been fixed. MapIcons are now a fully viable concept, and very much usable. Drawing MapIcons goes like this:

private async void DrawPoints(object sender, RoutedEventArgs e)
{
  // How to draw a new MapIcon with a label, anchorpoint and custom icon.
  // Icon comes from project assets
  var anchorPoint = new Point(0.5, 0.5);
  var image = RandomAccessStreamReference.CreateFromUri(
              new Uri("ms-appx:///Assets/wplogo.png"));

  try
  {
    // Helper extension method    var area = MyMap.GetViewArea();
    var area = MyMap.GetViewArea();

    // PointList is just a helper class that gives 'some data'
    var points = PointList.GetRandomPoints(
      new Geopoint(area.NorthwestCorner), new Geopoint(area.SoutheastCorner),
      50);
    foreach (var dataObject in points)
    {
      var shape = new MapIcon
      {
        Title = dataObject.Name,
        Location = new Geopoint(dataObject.Points.First()),
        NormalizedAnchorPoint = anchorPoint,
        Image = image,
        CollisionBehaviorDesired = 
          onCollisionShow? MapElementCollisionBehavior.RemainVisible : 
                           MapElementCollisionBehavior.Hide,

        ZIndex = 3,
      };
      MyMap.MapElements.Add(shape);
    }
  }
  catch (Exception)
  {
    var dialog = new MessageDialog("GetViewArea error");
    await dialog.ShowAsync();
  }
}

This may look a bit intimidating, but only the middle part is where the actual drawing takes place in the center of the code - the creation of the MapIcon object. That is distinctly different from the other two shapes:

  • A MapIcon has a label, that is set in the Title property. This is optional.
  • It also has an anchor point, this describes where the image appears on the map relative to the location. 0,0 is left top, 1,1 is right bottom. So if you set it to (0.5, 0.5) the center of the image will fall exactly op top of the coordinate in Location. And of course you can use everything in between should you desire so.
    image
    Tip: make your icons square. If they cannot be, then make the image square and make the superfluous part translucent. I have found non-square icons playing havoc with the anchor point.
  • It can be displayed using a custom image. This is also optional, if you don't set this you will get a default blue-ish text balloon like image.
  • The CollisionBehaviorDesired  property determines whether the MapIcon drawing should use the 'best effort' drawing method of old (Hide), or the new one (RemainVisible).The difference is controlled by this switch on the UI all the way at the bottom
    image
    It is default set to hide (just like the default value on the icon), but tor all practical purposes I think it should always be set to RemainVisible. Note - the effect of changing this setting may not  be visible immediately, but it will be once you start to zoom in or out.

The method draws 50 icons per click on the button within the current view of the map, which is calculated using the extension method GetViewArea. This is not a standard map method - its actually quite an old extension method I wrote some time ago, and in the new world of Windows 10, it can fail in certain circumstances, hence the try-catch

Deleting
The first two drawing methods toggle shapes, and also feature a call to a DeleteShapesFromLevel. Deleting shapes is a easy as removing them from the MapElements collection :)

private bool DeleteShapesFromLevel(int zIndex)
{
  // Delete shapes by z-index 
  var shapesOnLevel = MyMap.MapElements.Where(p => p.ZIndex == zIndex);
  if (shapesOnLevel.Any())
  {
    foreach (var shape in shapesOnLevel.ToList())
    {
      MyMap.MapElements.Remove(shape);
    }
    return true;
  }
  return false;
}

The only 'fancy' thing here is that I select shapes by z-index, which I keep calling 'level - having worked in GIS for 23 years tends to stick.

Summary and concluding remarks

  • Drawing lines, polygons and icons in Universal Windows Apps look remarkably like drawing the same things in Windows Phone 8.1
  • There are still two types to deal with when handling coordinates - BasicGeoposition and Geopoint, and you will just have to see by Intellisense when you need the one, and when you need the other.
  • There is no such thing as levels or layers as in the Windows 8.x Bing Maps control - there is only Z-index. As shape with a higher Z-index appears on top of a shape with a lower Z-index. In this maps control Z-indexes are properly honored now - at least for MapIcons there where some issues in Windows Phone 8.1
  • MapIcons are now a fully viable way of drawing icons that give a very good performance, now the developer has a way of controlling the way the are displayed).

Once again I refer to the sample app to see all the concepts described above in action.

14 August 2015

Gotcha: Windows 10 mobile does not download additional keyboards and speech language after hard reset

A quicky and nothing code related this time. After upgrading from Windows Phone 8.1 through a number of intermediary Insider builds I found I got more and more problems, even after the 10512 build, and decided to backup, hard reset and then restore my phone. Several sources on the internet says that is a way to solve stability issues. After all, this is still beta stuff. So I followed the procedure, and I ran into a snag - my phone is originally British, it had British keyboards and speech installed, but I wanted USA speech (to get Cortana) and keyboard, as well as a Dutch keyboard, for auto correct and spelling hints.

Whatever I tried, it could not get the phone to download speech or keyboards. It said simply 'error downloading' for speech, and 'text suggestions update for this keyboard didn't download successfully' for the keyboard. Now what? So I backed up the phone again, resetted again, so I would restore it from the exact same version. To no avail.

imageimage

 

 

 

 

 

 

 

 

 

The key thing to understand is that keyboards and speech are updates to the OS and as such part of the Insiders' preview. And apparently the phone is unregistered from the Insiders rings when you hard reset it. The solution is very simple - hit the Windows Insiders app as soon as it's downloaded (restore will take care of that), and activate the fast ring again. Boom. You can download speech and keyboards.

If I only realized this before I resetted the 2nd time around - that would have saved me half a day.

12 August 2015

Gotcha: custom Microsoft Band page shows only data in last added UI element

Today I finally nailed a very odd issue in the Microsoft Band SDK - or actually, my understanding thereof. For my current research project, I am building a custom UI on my Microsoft Band. I should contain two panels: one with a text, an icon and a button, and a second panel containing two texts. Details of how to setup up something like this, can be found in the band samples.

Building a Band UI is not for the faint hearted, it includes building the UI from code without any kind of designer. Filling those UI elements with a value then is a separate step, which contains quite a big gotcha that you cannot derive from the samples.

My Band UI creation code is like this:

private IEnumerable<PageLayout> BuildTileUi()
{
  var bandUi = new List<PageLayout>();
  var page1Elements = new List<PageElement>
  {
	new Icon {ElementId = IconId, Rect = new PageRect(60,10,24,24)},
	new TextBlock  {ElementId = TextTemperatureId, Rect = new PageRect(90, 10, 50, 40)},
	new TextButton {ElementId = ButtonToggleFanId, Rect = new PageRect(10, 50, 220, 40), 
	    HorizontalAlignment = HorizontalAlignment.Center}
  };
  var firstPanel = new FilledPanel(page1Elements) { Rect = new PageRect(0, 0, 240, 150) };

  var page2Elements = new List<PageElement>
  {
	new TextBlock {ElementId = TextTimeId, Rect = new PageRect(10, 10, 220, 40)},
	new TextBlock {ElementId = TextDateId, Rect = new PageRect(10, 58, 220, 40)}
  };
  var secondPanel = new FilledPanel(page2Elements) { Rect = new PageRect(0, 0, 240, 150) };

  bandUi.Add(new PageLayout(firstPanel));
  bandUi.Add(new PageLayout(secondPanel));

  return bandUi;
}

That is quite a handful, right? Then for setting values to those UI element, I had this little piece of code:

private List<PageData> BuildTileData1(string timeText,
                                      string dateText,
                                      string temperature,
                                      string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText)),
    new PageData(Page2Id, 1,
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, new IconData(IconId, 1)),
    new PageData(Page1Id, 0,
      new TextBlockData(TextTemperatureId, temperature)),
    new PageData(Page1Id, 0, new TextButtonData(ButtonToggleFanId, buttonText))
  };

  return result;
}

And this is set to my tile using bandClient.TileManager.SetPagesAsync. Which gave me a quite puzzling result: only the last element on each page actually had a value. So that means only the datetext and the button actually showed text. As the sample in the Band SDK samples only uses one UI element, I was like, repeat adding PageData elements, right?

Wrong!

It turns out you can only add values to a PageData element once per SetPagesAsync call. So what you need to do, actually, is:

private List<PageData> BuildTileData(string timeText, 
                                     string dateText, 
                                     string temperature, 
                                     string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText),
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, 
      new IconData(IconId, 1), 
      new TextButtonData(ButtonToggleFanId, buttonText), 
      new TextBlockData(TextTemperatureId, temperature)),
  };

  return result;
}

See the difference? Only add 2 PageData elements, one for every Page, not one for every page element data object. Maybe elementary, but I overlooked it. For quite some time. The constructors of PageData give a bit of a hint:

public PageData(Guid pageId, int pageLayoutIndex, IEnumerable<PageElementData> values);
public PageData(Guid pageId, int pageLayoutIndex, params PageElementData[] values);

as the third parameter is not a page element data object but a list of it. The thing is, I created the UI on demand (when I tapped the tile) and then it worked as expected - but not the seconds time around.

It took me quite some time to figure this one out, so I thought it best to blog about it right away. Maybe I am giving help here, maybe I am just documenting my own stupidity, but nevertheless ;)

I skip the demo sample this time - if you run into this problem, you are quite far with the Band UI already, and the demo I am working on contains this code anyway and will - hopefully - arrive online soon anyway. Attentive readers will have some inkling of what that demo exactly comprises.

Happy Band coding!

09 August 2015

Windows 10 maps part 1 - setting up

Intro
Ever after my TechDays 2015 talk I have been getting a lot of questions about the new Universal Windows App map control for  Windows 10, and now the launch is done and the "getting things done" mind set (the very point of Windows anyway) is setting in, I have decided to make some time to write down how to get things done indeed - with the said map control.

To prevent this becoming an insanely huge post that will take forever to complete (and read) this is going to be a seven part series.

  1. Setting up
  2. Manipulation and drawing
  3. Querying the map & attaching business objects to map shapes
  4. Theme & Options
  5. Styles & 3D
  6. Scenes & camera
  7. Using external maps with TMS/WMS

During the blog series I will refer to a sample app that I have published on GitHub already.

By the way: this may not become a continuous series: it's very well possible some stuff gets published in between, as I am currently very much exploring the Windows 10 CoreIot stuff and plan to write about that as well.

Getting a key
During the course of the various versions of Windows and Windows Phone development we needed some key of a kind. Where we needed to get that varied over time and platform, but we now have one Windows thus one place to get the key: the Bing Maps dev portal. You login to that portal using your Microsoft account, Truth to be told, it's not the most beautiful nor the most intuitive of sites I have ever seen, and the only service announcement dating back to February 2015 makes it feel a bit like an abandoned site, but it is the real McCoy and it does the trick.

You click "My account"  and then "Create or view keys"

image
This will take you to this screen where you can see your existing keys, and create new ones. Curiously it's not possible to delete keys.

image
When you click "here" in "click here to create a new key" this will give you the following popup

image
You can leave the Application URL box empty. Important is that you select "Universal Windows App". Fill in the name, and leave the Key type to "Basic" - at the time of writing this it was the only choice anyway. The result will be a rather long affair - the keys I have created are 220 characters long.

Use your key in code or XAML
There are actually two ways of putting your key to work: you can do it in XAML, or from code. In XAML it can only done by using a map control like this:

 <maps:MapControl x:Name="MyMap" MapServiceToken="Your mapkey goes here" />

By the way, the easiest way to get a map control on your app and get all the name spaces set up correctly is by dragging it onto the design surface, either in Visual Studio or in Blend.

The other way of getting this done is by using code.

Windows.Services.Maps.MapService.ServiceToken ="Your mapkey goes here" 

Put this for instance in the constructor in App.xamls.cs. If you have done it right, you won't see any of the pesky labels indicating you have an incorrect key. If it look like this:

image
... you might want back to square one and see what you missed.

A few things to consider / keep in mind

  • As far as I understand, you cannot reuse keys from earlier type of apps, even not when you are submitting a new version of an existing app that is already in the Store. You have to create a new one for your Windows 10 submission.
  • Bing maps keys are free for consumer-facing, education, and non-profit applications and for some business scenarios as well, but not in all cases, and not without limits. Be sure to study this page to make sure you create the right kind of key. If you are building a large enterprise app or an app that goes totally viral and pulls a lot of maps data, you might need an enterprise key and those are definitely not free.
  • Be aware of the fact that things like geocoding (finding an address based upon a location), reverse geocoding and routing also hit the map engine, so if you don't use a map but for instance want to do a simple 'find the ZIP code for this location app' you will need a Bing maps key as well. And unlike the map,that shows you a friendly warning that your MapServiceToken is not specified (like displayed above) but keeps functioning otherwise, the geocoding and routing api's simply will throw an exception when your key is not valid
  • If you come from Windows Phone 8.1, this may seem like the exact same control at first glance. It is not. Do not fall in that trap. It's a complete new control sitting in a new namespace:  Windows.UI.Xaml.Controls.Maps.MapControl

Stay tuned for more mapping. And get the sample app if you are impatient ;)