Skip to content

Commit 3720e40

Browse files
authored
Merge pull request #447 from almightyju/master
Improve support for Features and data.setStyle(func) support
2 parents 0b4effb + 439c279 commit 3720e40

File tree

10 files changed

+265
-30
lines changed

10 files changed

+265
-30
lines changed

Demos/Demo.Ui.Shared/Pages/MapDataPage.razor

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
@using GoogleMapsComponents
33
@using GoogleMapsComponents.Maps
44
@using GoogleMapsComponents.Maps.Data
5+
@using System.Runtime.InteropServices
6+
7+
@implements IAsyncDisposable
8+
59
@*
610
The code for this sample was taken from below on 09/02/2020:
711
https://developers.google.com/maps/documentation/javascript/earthquakes#maps_earthquake_circles-typescript
@@ -13,6 +17,8 @@
1317
<button @onclick="@GetMapDataFeature">Get MapData (Feature)</button>
1418
<button @onclick="@GetMapDataGeoJsonLine">Get MapData (GeoJson Line)</button>
1519
<button @onclick="@GetMapDataGeoJsonPolygon">Get MapData (GeoJson Polygon)</button>
20+
<button @onclick="@UseSetStyleInJs">Set Global Style in JS function</button>
21+
<button @onclick="@UseSetStyleInWASM">Set Global Style in Blazor (WASM)</button>
1622
<button @onclick="@RemoveFeatures">Remove All Features</button>
1723

1824
<div>
@@ -34,9 +40,55 @@
3440
};
3541
}
3642

37-
private Task OnAfterMapInit()
43+
private async Task OnAfterMapInit()
44+
{
45+
await _map1.InteropObject.Data.AddListener<GoogleMapsComponents.Maps.Data.MouseEvent>("click", async e => OnClick(e));
46+
await _map1.InteropObject.Data.AddListener<GoogleMapsComponents.Maps.Data.MouseEvent>("mouseover", async e => OnMouseOver(e));
47+
await _map1.InteropObject.Data.AddListener<GoogleMapsComponents.Maps.Data.MouseEvent>("mouseout", async e => OnMouseOut(e));
48+
}
49+
50+
private async Task UseSetStyleInJs()
51+
{
52+
await _map1.InteropObject.Data.SetStyle("window.mapDataPage.getFeatureStyle");
53+
}
54+
55+
private async Task UseSetStyleInWASM()
56+
{
57+
if (RuntimeInformation.ProcessArchitecture == Architecture.Wasm)
58+
await _map1.InteropObject.Data.SetStyle(GetStyle);
59+
}
60+
61+
StyleOptions GetStyle(Feature feature)
62+
{
63+
return new StyleOptions
64+
{
65+
FillColor = "orange",
66+
StrokeColor = "purple",
67+
StrokeWeight = 2,
68+
StrokeOpacity = 1,
69+
FillOpacity = 0.5f
70+
};
71+
}
72+
73+
private async Task OnClick(GoogleMapsComponents.Maps.Data.MouseEvent e)
74+
{
75+
if (e.Feature == null)
76+
return;
77+
string fill = await e.Feature.GetProperty<string>("fillColor");
78+
await e.Feature.SetProperty("fillColor", fill == "orange" ? "green" : "orange");
79+
}
80+
81+
private async Task OnMouseOver(GoogleMapsComponents.Maps.Data.MouseEvent e)
82+
{
83+
await _map1.InteropObject.Data.RevertStyle();
84+
await _map1.InteropObject.Data.OverrideStyle(e.Feature, new StyleOptions() {
85+
StrokeWeight = 8f
86+
});
87+
}
88+
89+
private async Task OnMouseOut(GoogleMapsComponents.Maps.Data.MouseEvent e)
3890
{
39-
return Task.CompletedTask;
91+
await _map1.InteropObject.Data.RevertStyle();
4092
}
4193

4294
private async Task GetMapDataFeature()
@@ -122,16 +174,23 @@
122174
" \"features\": [" +
123175
"{ \"type\": \"Feature\"," +
124176
" \"id\": 3," +
125-
" \"properties\": { \"stroke\": \"#555555\", \"stroke-width\": 2, \"stroke-opacity\": 1 }, " +
126177
" \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [ [ [ 151.219, -33.888], [151.23, -33.888], [151.23, -33.869], [ 151.219, -33.869], [ 151.219, -33.888]]]}}]}";
127178

