Skip to content

Commit 25a8bb6

Browse files
Add ability to add and remove shapes from drawing manager data source (#110)
* Add Drawing Manager component that can be used to add or remove shapes from drawing manager datasource * Add sample page for adding and removing shapes. * Add tests for DrawingManager.
1 parent ef97648 commit 25a8bb6

File tree

6 files changed

+589
-0
lines changed

6 files changed

+589
-0
lines changed

samples/AzureMapsControl.Sample/Components/Layout/NavMenu.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@
8686
<li>
8787
<NavLink class="dropdown-item" href="/Drawing/DrawingToolbarUpdate"> Toolbar update </NavLink>
8888
</li>
89+
<li>
90+
<NavLink class="dropdown-item" href="/Drawing/DrawingManagerLoadData"> Load data to drawing manager </NavLink>
91+
</li>
8992
</ul>
9093
</li>
9194
<NavLink class="nav-link" href="Indoor"> Indoor </NavLink>
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
@page "/Drawing/DrawingManagerLoadData"
2+
@rendermode InteractiveServer
3+
4+
@using AzureMapsControl.Components.Atlas
5+
@using AzureMapsControl.Components.Drawing
6+
@using AzureMapsControl.Components.Map
7+
8+
9+
<AzureMap Id="map"
10+
OnReady="MapReady"
11+
EventActivationFlags="MapEventActivationFlags.None().Enable(MapEventType.Ready)" />
12+
<div style="position: absolute; bottom: 10px; left: 10px; z-index: 1;">
13+
<button @onclick="AddRandomShape">Add Random Shape</button>
14+
<button @onclick="ClearShapes">Clear Shapes</button>
15+
</div>
16+
17+
18+
@code {
19+
private DrawingManager? _drawingManager;
20+
private Position _center = new Position(-122.33, 47.6);
21+
22+
public async Task MapReady(MapEventArgs eventArgs)
23+
{
24+
await eventArgs.Map.SetCameraOptionsAsync(options =>
25+
{
26+
options.Zoom = 10;
27+
options.Center = _center;
28+
});
29+
await eventArgs.Map.AddDrawingToolbarAsync(new AzureMapsControl.Components.Drawing.DrawingToolbarOptions
30+
{
31+
Buttons = new[]
32+
{
33+
AzureMapsControl.Components.Drawing.DrawingButton.DrawCircle,
34+
AzureMapsControl.Components.Drawing.DrawingButton.DrawLine,
35+
AzureMapsControl.Components.Drawing.DrawingButton.EditGeometry
36+
},
37+
Position = AzureMapsControl.Components.Controls.ControlPosition.TopRight,
38+
Style = AzureMapsControl.Components.Drawing.DrawingToolbarStyle.Dark
39+
});
40+
41+
var lineString = new AzureMapsControl.Components.Atlas.LineString(new[]
42+
{
43+
new AzureMapsControl.Components.Atlas.Position(-122.27577, 47.55938),
44+
new AzureMapsControl.Components.Atlas.Position(-122.29705, 47.60662),
45+
new AzureMapsControl.Components.Atlas.Position(-122.22358, 47.6367)
46+
});
47+
var shape = new AzureMapsControl.Components.Atlas.Shape<AzureMapsControl.Components.Atlas.LineString>(lineString);
48+
_drawingManager = eventArgs.Map.DrawingManager;
49+
await _drawingManager.AddShapesAsync(new[] { shape });
50+
}
51+
52+
private async Task AddRandomShape()
53+
{
54+
if (_drawingManager == null) return;
55+
56+
var random = new Random();
57+
var shapeType = random.Next(3);
58+
Shape shape;
59+
var numberOfPoints = random.Next(3, 5);
60+
var center = new Position(_center.Longitude + (random.NextDouble()-0.5) * 0.6, _center.Latitude + (random.NextDouble()-0.5) * 0.4);
61+
62+
switch (shapeType)
63+
{
64+
case 0: // Circle
65+
var radius = random.NextDouble() * 2000;
66+
shape = new Shape<Point>(new Point(center), new Dictionary<string, object>
67+
{
68+
{ "subType", "Circle" },
69+
{ "radius", radius }
70+
});
71+
break;
72+
case 1: // Polygon
73+
var polygonPositions = new List<Position>();
74+
for (var i = 0; i < numberOfPoints; i++)
75+
{
76+
polygonPositions.Add(new Position(center.Longitude + (random.NextDouble()-0.5) * 0.1, center.Latitude + (random.NextDouble()-0.5) * 0.1));
77+
}
78+
polygonPositions.Add(polygonPositions[0]);
79+
shape = new Shape<Polygon>(new Polygon(new[] { polygonPositions }));
80+
break;
81+
case 2: // Polyline
82+
var polylinePositions = new List<Position>();
83+
for (var i = 0; i < numberOfPoints; i++)
84+
{
85+
polylinePositions.Add(new Position(center.Longitude + (random.NextDouble()-0.5) * 0.1, center.Latitude + (random.NextDouble()-0.5) * 0.1));
86+
}
87+
shape = new Shape<LineString>(new LineString(polylinePositions));
88+
break;
89+
default:
90+
return;
91+
}
92+
93+
await _drawingManager.AddShapesAsync(new[] { shape });
94+
}
95+
96+
private async Task ClearShapes()
97+
{
98+
if (_drawingManager != null)
99+
{
100+
await _drawingManager.ClearAsync();
101+
}
102+
}
103+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
namespace AzureMapsControl.Components.Drawing
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
using AzureMapsControl.Components.Atlas;
8+
using AzureMapsControl.Components.Logger;
9+
using AzureMapsControl.Components.Runtime;
10+
11+
using Microsoft.Extensions.Logging;
12+
13+
14+
/// <summary>
15+
/// DrawingManager for the DrawingToolbar
16+
/// </summary>
17+
public sealed class DrawingManager
18+
{
19+
internal IMapJsRuntime JSRuntime { get; set; }
20+
internal ILogger Logger { get; set; }
21+
public bool Disposed { get; private set; }
22+
23+
/// <summary>
24+
/// List of shapes added to the data source
25+
/// </summary>
26+
private List<Shape> _sourceShapes;
27+
28+
/// <summary>
29+
/// Add shapes to the drawing manager data source
30+
/// </summary>
31+
/// <param name="shapes">Shapes to add</param>
32+
/// <returns></returns>
33+
/// <exception cref="Exceptions.ComponentNotAddedToMapException">The control has not been added to the map</exception>
34+
/// <exception cref="Exceptions.ComponentDisposedException">The control has already been disposed</exception>
35+
public async ValueTask AddShapesAsync(IEnumerable<Shape> shapes)
36+
{
37+
if (shapes == null || !shapes.Any())
38+
{
39+
return;
40+
}
41+
42+
EnsureJsRuntimeExists();
43+
EnsureNotDisposed();
44+
45+
if (_sourceShapes == null)
46+
{
47+
_sourceShapes = new List<Shape>();
48+
}
49+
50+
var lineStrings = shapes.OfType<Shape<LineString>>();
51+
if (lineStrings.Any())
52+
{
53+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{lineStrings.Count()} linestrings will be added");
54+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), lineStrings);
55+
}
56+
57+
var multiLineStrings = shapes.OfType<Shape<MultiLineString>>();
58+
if (multiLineStrings.Any())
59+
{
60+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{multiLineStrings.Count()} multilinestrings will be added");
61+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), multiLineStrings);
62+
}
63+
64+
var multiPoints = shapes.OfType<Shape<MultiPoint>>();
65+
if (multiPoints.Any())
66+
{
67+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{multiPoints.Count()} multipoints will be added");
68+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), multiPoints);
69+
}
70+
71+
var multiPolygons = shapes.OfType<Shape<MultiPolygon>>();
72+
if (multiPolygons.Any())
73+
{
74+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{multiPolygons.Count()} multipolygons will be added");
75+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), multiPolygons);
76+
}
77+
78+
var points = shapes.OfType<Shape<Point>>();
79+
if (points.Any())
80+
{
81+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{points.Count()} points will be added");
82+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), points);
83+
}
84+
85+
var polygons = shapes.OfType<Shape<Polygon>>();
86+
if (polygons.Any())
87+
{
88+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{polygons.Count()} polygons will be added");
89+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), polygons);
90+
}
91+
92+
var routePoints = shapes.OfType<Shape<RoutePoint>>();
93+
if (routePoints.Any())
94+
{
95+
Logger?.LogAzureMapsControlDebug(AzureMapLogEvent.Source_AddAsync, $"{routePoints.Count()} route points will be added");
96+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.AddShapes.ToDrawingNamespace(), routePoints);
97+
}
98+
99+
_sourceShapes.AddRange(shapes);
100+
}
101+
102+
/// <summary>
103+
/// Clear the drawing manager source
104+
/// </summary>
105+
/// <returns></returns>
106+
/// <exception cref="Exceptions.ComponentNotAddedToMapException">The control has not been added to the map</exception>
107+
/// <exception cref="Exceptions.ComponentDisposedException">The control has already been disposed</exception>
108+
public async ValueTask ClearAsync()
109+
{
110+
Logger?.LogAzureMapsControlInfo(AzureMapLogEvent.Source_ClearAsync, "Clearing drawing manager source");
111+
112+
EnsureJsRuntimeExists();
113+
EnsureNotDisposed();
114+
115+
_sourceShapes = null;
116+
await JSRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Source.Clear.ToDrawingNamespace());
117+
}
118+
119+
/// <summary>
120+
/// Mark the control as disposed
121+
/// </summary>
122+
/// <returns></returns>
123+
/// <exception cref="Exceptions.ComponentNotAddedToMapException">The control has not been added to the map</exception>
124+
/// <exception cref="Exceptions.ComponentDisposedException">The control has already been disposed</exception>
125+
internal void Dispose()
126+
{
127+
Logger?.LogAzureMapsControlInfo(AzureMapLogEvent.Source_DisposeAsync, "DrawingManager - Dispose");
128+
129+
EnsureJsRuntimeExists();
130+
EnsureNotDisposed();
131+
132+
Disposed = true;
133+
}
134+
135+
private void EnsureJsRuntimeExists()
136+
{
137+
if (JSRuntime is null)
138+
{
139+
throw new Exceptions.ComponentNotAddedToMapException();
140+
}
141+
}
142+
143+
private void EnsureNotDisposed()
144+
{
145+
if (Disposed)
146+
{
147+
throw new Exceptions.ComponentDisposedException();
148+
}
149+
}
150+
}
151+
}

