Display points of interest on a map
Important
Bing Maps for Enterprise service retirement
The UWP MapControl and map services from the Windows.Services.Maps namespace rely on Bing Maps. Bing Maps for Enterprise is deprecated and will be retired, at which point the MapControl and services will no longer receive data.
For more information, see the Bing Maps Developer Center and Bing Maps documentation.
Note
MapControl and map services require a maps authentication key called a MapServiceToken. For more info about getting and setting a maps authentication key, see Request a maps authentication key.
Add points of interest (POI) to a map using pushpins, images, shapes, and XAML UI elements. A POI is a specific point on the map that represents something of interest. For example, the location of a business, city, or friend.
Display pushpins, images, and shapes on the map by adding MapIcon, MapBillboard, MapPolygon, and MapPolyline objects to a MapElements collection of a MapElementsLayer object. Then, add that layer object to the Layers collection of a map control.
Note
In previous releases, this guide showed you how to add map elements to the MapElements collection. While you can still use this approach, you'll miss out on some of the advantages of the new map layer model. To learn more, see the Working with layers section of this guide.
You can also display XAML user interface elements such as a Button, a HyperlinkButton, or a TextBlock on the map by adding them to the MapItemsControl or as Children of the MapControl.
If you have a large number of elements to place on the map, consider overlaying tiled images on the map. To display roads on the map, see Display routes and directions
Add a pushpin
Display an image such a pushpin, with optional text, on the map by using the MapIcon class. You can accept the default image or provide a custom image by using the Image property. The following image displays the default image for a MapIcon with no value specified for the Title property, with a short title, with a long title, and with a very long title.
The following example shows a map of the city of Seattle and adds a MapIcon with the default image and an optional title to indicate the location of the Space Needle. It also centers the map over the icon and zooms in. For general info about using the map control, see Display maps with 2D, 3D, and Streetside views.
public void AddSpaceNeedleIcon()
{
var MyLandmarks = new List<MapElement>();
BasicGeoposition snPosition = new BasicGeoposition { Latitude = 47.620, Longitude = -122.349 };
Geopoint snPoint = new Geopoint(snPosition);
var spaceNeedleIcon = new MapIcon
{
Location = snPoint,
NormalizedAnchorPoint = new Point(0.5, 1.0),
ZIndex = 0,
Title = "Space Needle"
};
MyLandmarks.Add(spaceNeedleIcon);
var LandmarksLayer = new MapElementsLayer
{
ZIndex = 1,
MapElements = MyLandmarks
};
myMap.Layers.Add(LandmarksLayer);
myMap.Center = snPoint;
myMap.ZoomLevel = 14;
}
This example displays the following POI on the map (the default image in the center).
The following line of code displays the MapIcon with a custom image saved in the Assets folder of the project. The Image property of the MapIcon expects a value of type RandomAccessStreamReference. This type requires a using statement for the Windows.Storage.Streams namespace.
Note
If you use the same image for multiple map icons, declare the RandomAccessStreamReference at the page or app level for the best performance.
MapIcon1.Image =
RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/customicon.png"));
Keep these considerations in mind when working with the MapIcon class:
- The Image property supports a maximum image size of 2048×2048 pixels.
- By default, the map icon's image is not guaranteed to be shown. It may be hidden when it obscures other elements or labels on the map. To keep it visible, set the map icon's CollisionBehaviorDesired property to MapElementCollisionBehavior.RemainVisible.
- The optional Title of the MapIcon is not guaranteed to be shown. If you don't see the text, zoom out by decreasing the value of the ZoomLevel property of the MapControl.
- When you display a MapIcon image that points to a specific location on the map - for example, a pushpin or an arrow - consider setting the value of the NormalizedAnchorPoint property to the approximate location of the pointer on the image. If you leave the value of NormalizedAnchorPoint at its default value of (0, 0), which represents the upper left corner of the image, changes in the ZoomLevel of the map may leave the image pointing to a different location.
- If you don't explicitly set an Altitude and AltitudeReferenceSystem, the MapIcon will be placed on the surface.
Add a 3D pushpin
You can add three-dimensional objects to a map. Use the MapModel3D class to import a 3D object from a 3D Manufacturing Format (3MF) file.
This image uses 3D coffee cups to mark the locations of coffee shops in a neighborhood.
The following code adds a coffee cup to the map by using importing a 3MF file. To keep things simple, this code adds the image to the center of the map, but your code would likely add the image to a specific location.
public async void Add3DMapModel()
{
var mugStreamReference = RandomAccessStreamReference.CreateFromUri
(new Uri("ms-appx:///Assets/mug.3mf"));
var myModel = await MapModel3D.CreateFrom3MFAsync(mugStreamReference,
MapModel3DShadingOption.Smooth);
myMap.Layers.Add(new MapElementsLayer
{
ZIndex = 1,
MapElements = new List<MapElement>
{
new MapElement3D
{
Location = myMap.Center,
Model = myModel,
},
},
});
}
Add an image
Display large images that relate to map locations such as a picture of a restaurant or a landmark. As users zoom out, the image will shrink proportionally in size to enable the user to view more of the map. This is a bit different than a MapIcon which marks a specific location, is typically small, and remains the same size as users zoom in and out of a map.
The following code shows the MapBillboard presented in the image above.
public void AddLandmarkPhoto()
{
// Create MapBillboard.
RandomAccessStreamReference mapBillboardStreamReference =
RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/billboard.jpg"));
var mapBillboard = new MapBillboard(myMap.ActualCamera)
{
Location = myMap.Center,
NormalizedAnchorPoint = new Point(0.5, 1.0),
Image = mapBillboardStreamReference
};
// Add MapBillboard to a layer on the map control.
var MyLandmarkPhotos = new List<MapElement>();
MyLandmarkPhotos.Add(mapBillboard);
var LandmarksPhotoLayer = new MapElementsLayer
{
ZIndex = 1,
MapElements = MyLandmarkPhotos
};
myMap.Layers.Add(LandmarksPhotoLayer);
}
There's three parts of this code worth examining a little closer: The image, the reference camera, and the NormalizedAnchorPoint property.
Image
This example shows a custom image saved in the Assets folder of the project. The Image property of the MapBillboard expects a value of type RandomAccessStreamReference. This type requires a using statement for the Windows.Storage.Streams namespace.
Note
If you use the same image for multiple map icons, declare the RandomAccessStreamReference at the page or app level for the best performance.
Reference camera
Because a MapBillboard image scales in and out as the ZoomLevel of the map changes, it's important to define where in that ZoomLevel the image appears at a normal 1x scale. That position is defined in the reference camera of the MapBillboard, and to set it, you'll have to pass a MapCamera object into the constructor of the MapBillboard.
You can define the position that you want in a Geopoint, and then use that Geopoint to create a MapCamera object. However, in this example, we're just using the MapCamera object returned by the ActualCamera property of the map control. This is the map's internal camera. The current position of that camera becomes the reference camera position; the position where the MapBillboard image appears at 1x scale.
If your app gives users the ability to zoom out on the map, the image decreases in size because the maps internal camera is rising in altitude while the image at 1x scale remains fixed at the reference camera's position.
NormalizedAnchorPoint
The NormalizedAnchorPoint is the point of the image that is anchored to the Location property of the MapBillboard. The point 0.5,1 is the bottom center of the image. Because we've set the Location property of the MapBillboard to the center of the map's control, the bottom center of the image will be anchored at the center of the maps control. If you want your image to appear centered directly over a point, set the NormalizedAnchorPoint to 0.5,0.5.
Add a shape
Display a multi-point shape on the map by using the MapPolygon class. The following example, from the UWP map sample, displays a red box with blue border on the map.
public void HighlightArea()
{
// Create MapPolygon.
double centerLatitude = myMap.Center.Position.Latitude;
double centerLongitude = myMap.Center.Position.Longitude;
var mapPolygon = new MapPolygon
{
Path = new Geopath(new List<BasicGeoposition> {
new BasicGeoposition() {Latitude=centerLatitude+0.0005, Longitude=centerLongitude-0.001 },
new BasicGeoposition() {Latitude=centerLatitude-0.0005, Longitude=centerLongitude-0.001 },
new BasicGeoposition() {Latitude=centerLatitude-0.0005, Longitude=centerLongitude+0.001 },
new BasicGeoposition() {Latitude=centerLatitude+0.0005, Longitude=centerLongitude+0.001 },
}),
ZIndex = 1,
FillColor = Colors.Red,
StrokeColor = Colors.Blue,
StrokeThickness = 3,
StrokeDashed = false,
};
// Add MapPolygon to a layer on the map control.
var MyHighlights = new List<MapElement>();
MyHighlights.Add(mapPolygon);
var HighlightsLayer = new MapElementsLayer
{
ZIndex = 1,
MapElements = MyHighlights
};
myMap.Layers.Add(HighlightsLayer);
}
Add a line
Display a line on the map by using the MapPolyline class. The following example, from the UWP map sample, displays a dashed line on the map.
public void DrawLineOnMap()
{
// Create Polyline.
double centerLatitude = myMap.Center.Position.Latitude;
double centerLongitude = myMap.Center.Position.Longitude;
var mapPolyline = new MapPolyline
{
Path = new Geopath(new List<BasicGeoposition> {
new BasicGeoposition() {Latitude=centerLatitude-0.0005, Longitude=centerLongitude-0.001 },
new BasicGeoposition() {Latitude=centerLatitude+0.0005, Longitude=centerLongitude+0.001 },
}),
StrokeColor = Colors.Black,
StrokeThickness = 3,
StrokeDashed = true,
};
// Add Polyline to a layer on the map control.
var MyLines = new List<MapElement>();
MyLines.Add(mapPolyline);
var LinesLayer = new MapElementsLayer
{
ZIndex = 1,
MapElements = MyLines
};
myMap.Layers.Add(LinesLayer);
}
Add XAML
Display custom UI elements on the map using XAML. Position XAML on the map by specifying the location and normalized anchor point of the XAML.
- Set the location on the map where the XAML is placed by calling SetLocation.
- Set the relative location on the XAML that corresponds to the specified location by calling SetNormalizedAnchorPoint.
The following example shows a map of the city of Seattle and adds a XAML Border control to indicate the location of the Space Needle. It also centers the map over the area and zooms in. For general info about using the map control, see Display maps with 2D, 3D, and Streetside views.
private void displayXAMLButton_Click(object sender, RoutedEventArgs e)
{
// Specify a known location.
BasicGeoposition snPosition = new BasicGeoposition { Latitude = 47.620, Longitude = -122.349 };
Geopoint snPoint = new Geopoint(snPosition);
// Create a XAML border.
Border border = new Border
{
Height = 100,
Width = 100,
BorderBrush = new SolidColorBrush(Windows.UI.Colors.Blue),
BorderThickness = new Thickness(5),
};
// Center the map over the POI.
MapControl1.Center = snPoint;
MapControl1.ZoomLevel = 14;
// Add XAML to the map.
MapControl1.Children.Add(border);
MapControl.SetLocation(border, snPoint);
MapControl.SetNormalizedAnchorPoint(border, new Point(0.5, 0.5));
}
This example displays a blue border on the map.
The next examples show how to add XAML UI elements directly in the XAML markup of the page using data binding. As with other XAML elements that display content, Children is the default content property of the MapControl and does not have to be specified explicitly in XAML markup.
This example shows how to display two XAML controls as implicit children of the MapControl. These controls appear on the map at the data bound locations.
<maps:MapControl>
<TextBox Text="Seattle" maps:MapControl.Location="{x:Bind SeattleLocation}"/>
<TextBox Text="Bellevue" maps:MapControl.Location="{x:Bind BellevueLocation}"/>
</maps:MapControl>
Set these locations by using a properties in your code-behind file.
public Geopoint SeattleLocation { get; set; }
public Geopoint BellevueLocation { get; set; }
This example shows how to display two XAML controls contained within a MapItemsControl.These controls appear on the map at the data bound locations.
<maps:MapControl>
<maps:MapItemsControl>
<TextBox Text="Seattle" maps:MapControl.Location="{x:Bind SeattleLocation}"/>
<TextBox Text="Bellevue" maps:MapControl.Location="{x:Bind BellevueLocation}"/>
</maps:MapItemsControl>
</maps:MapControl>
This example displays a collection of XAML elements bound to a MapItemsControl.
<maps:MapControl x:Name="MapControl" MapTapped="MapTapped" MapDoubleTapped="MapTapped" MapHolding="MapTapped">
<maps:MapItemsControl ItemsSource="{x:Bind LandmarkOverlays}">
<maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="Black" Tapped ="Overlay_Tapped">
<TextBlock maps:MapControl.Location="{Binding Location}" Text="{Binding Title}"
maps:MapControl.NormalizedAnchorPoint="0.5,0.5" FontSize="20" Margin="5"/>
</StackPanel>
</DataTemplate>
</maps:MapItemsControl.ItemTemplate>
</maps:MapItemsControl>
</maps:MapControl>
The ItemsSource
property in the example above is bound to a property of type IList in the code-behind file.
public sealed partial class Scenario1 : Page
{
public IList LandmarkOverlays { get; set; }
public MyClassConstructor()
{
SetLandMarkLocations();
this.InitializeComponent();
}
private void SetLandMarkLocations()
{
LandmarkOverlays = new List<MapElement>();
var pikePlaceIcon = new MapIcon
{
Location = new Geopoint(new BasicGeoposition
{ Latitude = 47.610, Longitude = -122.342 }),
Title = "Pike Place Market"
};
LandmarkOverlays.Add(pikePlaceIcon);
var SeattleSpaceNeedleIcon = new MapIcon
{
Location = new Geopoint(new BasicGeoposition
{ Latitude = 47.6205, Longitude = -122.3493 }),
Title = "Seattle Space Needle"
};
LandmarkOverlays.Add(SeattleSpaceNeedleIcon);
}
}
Working with layers
The examples in this guide add elements to a MapElementsLayer collection. Then they show how to add that collection to the Layers property of the map control. In previous releases, this guide showed you how to add map elements to the MapElements collection as follows:
var pikePlaceIcon = new MapIcon
{
Location = new Geopoint(new BasicGeoposition
{ Latitude = 47.610, Longitude = -122.342 }),
NormalizedAnchorPoint = new Point(0.5, 1.0),
ZIndex = 0,
Title = "Pike Place Market"
};
myMap.MapElements.Add(pikePlaceIcon);
While you can still use this approach, you'll miss out on some of the advantages of the new map layer model. By grouping your elements into layers, you can manipulate each layer independently from one another. For example, each layer has it's own set of events so you can respond to an event on a specific layer and perform an action specific to that event.
Also, you can bind XAML directly to a MapLayer. This is something that you can't do by using the MapElements collection.
One way you could do this is by using a view model class, code-behind a XAML page, and a XAML page.
View model class
public class LandmarksViewModel
{
public ObservableCollection<MapLayer> LandmarkLayer
{ get; } = new ObservableCollection<MapLayer>();
public LandmarksViewModel()
{
var MyElements = new List<MapElement>();
var pikePlaceIcon = new MapIcon
{
Location = new Geopoint(new BasicGeoposition
{ Latitude = 47.610, Longitude = -122.342 }),
Title = "Pike Place Market"
};
MyElements.Add(pikePlaceIcon);
var LandmarksLayer = new MapElementsLayer
{
ZIndex = 1,
MapElements = MyElements
};
LandmarkLayer.Add(LandmarksLayer);
}
Code-behind a XAML page
Connect the view model class to your code behind page.
public LandmarksViewModel ViewModel { get; set; }
public myMapPage()
{
this.InitializeComponent();
this.ViewModel = new LandmarksViewModel();
}
XAML page
In your XAML page, bind to the property in your view model class that returns the layer.
<maps:MapControl
x:Name="myMap" TransitFeaturesVisible="False" Loaded="MyMap_Loaded" Grid.Row="2"
MapServiceToken="Your token" Layers="{x:Bind ViewModel.LandmarkLayer}"/>