128-
await _map1.InteropObject.Data.AddGeoJson(jsonData);
129-
await _map1.InteropObject.Data.SetStyle(new StyleOptions { StrokeColor = "magenta", StrokeWeight = 2, FillColor = "magenta", FillOpacity = 0.3f });
179+
var features = await _map1.InteropObject.Data.AddGeoJson(jsonData);
180+
foreach(Feature feature in features) {
181+
await feature.SetProperty("fillColor", "red");
182+
await feature.SetProperty("strokeColor", "cyan");
183+
}
130184
}
131185

132186
private async Task RemoveFeatures()
133187
{
134188
await _map1.InteropObject.Data.RemoveAll();
135189
}
190+
191+
public async ValueTask DisposeAsync()
192+
{
193+
await _map1.DisposeAsync();
194+
}
136195
}
137196

Demos/ServerSideDemo/Pages/_Host.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
<script src="_framework/blazor.server.js"></script>
2121
<script src="https://unpkg.com/@@googlemaps/markerclusterer/dist/index.min.js"></script>
22+
<script src="js/clientSideFunctions.js"></script>
2223
<script src="_content/BlazorGoogleMaps/js/objectManager.js?v=1"></script>
2324
</body>
2425
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
window.mapDataPage = {
2+
getFeatureStyle: function (mapData, feature) {
3+
let fillColour = feature.getProperty('fillColor');
4+
let strokeColour = feature.getProperty('strokeColor');
5+
return {
6+
fillColor: fillColour,
7+
fillOpacity: 0.3,
8+
strokeColor: strokeColour,
9+
strokeWeight: 2,
10+
strokeOpacity: 1
11+
};
12+
}
13+
}

