From 189aa9d0c795d05e0afd601857c7fedf925df20a Mon Sep 17 00:00:00 2001 From: Vamsi TP Date: Mon, 10 Jun 2024 14:31:33 +0530 Subject: [PATCH] Implementing AdvancedMarkerList based on MarkerList. TODO: Rename AdvancedMarkerView class to AdvancedMarkerElement (or AdvancedMarker) --- .../GoogleMapsComponents.csproj | 2 +- .../Maps/AdvancedMarkerView.cs | 6 +- .../Maps/Extension/AdvancedMarkerList.cs | 443 ++++++++++++++++++ ServerSideDemo/Pages/GMap.razor.cs | 5 + .../Pages/MapAdvancedMarkerListPage.razor | 15 + .../Pages/MapAdvancedMarkerListPage.razor.cs | 249 ++++++++++ ServerSideDemo/Shared/NavMenu.razor | 5 + 7 files changed, 723 insertions(+), 2 deletions(-) create mode 100644 GoogleMapsComponents/Maps/Extension/AdvancedMarkerList.cs create mode 100644 ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor create mode 100644 ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor.cs diff --git a/GoogleMapsComponents/GoogleMapsComponents.csproj b/GoogleMapsComponents/GoogleMapsComponents.csproj index 3c22b18d..d8b3c11d 100644 --- a/GoogleMapsComponents/GoogleMapsComponents.csproj +++ b/GoogleMapsComponents/GoogleMapsComponents.csproj @@ -15,7 +15,7 @@ 3.0 true BlazorGoogleMaps - 4.3.0 + 4.3.1 Rungwiroon QueueStack Solution BlazorGoogleMaps diff --git a/GoogleMapsComponents/Maps/AdvancedMarkerView.cs b/GoogleMapsComponents/Maps/AdvancedMarkerView.cs index b1da7136..0a3a4ab9 100644 --- a/GoogleMapsComponents/Maps/AdvancedMarkerView.cs +++ b/GoogleMapsComponents/Maps/AdvancedMarkerView.cs @@ -1,4 +1,5 @@ using Microsoft.JSInterop; + using System.Threading.Tasks; namespace GoogleMapsComponents.Maps; @@ -9,9 +10,12 @@ namespace GoogleMapsComponents.Maps; /// public class AdvancedMarkerView : ListableEntityBase { + // https://developers.google.com/maps/documentation/javascript/reference/3.55/advanced-markers + public const string GoogleMapAdvancedMarkerName = "google.maps.marker.AdvancedMarkerElement"; + public static async Task CreateAsync(IJSRuntime jsRuntime, AdvancedMarkerViewOptions? opts = null) { - var jsObjectRef = await JsObjectRef.CreateAsync(jsRuntime, "google.maps.marker.AdvancedMarkerView", opts); + var jsObjectRef = await JsObjectRef.CreateAsync(jsRuntime, GoogleMapAdvancedMarkerName, opts); var obj = new Marker(jsObjectRef); return obj; } diff --git a/GoogleMapsComponents/Maps/Extension/AdvancedMarkerList.cs b/GoogleMapsComponents/Maps/Extension/AdvancedMarkerList.cs new file mode 100644 index 00000000..3764091c --- /dev/null +++ b/GoogleMapsComponents/Maps/Extension/AdvancedMarkerList.cs @@ -0,0 +1,443 @@ +using Microsoft.JSInterop; + +using OneOf; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GoogleMapsComponents.Maps.Extension; + +/// +/// +/// A class able to manage a lot of AdvancedMarkerView objects and get / set their properties at the same time, eventually with different values +/// +/// +/// Main concept is that for each AdvancedMarkerView to be distinguished from other ones, it needs +/// to have a "unique key" with an "external world meaning", so not necessarily a GUID. +/// +/// +/// In real cases Markers are likely to be linked to places, activities, transit stops and so on -> So, what better way to choose as AdvancedMarkerView "unique key" the "id" of the object each marker is related to? +/// A string key has been selected as type due to its implicit versatility. +/// +/// +/// To create Markers, simply call AdvancedMarkerList.CreateAsync with a Dictionary of desired AdvancedMarkerView keys and AdvancedMarkerViewOptions values. +/// After that, a new instance of AdvancedMarkerList class will be returned with its Markers dictionary member populated with the corresponding results +/// +/// +/// At run-time is possible to:
+/// +/// add AdvancedMarkerView to the same AdvancedMarkerList class using AddMultipleAsync method (only keys not matching with existent AdvancedMarkerView keys will be created)
+/// Markers dictionary will contain "union distinct" of existent AdvancedMarkerView's keys and new keys
+///
+/// remove AdvancedMarkerView from the AdvancedMarkerList class (only AdvancedMarkerView having keys matching with existent keys will be removed)
+/// Markers dictionary will contain "original - required and found" AdvancedMarkerView's keys (eventually any is all AdvancedMarkerView are removed)
+///
+///
+///
+/// +/// Each definer getter properties can be used as follows:
+/// a) without parameter -> all eventually defined markers related property will be returned (if any)
+/// b) with a List<string> of keys -> all eventually matching keys with Markers Dictionary keys produces related markers property extraction (if any defined) +///
+/// +/// Each setter properties can be used as follows:
+/// With a Dictionary<string, {property type}> indicating for each AdvancedMarkerView (related to that key) the corresponding related property value. +///
+///
+public class AdvancedMarkerList : ListableEntityListBase +{ + public Dictionary Markers => BaseListableEntities; + + /// + /// Create markers list + /// + /// + /// Dictionary of desired AdvancedMarkerView keys and AdvancedMarkerViewOptions values. Key as any type unique key. Not necessarily Guid + /// New instance of AdvancedMarkerList class will be returned with its Markers dictionary member populated with the corresponding results + public static async Task CreateAsync(IJSRuntime jsRuntime, Dictionary opts) + { + var jsObjectRef = new JsObjectRef(jsRuntime, Guid.NewGuid()); + + Dictionary jsObjectRefs = await JsObjectRef.CreateMultipleAsync( + jsRuntime, + AdvancedMarkerView.GoogleMapAdvancedMarkerName, + opts.ToDictionary(e => e.Key, e => (object)e.Value)); + + Dictionary objs = jsObjectRefs.ToDictionary(e => e.Key, e => new AdvancedMarkerView(e.Value)); + var obj = new AdvancedMarkerList(jsObjectRef, objs); + + return obj; + } + + /// + /// Sync list over lifetime: Create and remove list depending on entity count; + /// entities will be removed, added or changed to mirror the given set. + /// + /// + /// The list to manage. May be null. + /// + /// + /// + /// + /// + /// The managed list. Assign to the variable you used as parameter. + /// + public static async Task SyncAsync(AdvancedMarkerList? list, + IJSRuntime jsRuntime, + Dictionary opts, + Action? clickCallback = null) + { + if (opts.Count == 0) + { + if (list != null) + { + await list.SetMultipleAsync(opts); + list = null; + } + } + else + { + if (list == null) + { + list = await AdvancedMarkerList.CreateAsync(jsRuntime, new Dictionary()); + if (clickCallback != null) + { + list.EntityClicked += (_, e) => + { + clickCallback(e.MouseEvent, e.Key, e.Entity); + }; + } + } + await list.SetMultipleAsync(opts); + } + return list; + } + + private AdvancedMarkerList(JsObjectRef jsObjectRef, Dictionary markers) + : base(jsObjectRef, markers) + { + } + + /// + /// Set the set of entities; entities will be removed, added or changed to mirror the given set. + /// + /// + /// + public async Task SetMultipleAsync(Dictionary opts) + { + await base.SetMultipleAsync(opts, AdvancedMarkerView.GoogleMapAdvancedMarkerName); + } + + /// + /// Only keys not matching with existent AdvancedMarkerView keys will be created + /// + /// + public async Task AddMultipleAsync(Dictionary opts) + { + await base.AddMultipleAsync(opts, AdvancedMarkerView.GoogleMapAdvancedMarkerName); + } + + public Task> GetAnimations(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getAnimation", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => Helper.ToEnum(r.Value))); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetClickables(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getClickable", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetCursors(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getCursor", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task>> GetIcons(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync>( + "getIcon", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult>(); + } + } + + public Task> GetLabels(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getLabel", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetPositions(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getPosition", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetShapes(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getShape", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetTitles(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getTitle", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + public Task> GetZIndexes(List? filterKeys = null) + { + var matchingKeys = ComputeMatchingKeys(filterKeys); + + if (matchingKeys.Any()) + { + Dictionary internalMapping = ComputeInternalMapping(matchingKeys); + Dictionary dictArgs = ComputeDictArgs(matchingKeys); + + return _jsObjectRef.InvokeMultipleAsync( + "getZIndex", + dictArgs).ContinueWith(e => e.Result.ToDictionary(r => internalMapping[new Guid(r.Key)], r => r.Value)); + } + else + { + return ComputeEmptyResult(); + } + } + + /// + /// Start an animation. + /// Any ongoing animation will be cancelled. + /// Currently supported animations are: BOUNCE, DROP. + /// Passing in null will cause any animation to stop. + /// + /// + public Task SetAnimations(Dictionary animations) + { + var dictArgs = animations.ToDictionary(e => Markers[e.Key].Guid, e => (object?)GetAnimationCode(e.Value)); + return _jsObjectRef.InvokeMultipleAsync("setAnimation", dictArgs); + } + + public int? GetAnimationCode(Animation? animation) + { + switch (animation) + { + case null: return null; + case Animation.Bounce: return 1; + case Animation.Drop: return 2; + default: return 0; + } + } + + /// + /// Sets the Clickable flag of one or more Markers to match a dictionary of marker keys and flag values. + /// + /// + /// + public Task SetClickables(Dictionary flags) + { + var dictArgs = flags.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setClickable", dictArgs); + } + + public Task SetCursors(Dictionary cursors) + { + var dictArgs = cursors.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setCursor", dictArgs); + } + + /// + /// Set Icon on each AdvancedMarkerView matching a param dictionary key to the param value with single JSInterop call. + /// + /// + /// + public Task SetIcons(Dictionary> icons) + { + var dictArgs = icons.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setIcon", + dictArgs); + } + + /// + public Task SetIcons(Dictionary icons) + { + var dictArgs = icons.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setIcon", dictArgs); + } + + /// + public Task SetIcons(Dictionary icons) + { + var dictArgs = icons.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setIcon", dictArgs); + } + + /// + /// Set Label on each AdvancedMarkerView matching a param dictionary key to the param value with single JSInterop call. + /// + /// + /// + public Task SetLabels(Dictionary> labels) + { + var dictArgs = labels.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setLabel", + dictArgs); + } + + /// + public Task SetLabels(Dictionary labels) + { + var dictArgs = labels.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setLabel", + dictArgs); + } + + /// + public Task SetLabels(Dictionary labels) + { + var dictArgs = labels.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setLabel", + dictArgs); + } + + public Task SetOpacities(Dictionary opacities) + { + var dictArgs = opacities.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync( + "setOpacity", + dictArgs); + } + + public Task SetPositions(Dictionary latLngs) + { + var dictArgs = latLngs.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setPosition", dictArgs); + } + + public Task SetShapes(Dictionary shapes) + { + var dictArgs = shapes.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setShape", dictArgs); + } + + public Task SetTitles(Dictionary titles) + { + var dictArgs = titles.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setTitle", dictArgs); + } + + public Task SetZIndexes(Dictionary zIndexes) + { + var dictArgs = zIndexes.ToDictionary(e => Markers[e.Key].Guid, e => (object)e.Value); + return _jsObjectRef.InvokeMultipleAsync("setZIndex", dictArgs); + } +} \ No newline at end of file diff --git a/ServerSideDemo/Pages/GMap.razor.cs b/ServerSideDemo/Pages/GMap.razor.cs index 5c0dde0c..d2f5f7fb 100644 --- a/ServerSideDemo/Pages/GMap.razor.cs +++ b/ServerSideDemo/Pages/GMap.razor.cs @@ -1,5 +1,10 @@ using GoogleMapsComponents.Maps; using Microsoft.AspNetCore.Components; + +#if DEBUG +using System; +#endif + using System.Threading.Tasks; namespace ServerSideDemo.Pages; diff --git a/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor b/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor new file mode 100644 index 00000000..487bb26d --- /dev/null +++ b/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor @@ -0,0 +1,15 @@ +@page "/map-advanced-marker-list" +@using GoogleMapsComponents + +

Google Advanced Map Markers List

+ + + + + + + + + + + \ No newline at end of file diff --git a/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor.cs b/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor.cs new file mode 100644 index 00000000..3bafa763 --- /dev/null +++ b/ServerSideDemo/Pages/MapAdvancedMarkerListPage.razor.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using GoogleMapsComponents; +using GoogleMapsComponents.Maps; +using GoogleMapsComponents.Maps.Coordinates; +using GoogleMapsComponents.Maps.Extension; + +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; + +using ServerSideDemo.Shared; + +namespace ServerSideDemo.Pages; + +public partial class MapAdvancedMarkerListPage +{ + private GoogleMap map1; + + private MapOptions mapOptions; + + private Stack markers = new Stack(); + + private List _events = new List(); + + private MapEventList eventList; + + private LatLngBounds bounds; + private AdvancedMarkerList _markerList; + + [Inject] + public IJSRuntime JsObjectRef { get; set; } + + protected override void OnInitialized() + { + mapOptions = new MapOptions() + { + Zoom = 13, + Center = new LatLngLiteral() + { + Lat = 13.505892, + Lng = 100.8162 + }, + MapTypeId = MapTypeId.Roadmap + }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + bounds = await LatLngBounds.CreateAsync(map1.JsRuntime); + } + } + + private async Task InvokeClustering() + { + var coordinates = new List() + { + new LatLngLiteral(){ Lng = 147.154312, Lat = -31.56391 }, + new LatLngLiteral(){ Lng = 150.363181, Lat = -33.718234 }, + new LatLngLiteral(){ Lng = 150.371124, Lat = -33.727111 }, + new LatLngLiteral(){ Lng = 151.209834, Lat = -33.848588 }, + new LatLngLiteral(){ Lng = 151.216968, Lat = -33.851702 }, + new LatLngLiteral(){ Lng = 150.863657, Lat = -34.671264 }, + new LatLngLiteral(){ Lng = 148.662905, Lat = -35.304724 }, + new LatLngLiteral(){ Lng = 175.699196, Lat = -36.817685 }, + new LatLngLiteral(){ Lng = 175.790222, Lat = -36.828611 }, + new LatLngLiteral(){ Lng = 145.116667, Lat = -37.75 }, + new LatLngLiteral(){ Lng = 145.128708, Lat = -37.759859 }, + new LatLngLiteral(){ Lng = 145.133858, Lat = -37.765015 }, + new LatLngLiteral(){ Lng = 145.143299, Lat = -37.770104 }, + new LatLngLiteral(){ Lng = 145.145187, Lat = -37.7737 }, + new LatLngLiteral(){ Lng = 145.137978, Lat = -37.774785 }, + new LatLngLiteral(){ Lng = 144.968119, Lat = -37.819616 }, + new LatLngLiteral(){ Lng = 144.695692, Lat = -38.330766 }, + new LatLngLiteral(){ Lng = 175.053218, Lat = -39.927193 }, + new LatLngLiteral(){ Lng = 174.865694, Lat = -41.330162 }, + new LatLngLiteral(){ Lng = 147.439506, Lat = -42.734358 }, + new LatLngLiteral(){ Lng = 147.501315, Lat = -42.734358 }, + new LatLngLiteral(){ Lng = 147.438, Lat = -42.735258 }, + new LatLngLiteral(){ Lng = 170.463352, Lat = -43.999792 }, + }; + + var markers = await GetMarkers(coordinates, map1.InteropObject); + + await MarkerClustering.CreateAsync(map1.JsRuntime, map1.InteropObject, markers); + + //initMap + //await JsObjectRef.InvokeAsync("initMap", map1.InteropObject.Guid.ToString(), markers); + } + + private async Task> GetMarkers(IEnumerable coords, Map map) + { + var result = new List(coords.Count()); + var index = 1; + foreach (var latLngLiteral in coords) + { + var marker = await AdvancedMarkerView.CreateAsync(map1.JsRuntime, new AdvancedMarkerViewOptions() + { + Position = latLngLiteral, + Map = map, + Content = $"Test {index++}", + //Icon = new Icon() + //{ + // Url = "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png" + //} + //Icon = "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png" + }); + + result.Add(marker); + } + + return result; + } + + private async Task AddMarker2() + { + var coordinates = new List() + { + new LatLngLiteral(){ Lng = 145.128708, Lat = -37.759859 }, + new LatLngLiteral(){ Lng = 145.133858, Lat = -37.765015 }, + new LatLngLiteral(){ Lng = 145.143299, Lat = -37.770104 }, + new LatLngLiteral(){ Lng = 145.145187, Lat = -37.7737 }, + new LatLngLiteral(){ Lng = 145.137978, Lat = -37.774785 }, + new LatLngLiteral(){ Lng = 144.968119, Lat = -37.819616 }, + new LatLngLiteral(){ Lng = 144.695692, Lat = -38.330766 }, + new LatLngLiteral(){ Lng = 175.053218, Lat = -39.927193 }, + new LatLngLiteral(){ Lng = 174.865694, Lat = -41.330162 }, + new LatLngLiteral(){ Lng = 147.439506, Lat = -42.734358 }, + new LatLngLiteral(){ Lng = 147.501315, Lat = -42.734358 }, + new LatLngLiteral(){ Lng = 147.438, Lat = -42.735258 }, + new LatLngLiteral(){ Lng = 170.463352, Lat = -43.999792 }, + }; + await AddMarkersGroup(coordinates); + } + + private async Task AddMarker1() + { + var coordinates = new List() + { + new LatLngLiteral(){ Lng = 147.154312, Lat = -31.56391 }, + new LatLngLiteral(){ Lng = 150.363181, Lat = -33.718234 }, + new LatLngLiteral(){ Lng = 150.371124, Lat = -33.727111 }, + new LatLngLiteral(){ Lng = 151.209834, Lat = -33.848588 }, + new LatLngLiteral(){ Lng = 151.216968, Lat = -33.851702 }, + new LatLngLiteral(){ Lng = 150.863657, Lat = -34.671264 }, + new LatLngLiteral(){ Lng = 148.662905, Lat = -35.304724 }, + new LatLngLiteral(){ Lng = 175.699196, Lat = -36.817685 }, + new LatLngLiteral(){ Lng = 175.790222, Lat = -36.828611 }, + new LatLngLiteral(){ Lng = 145.116667, Lat = -37.75 }, + }; + + + for (int index = 0; index < 200; index++) + { + var dif = (index * 0.001); + coordinates.Add(new LatLngLiteral() { Lng = 145.116667 + dif, Lat = -37.75 + dif }); + } + + await AddMarkersGroup(coordinates); + } + + private async Task AddMarkersGroup(IEnumerable coordinates) + { + if (_markerList == null) + { + _markerList = await AdvancedMarkerList.CreateAsync( + map1.JsRuntime, + coordinates.ToDictionary(s => Guid.NewGuid().ToString(), y => new AdvancedMarkerViewOptions() + { + Position = new LatLngLiteral() { Lng = y.Lng, Lat = y.Lat }, + Map = map1.InteropObject, + //Icon = new Icon() { Url = s.MarkerIconPath, ScaledSize = iconSize, Anchor = iconAnchor }, + Clickable = true, + Title = Guid.NewGuid().ToString(), + Visible = true + }) + ); + } + else + { + var cordDic = coordinates.ToDictionary(s => Guid.NewGuid().ToString(), y => new AdvancedMarkerViewOptions() + { + Position = new LatLngLiteral() { Lng = y.Lng, Lat = y.Lat }, + Map = map1.InteropObject, + //Icon = new Icon() { Url = s.MarkerIconPath, ScaledSize = iconSize, Anchor = iconAnchor }, + Clickable = true, + Title = Guid.NewGuid().ToString(), + Visible = true + }); + + await _markerList.AddMultipleAsync(cordDic); + } + + foreach (var latLngLiteral in coordinates) + { + await bounds.Extend(latLngLiteral); + } + + await FitBounds(); + } + + private async Task RemoveMarkers() + { + foreach (var markerListMarker in _markerList.Markers) + { + await markerListMarker.Value.SetMap(null); + } + + await _markerList.RemoveAllAsync(); + } + + private async Task RemoveMarker() + { + if (!markers.Any()) + { + return; + } + + var lastMarker = markers.Pop(); + await lastMarker.SetMap(null); + } + + private async Task Recenter() + { + if (!markers.Any()) + { + return; + } + + var lastMarker = markers.Peek(); + var center = await map1.InteropObject.GetCenter(); + await lastMarker.SetPosition(center); + } + + private async Task FitBounds() + { + if (await this.bounds.IsEmpty()) + { + return; + } + + var boundsLiteral = await bounds.ToJson(); + await map1.InteropObject.FitBounds(boundsLiteral, OneOf.OneOf.FromT0(5)); + } +} \ No newline at end of file diff --git a/ServerSideDemo/Shared/NavMenu.razor b/ServerSideDemo/Shared/NavMenu.razor index 3d23f2fc..8b4057bb 100644 --- a/ServerSideDemo/Shared/NavMenu.razor +++ b/ServerSideDemo/Shared/NavMenu.razor @@ -92,6 +92,11 @@ Map Marker List +