src/AzureMapsControl.Components/Map/Map.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public sealed class Map
6767

6868
public DrawingToolbarOptions DrawingToolbarOptions { get; internal set; }
6969

70+
public DrawingManager DrawingManager { get; internal set; }
71+
7072
public IEnumerable<Control> Controls => _controls;
7173

7274
public IEnumerable<Layer> Layers => _layers;
@@ -352,6 +354,10 @@ await _jsRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Drawing.AddDrawin
352354
Events = drawingToolbarOptions.Events?.EnabledEvents
353355
},
354356
DotNetObjectReference.Create(_drawingToolbarEventInvokeHelper));
357+
DrawingManager = new DrawingManager() {
358+
JSRuntime = _jsRuntime,
359+
Logger = _logger
360+
};
355361
}
356362
}
357363

@@ -394,6 +400,8 @@ public async ValueTask RemoveDrawingToolbarAsync()
394400
{
395401
await _jsRuntime.InvokeVoidAsync(Constants.JsConstants.Methods.Drawing.RemoveDrawingToolbar.ToDrawingNamespace());
396402
DrawingToolbarOptions = null;
403+
DrawingManager?.Dispose();
404+
DrawingManager = null;
397405
}
398406
}
399407

src/AzureMapsControl.Components/typescript/drawing/drawing.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import * as azmaps from 'azure-maps-control';
33
import { EventHelper } from '../events/event-helper';
44
import { Core } from '../core/core';
55
import { DrawingEventArgs } from './drawing-event-args';
6+
import { Shape } from '../geometries/geometry';
7+
import { GeometryBuilder } from '../geometries/geometry-builder';
68

79
export class Drawing {
810

@@ -96,4 +98,13 @@ export class Drawing {
9698
});
9799
}
98100

101+
public static addShapes(shapes: Shape[]): void {
102+
const mapsShapes = shapes.map(shape => GeometryBuilder.buildShape(shape));
103+
this._drawingManager.getSource().add(mapsShapes);
104+
}
105+
106+
public static clear(): void {
107+
this._drawingManager.getSource().clear();
108+
}
109+
99110
}

0 commit comments

Comments
 (0)