GoogleMapsComponents/Helper.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,28 @@ private static IEnumerable<object> MakeArgJsFriendly(IJSRuntime jsRuntime, IEnum
132132
return DotNetObjectReference.Create(new JsCallableAction(jsRuntime, action));
133133
default:
134134
{
135-
if (argType.IsGenericType
136-
&& (argType.GetGenericTypeDefinition() == typeof(Action<>)))
135+
if(argType.IsGenericType)
137136
{
138-
var genericArguments = argType.GetGenericArguments();
139-
140-
//Debug.WriteLine($"Generic args : {genericArguments.Count()}");
141-
142-
return DotNetObjectReference.Create(new JsCallableAction(jsRuntime, (Delegate)arg, genericArguments));
137+
var typeDefinition = argType.GetGenericTypeDefinition();
138+
if (typeDefinition == typeof(Action<>))
139+
{
140+
var genericArguments = argType.GetGenericArguments();
141+
142+
//Debug.WriteLine($"Generic args : {genericArguments.Count()}");
143+
144+
return DotNetObjectReference.Create(new JsCallableAction(jsRuntime, (Delegate)arg, genericArguments));
145+
}
146+
if(typeDefinition == typeof(Func<,>))
147+
{
148+
var genericArguments = argType.GetGenericArguments();
149+
return DotNetObjectReference.Create(new JsCallableFunc((Delegate)arg, genericArguments));
150+
}
143151
}
144152

145153
switch (arg)
146154
{
147155
case JsCallableAction _:
156+
case JsCallableFunc _:
148157
return DotNetObjectReference.Create(arg);
149158
case IJsObjectRef jsObjectRef:
150159
{

GoogleMapsComponents/JsCallableAction.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ public void Invoke(string args, string guid)
2727
return;
2828
}
2929

30-
var jArray = JsonDocument.Parse(args)
31-
.RootElement
32-
.EnumerateArray();
30+
var rootElement = JsonDocument.Parse(args).RootElement;
31+
JsonElement.ArrayEnumerator jArray = rootElement.ValueKind == JsonValueKind.Array
32+
? rootElement.EnumerateArray() : [];
3333

3434
var arguments = _argumentTypes.Zip(jArray, (type, jToken) => new { jToken, type })
3535
.Select(x =>
@@ -39,6 +39,18 @@ public void Invoke(string args, string guid)
3939
{
4040
actionArg.JsObjectRef = new JsObjectRef(_jsRuntime, new Guid(guid));
4141
}
42+
if (obj is Maps.Data.MouseEvent featureEvent)
43+
{
44+
//null any reference that might have come from the json since it won't be usable
45+
featureEvent.Feature = null;
46+
47+
if(x.jToken.TryGetProperty("featureUUID", out var featureUuidProp)
48+
&& Guid.TryParse(featureUuidProp.GetString(), out Guid featureUuid))
49+
{
50+
var featureObjectRef = new JsObjectRef(_jsRuntime, featureUuid);
51+
featureEvent.Feature = new Maps.Data.Feature(featureObjectRef);
52+
}
53+
}
4254

4355
return obj;
4456
})
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Microsoft.JSInterop;
2+
using System;
3+
using System.Linq;
4+
using System.Text.Json;
5+
6+
namespace GoogleMapsComponents;
7+
8+
public class JsCallableFunc
9+
{
10+
private readonly Delegate _delegate;
11+
private readonly Type[] _argumentTypes;
12+
13+
public JsCallableFunc(Delegate @delegate, params Type[] argumentTypes)
14+
{
15+
_delegate = @delegate;
16+
_argumentTypes = argumentTypes;
17+
if (argumentTypes.Length == 0)
18+
throw new ArgumentException("Must have at least 1 argument type, which would be the return type of the delegate", nameof(argumentTypes));
19+
}
20+
21+
[JSInvokable]
22+
public object? Invoke(string args, string guid)
23+
{
24+
if (string.IsNullOrWhiteSpace(args) || !_argumentTypes.Any())
25+
{
26+
object?[]? nullArgs = _argumentTypes.SkipLast(1).Select(x => (object?)null).ToArray();
27+
return _delegate.DynamicInvoke(nullArgs);
28+
}
29+
30+
var rootElement = JsonDocument.Parse(args).RootElement;
31+
JsonElement.ArrayEnumerator jArray = rootElement.ValueKind == JsonValueKind.Array
32+
? rootElement.EnumerateArray() : [];
33+
34+
var arguments = _argumentTypes.SkipLast(1).Zip(jArray, (type, jToken) => new { jToken, type })
35+
.Select(x =>
36+
{
37+
var obj = Helper.DeSerializeObject(x.jToken, x.type);
38+
return obj;
39+
})
40+
.ToArray();
41+
42+
return _delegate.DynamicInvoke(arguments);
43+
}
44+
}

GoogleMapsComponents/Maps/Data.cs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections;
77
using System.Collections.Generic;
88
using System.Linq;
9+
using System.Runtime.InteropServices;
910
using System.Threading.Tasks;
1011

1112
namespace GoogleMapsComponents.Maps;
@@ -151,11 +152,17 @@ public Task<string> GetDrawingMode()
151152
/// </summary>
152153
/// <param name="id"></param>
153154
/// <returns></returns>
154-
public Task<Feature> GetFeatureById(OneOf<int, string> id)
155+
public async Task<Feature?> GetFeatureById(OneOf<int, string> id)
155156
{
156-
return _jsObjectRef.InvokeAsync<Feature>(
157+
string featureUuid = await _jsObjectRef.InvokeAsync<string>(
157158
"getFeatureById",
158159
id.Value);
160+
161+
if (string.IsNullOrEmpty(featureUuid) || !Guid.TryParse(featureUuid, out Guid featureGuid))
162+
return null;
163+
164+
var featureRef = new JsObjectRef(_jsObjectRef.JSRuntime, featureGuid);
165+
return new Feature(featureRef);
159166
}
160167

161168
/// <summary>
@@ -299,18 +306,65 @@ public Task SetMap(Map map)
299306
/// <summary>
300307
/// Sets the style for all features in the collection.
301308
/// Styles specified on a per-feature basis via overrideStyle() continue to apply.
302-
/// Pass either an object with the desired style options, or a function that computes the style for each feature.
303-
/// The function will be called every time a feature's properties are updated.
304309
/// </summary>
305310
/// <param name="style"></param>
306311
/// <returns></returns>
307-
public Task SetStyle(OneOf<Func<Feature, StyleOptions>, StyleOptions> style)
312+
public Task SetStyle(StyleOptions style)
308313
{
309314
return _jsObjectRef.InvokeAsync(
310315
"setStyle",
311316
style);
312317
}
313318

319+
/// <summary>
320+
/// Sets the style for all features in the collection.
321+
/// Styles specified on a per-feature basis via overrideStyle() continue to apply.
322+
/// The function will be called every time a feature's properties are updated.
323+
/// WASM Only!
324+
/// </summary>
325+
/// <param name="styleFunc"></param>
326+
/// <returns></returns>
327+
/// <exception cref="PlatformNotSupportedException">
328+
/// Thrown if not running in WebAssembly as Google API expects the style function to be
329+
/// synchronous and that's only possible in WebAssembly. Instead use <seealso cref="SetStyle(string)"/>
330+
/// for client side usage
331+
/// </exception>
332+
public Task SetStyle(Func<Feature, StyleOptions> styleFunc)
333+
{
334+
if (RuntimeInformation.ProcessArchitecture != Architecture.Wasm)
335+
throw new PlatformNotSupportedException("Cannot be used outside of WebAssembly. Instead use SetStyle(jsFunctionName)");
336+
return _jsObjectRef.InvokeAsync(
337+
"setStyle",
338+
(Delegate)callback);
339+
340+
StyleOptions callback(string featureId)
341+
{
342+
Guid featureGuid = Guid.Parse(featureId);
343+
var feature = new Feature(new JsObjectRef(_jsObjectRef.JSRuntime, featureGuid));
344+
return styleFunc(feature);
345+
}
346+
}
347+
348+
/// <summary>
349+
/// Sets the style for all features by calling a javascript function
350+
/// </summary>
351+
/// <param name="jsFunctionName">The name of the function to call to get the style</param>
352+
/// <returns></returns>
353+
/// <remarks>
354+
/// Example function:
355+
/// window.getFeatureStyle = function (mapData, feature) {
356+
/// let fillColour = feature.getProperty('fillColor');
357+
/// return {
358+
/// fillColor: fillColour,
359+
/// fillOpacity: 0.3
360+
/// };
361+
/// }
362+
/// </remarks>
363+
public Task SetStyle(string jsFunctionName)
364+
{
365+
return _jsObjectRef.InvokeAsync("setStyleCallback", jsFunctionName);
366+
}
367+
314368
/// <summary>
315369
/// Exports the features in the collection to a GeoJSON object.
316370
/// </summary>

GoogleMapsComponents/Maps/Data/Feature.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,18 @@ public async Task<int> GetId()
6363
/// </summary>
6464
/// <param name="name"></param>
6565
/// <returns></returns>
66-
public object GetProperty(string name)
66+
public Task<T> GetProperty<T>(string name)
6767
{
68-
throw new NotImplementedException();
69-
}
68+
return _jsObjectRef.InvokeAsync<T>("getProperty", name);
69+
}
7070

7171
/// <summary>
7272
/// Removes the property with the given name.
7373
/// </summary>
7474
/// <param name="name"></param>
75-
public void RemoveProperty(string name)
75+
public Task RemoveProperty(string name)
7676
{
77-
throw new NotImplementedException();
77+
return _jsObjectRef.InvokeAsync("removeProperty", name);
7878
}
7979

8080
/// <summary>
@@ -92,9 +92,9 @@ public void SetGeometry(OneOf<Geometry, LatLngLiteral> newGeometry)
9292
/// </summary>
9393
/// <param name="name"></param>
9494
/// <param name="newValue"></param>
95-
public void SetProperty(string name, object newValue)
95+
public Task SetProperty(string name, object newValue)
9696
{
97-
throw new NotImplementedException();
97+
return _jsObjectRef.InvokeAsync("setProperty", name, newValue);
9898
}
9999

100100
/// <summary>

GoogleMapsComponents/Maps/Data/MouseEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
/// </summary>
88
public class MouseEvent : Maps.MouseEvent
99
{
10-
public Feature Feature { get; set; }
10+
public Feature? Feature { get; set; }
1111
}

0 commit comments

Comments
 (0)