Skip to content
This repository was archived by the owner on Oct 20, 2022. It is now read-only.

Commit 005772e

Browse files
committed
Moved old polygon and polyline custom renderer pages here
1 parent 36fadcc commit 005772e

File tree

4 files changed

+610
-0
lines changed

4 files changed

+610
-0
lines changed
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
---
2+
title: "Highlighting a Region on a Map"
3+
description: "This article explains how to add a polygon overlay to a map, to highlight a region on the map. Polygons are a closed shape and have their interiors filled in."
4+
ms.prod: xamarin
5+
ms.assetid: E79EB2CF-8DD6-44A8-B47D-5F0A94FB0A63
6+
ms.technology: xamarin-forms
7+
author: davidbritch
8+
ms.author: dabritch
9+
ms.date: 11/29/2017
10+
---
11+
12+
# Highlighting a Region on a Map
13+
14+
[![Download Sample](~/media/shared/download.png) Download the sample](https://docs.microsoft.com/samples/xamarin/xamarin-forms-samples/customrenderers-map-polygon)
15+
16+
_This article explained how to add a polygon overlay to a map, to highlight a region on the map. Polygons are a closed shape and have their interiors filled in._
17+
18+
## Overview
19+
20+
An overlay is a layered graphic on a map. Overlays support drawing graphical content that scales with the map as it is zoomed. The following screenshots show the result of adding a polygon overlay to a map:
21+
22+
![](polygon-map-overlay-images/screenshots.png)
23+
24+
When a [`Map`](xref:Xamarin.Forms.Maps.Map) control is rendered by a Xamarin.Forms application, in iOS the `MapRenderer` class is instantiated, which in turn instantiates a native `MKMapView` control. On the Android platform, the `MapRenderer` class instantiates a native `MapView` control. On the Universal Windows Platform (UWP), the `MapRenderer` class instantiates a native `MapControl`. The rendering process can be taken advantage of to implement platform-specific map customizations by creating a custom renderer for a `Map` on each platform. The process for doing this is as follows:
25+
26+
1. [Create](#Creating_the_Custom_Map) a Xamarin.Forms custom map.
27+
1. [Consume](#Consuming_the_Custom_Map) the custom map from Xamarin.Forms.
28+
1. [Customize](#Customizing_the_Map) the map by creating a custom renderer for the map on each platform.
29+
30+
> [!NOTE]
31+
> [`Xamarin.Forms.Maps`](xref:Xamarin.Forms.Maps) must be initialized and configured before use. For more information, see [`Maps Control`](~/xamarin-forms/user-interface/map/index.md).
32+
33+
For information about customizing a map using a custom renderer, see [Customizing a Map Pin](~/xamarin-forms/app-fundamentals/custom-renderer/map/customized-pin.md).
34+
35+
<a name="Creating_the_Custom_Map" />
36+
37+
### Creating the Custom Map
38+
39+
Create a subclass of the [`Map`](xref:Xamarin.Forms.Maps.Map) class, that adds a `ShapeCoordinates` property:
40+
41+
```csharp
42+
public class CustomMap : Map
43+
{
44+
public List<Position> ShapeCoordinates { get; set; }
45+
46+
public CustomMap ()
47+
{
48+
ShapeCoordinates = new List<Position> ();
49+
}
50+
}
51+
```
52+
53+
The `ShapeCoordinates` property will store a collection of coordinates that define the region to be highlighted.
54+
55+
<a name="Consuming_the_Custom_Map" />
56+
57+
### Consuming the Custom Map
58+
59+
Consume the `CustomMap` control by declaring an instance of it in the XAML page instance:
60+
61+
```xaml
62+
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
63+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
64+
xmlns:local="clr-namespace:MapOverlay;assembly=MapOverlay"
65+
x:Class="MapOverlay.MapPage">
66+
<ContentPage.Content>
67+
<local:CustomMap x:Name="customMap" MapType="Street" WidthRequest="{x:Static local:App.ScreenWidth}" HeightRequest="{x:Static local:App.ScreenHeight}" />
68+
</ContentPage.Content>
69+
</ContentPage>
70+
```
71+
72+
Alternatively, consume the `CustomMap` control by declaring an instance of it in the C# page instance:
73+
74+
```csharp
75+
public class MapPageCS : ContentPage
76+
{
77+
public MapPageCS ()
78+
{
79+
var customMap = new CustomMap {
80+
MapType = MapType.Street,
81+
WidthRequest = App.ScreenWidth,
82+
HeightRequest = App.ScreenHeight
83+
};
84+
...
85+
Content = customMap;
86+
}
87+
}
88+
```
89+
90+
Initialize the `CustomMap` control as required:
91+
92+
```csharp
93+
public partial class MapPage : ContentPage
94+
{
95+
public MapPage ()
96+
{
97+
...
98+
customMap.ShapeCoordinates.Add (new Position (37.797513, -122.402058));
99+
customMap.ShapeCoordinates.Add (new Position (37.798433, -122.402256));
100+
customMap.ShapeCoordinates.Add (new Position (37.798582, -122.401071));
101+
customMap.ShapeCoordinates.Add (new Position (37.797658, -122.400888));
102+
103+
customMap.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (37.79752, -122.40183), Distance.FromMiles (0.1)));
104+
}
105+
}
106+
```
107+
108+
This initialization specifies a series of latitude and longitude coordinates, to define the region of the map to be highlighted. It then positions the map's view with the [`MoveToRegion`](xref:Xamarin.Forms.Maps.Map.MoveToRegion*) method, which changes the position and zoom level of the map by creating a [`MapSpan`](xref:Xamarin.Forms.Maps.MapSpan) from a [`Position`](xref:Xamarin.Forms.Maps.Position) and a [`Distance`](xref:Xamarin.Forms.Maps.Distance).
109+
110+
<a name="Customizing_the_Map" />
111+
112+
### Customizing the Map
113+
114+
A custom renderer must now be added to each application project to add the polygon overlay to the map.
115+
116+
#### Creating the Custom Renderer on iOS
117+
118+
Create a subclass of the `MapRenderer` class and override its `OnElementChanged` method to add the polygon overlay:
119+
120+
```csharp
121+
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
122+
namespace MapOverlay.iOS
123+
{
124+
public class CustomMapRenderer : MapRenderer
125+
{
126+
MKPolygonRenderer polygonRenderer;
127+
128+
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
129+
{
130+
base.OnElementChanged(e);
131+
132+
if (e.OldElement != null) {
133+
var nativeMap = Control as MKMapView;
134+
if (nativeMap != null) {
135+
nativeMap.RemoveOverlays(nativeMap.Overlays);
136+
nativeMap.OverlayRenderer = null;
137+
polygonRenderer = null;
138+
}
139+
}
140+
141+
if (e.NewElement != null) {
142+
var formsMap = (CustomMap)e.NewElement;
143+
var nativeMap = Control as MKMapView;
144+
145+
nativeMap.OverlayRenderer = GetOverlayRenderer;
146+
147+
CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.ShapeCoordinates.Count];
148+
149+
int index = 0;
150+
foreach (var position in formsMap.ShapeCoordinates)
151+
{
152+
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
153+
index++;
154+
}
155+
156+
var blockOverlay = MKPolygon.FromCoordinates(coords);
157+
nativeMap.AddOverlay(blockOverlay);
158+
}
159+
}
160+
...
161+
}
162+
}
163+
164+
```
165+
166+
This method performs the following configuration, provided that the custom renderer is attached to a new Xamarin.Forms element:
167+
168+
- The `MKMapView.OverlayRenderer` property is set to a corresponding delegate.
169+
- The collection of latitude and longitude coordinates are retrieved from the `CustomMap.ShapeCoordinates` property and stored as an array of `CLLocationCoordinate2D` instances.
170+
- The polygon is created by calling the static `MKPolygon.FromCoordinates` method, which specifies the latitude and longitude of each point.
171+
- The polygon is added to the map by calling the `MKMapView.AddOverlay` method. This method automatically closes the polygon by drawing a line that connects the first and last points.
172+
173+
Then, implement the `GetOverlayRenderer` method to customize the rendering of the overlay:
174+
175+
```csharp
176+
public class CustomMapRenderer : MapRenderer
177+
{
178+
MKPolygonRenderer polygonRenderer;
179+
...
180+
181+
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
182+
{
183+
if (polygonRenderer == null && !Equals(overlayWrapper, null)) {
184+
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
185+
polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon) {
186+
FillColor = UIColor.Red,
187+
StrokeColor = UIColor.Blue,
188+
Alpha = 0.4f,
189+
LineWidth = 9
190+
};
191+
}
192+
return polygonRenderer;
193+
}
194+
}
195+
```
196+
197+
#### Creating the Custom Renderer on Android
198+
199+
Create a subclass of the `MapRenderer` class and override its `OnElementChanged` and `OnMapReady` methods to add the polygon overlay:
200+
201+
```csharp
202+
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
203+
namespace MapOverlay.Droid
204+
{
205+
public class CustomMapRenderer : MapRenderer
206+
{
207+
List<Position> shapeCoordinates;
208+
209+
public CustomMapRenderer(Context context) : base(context)
210+
{
211+
}
212+
213+
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
214+
{
215+
base.OnElementChanged(e);
216+
217+
if (e.OldElement != null)
218+
{
219+
// Unsubscribe
220+
}
221+
222+
if (e.NewElement != null)
223+
{
224+
var formsMap = (CustomMap)e.NewElement;
225+
shapeCoordinates = formsMap.ShapeCoordinates;
226+
Control.GetMapAsync(this);
227+
}
228+
}
229+
230+
protected override void OnMapReady(Android.Gms.Maps.GoogleMap map)
231+
{
232+
base.OnMapReady(map);
233+
234+
var polygonOptions = new PolygonOptions();
235+
polygonOptions.InvokeFillColor(0x66FF0000);
236+
polygonOptions.InvokeStrokeColor(0x660000FF);
237+
polygonOptions.InvokeStrokeWidth(30.0f);
238+
239+
foreach (var position in shapeCoordinates)
240+
{
241+
polygonOptions.Add(new LatLng(position.Latitude, position.Longitude));
242+
}
243+
NativeMap.AddPolygon(polygonOptions);
244+
}
245+
}
246+
}
247+
```
248+
249+
The `OnElementChanged` method retrieves the collection of latitude and longitude coordinates from the `CustomMap.ShapeCoordinates` property and stores them in a member variable. It then calls the `MapView.GetMapAsync` method, which gets the underlying `GoogleMap` that is tied to the view, provided that the custom renderer is attached to a new Xamarin.Forms element. Once the `GoogleMap` instance is available, the `OnMapReady` method will be invoked, where the polygon is created by instantiating a `PolygonOptions` object that specifies the latitude and longitude of each point. The polygon is then added to the map by calling the `NativeMap.AddPolygon` method. This method automatically closes the polygon by drawing a line that connects the first and last points.
250+
251+
#### Creating the Custom Renderer on the Universal Windows Platform
252+
253+
Create a subclass of the `MapRenderer` class and override its `OnElementChanged` method to add the polygon overlay:
254+
255+
```csharp
256+
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
257+
namespace MapOverlay.UWP
258+
{
259+
public class CustomMapRenderer : MapRenderer
260+
{
261+
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
262+
{
263+
base.OnElementChanged(e);
264+
265+
if (e.OldElement != null)
266+
{
267+
// Unsubscribe
268+
}
269+
270+
if (e.NewElement != null)
271+
{
272+
var formsMap = (CustomMap)e.NewElement;
273+
var nativeMap = Control as MapControl;
274+
275+
var coordinates = new List<BasicGeoposition>();
276+
foreach (var position in formsMap.ShapeCoordinates)
277+
{
278+
coordinates.Add(new BasicGeoposition() { Latitude = position.Latitude, Longitude = position.Longitude });
279+
}
280+
281+
var polygon = new MapPolygon();
282+
polygon.FillColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
283+
polygon.StrokeColor = Windows.UI.Color.FromArgb(128, 0, 0, 255);
284+
polygon.StrokeThickness = 5;
285+
polygon.Path = new Geopath(coordinates);
286+
nativeMap.MapElements.Add(polygon);
287+
}
288+
}
289+
}
290+
}
291+
```
292+
293+
This method performs the following operations, provided that the custom renderer is attached to a new Xamarin.Forms element:
294+
295+
- The collection of latitude and longitude coordinates are retrieved from the `CustomMap.ShapeCoordinates` property and converted into a `List` of `BasicGeoposition` coordinates.
296+
- The polygon is created by instantiating a `MapPolygon` object. The `MapPolygon` class is used to display a multi-point shape on the map by setting its `Path` property to a `Geopath` object that contains the shape coordinates.
297+
- The polygon is rendered on the map by adding it to the `MapControl.MapElements` collection. Note that the polygon will be automatically closed by drawing a line that connects the first and last points.
298+
299+
## Summary
300+
301+
This article explained how to add a polygon overlay to a map, to highlight a region of the map. Polygons are a closed shape and have their interiors filled in.
302+
303+
## Related Links
304+
305+
- [Polygon Map Overlay (sample)](https://docs.microsoft.com/samples/xamarin/xamarin-forms-samples/customrenderers-map-polygon)
306+
- [Customizing a Map Pin](~/xamarin-forms/app-fundamentals/custom-renderer/map/customized-pin.md)
307+
- [Xamarin.Forms.Maps](xref:Xamarin.Forms.Maps)
Loading

0 commit comments

Comments
 (0)