Loading GeoJSON data in Blazor WebAssembly

Hello,

The documentation on this is very sparse. I found an example for Blazor Server in the forums and that works fine. However, when I try from the client side I cannot get it to work.

If I just provide the URL of a location of the file it eventually loads but it takes  over a minute !!!
I am trying to read in the file contents like the server side example and that throws an exception. Without any more documentation on how the parameters are used or what are valid values I am stuck. Below is my code. Using '@MapShapeData' works but takes very long. Using '@MapData' throws the exception below. Reading in the local version works fine from Blazor server.


@page "/maps-features"
@using System.Collections.ObjectModel
@using Syncfusion.Blazor.Maps
@inject HttpClient _client

<h2>Maps</h2>
<br />
<div id="ControlRegion">
    <SfMaps>
        <MapsLayers>
            <MapsLayer ShapeData="@MapShapeData" TValue="string">
            </MapsLayer>
        </MapsLayers>
    </SfMaps>
</div>
@code {
    string MapData;
    string path;
    protected override async Task OnInitializedAsync() {
        path = @"PH.lo.json";
        MapData = await _client.GetStringAsync(path);
    }

    public MapDataSettings MapShapeData = new MapDataSettings {
        async = false,
        dataOptions = "https://raw.githubusercontent.com/faeldon/philippines-json-maps/master/geojson/country/lowres/country.0.001.json",
        type = "GET"
    };
    public class MapDataSettings {
        public Boolean async { get; set; }
        public String dataOptions { get; set; }
        public String type { get; set; }
    };
}


crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: The requested operation requires an element of type 'Object', but the target element has type 'String'.
System.InvalidOperationException: The requested operation requires an element of type 'Object', but the target element has type 'String'.
   at System.Text.Json.JsonDocument.CheckExpectedType(JsonTokenType expected, JsonTokenType actual)
   at System.Text.Json.JsonDocument.TryGetNamedPropertyValue(Int32 index, ReadOnlySpan`1 propertyName, JsonElement& value)
   at System.Text.Json.JsonElement.TryGetProperty(ReadOnlySpan`1 propertyName, JsonElement& value)
   at System.Text.Json.JsonElement.TryGetProperty(String propertyName, JsonElement& value)
   at Syncfusion.Blazor.Maps.Internal.ShapeRender.ShapeRenderer(Double scale)
   at Syncfusion.Blazor.Maps.Internal.ShapeRender.Render()
   at Syncfusion.Blazor.Maps.SfMaps.InitiateRender(Boolean firstRender)
   at Syncfusion.Blazor.Maps.SfMaps.OnAfterScriptRendered()
   at Syncfusion.Blazor.SfBaseComponent.OnAfterRenderAsync(Boolean firstRender)
   at Syncfusion.Blazor.Maps.SfMaps.OnAfterRenderAsync(Boolean firstRender)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)

5 Replies

SB Swetha Babu Syncfusion Team April 1, 2021 12:08 PM UTC

Hi David,

Thank you for contacting Syncfusion support.

Query 1Using '@MapData' throws the exception

When we checked the provided code snippet, we came to know that you have set a string that contains the JSON file name to the "ShapeData" property. So, the reported issue occurs. If you need to set the JSON object in the Maps component, you must convert the JSON string from the file into a JSON object by deserializing it using NewtonSoft.Json or System.Text.Json libraries. When this JSON object is set in the ShapeData property of the Maps component, the map will be rendered. However, we have modified the provided code snippet to demonstrate the same.

Code Snippet:

<SfMaps>
    <MapsBorder Width="3" Color="red"></MapsBorder>
    <MapsZoomSettings Enable="false" />
    <MapsLayers>
        <MapsLayer ShapeData="@MapData" TValue="String">
            <MapsShapeSettings Fill="#E5E5E5">
            </MapsShapeSettings>
        </MapsLayer>
    </MapsLayers>
</SfMaps>

@code {
    object MapData;
    string path;
    protected override async Task OnInitializedAsync()
    {
        path = "https://raw.githubusercontent.com/faeldon/philippines-json-maps/master/geojson/country/lowres/country.0.001.json";
        MapData = Newtonsoft.Json.JsonConvert.DeserializeObject(await _client.GetStringAsync(path));
    }    
}

Query 2If I just provide the URL of a location of the file it eventually loads but it takes  over a minute

Since we parse and render the GeoJSON object in the C# code of the Maps component, there is a delay in rendering the Maps component. However, we are already working to overcome this delay in rendering the Maps and improve the component performance. This fix will be included in our upcoming weekly NuGet release which is expected to be available by the mid of April 2021.

Please let us know if you need any further assistance.

Regards,
Swetha Babu



DA David April 1, 2021 11:30 PM UTC

Thank you very much for answering my questions and even giving me a working version! 
I tried it and it worked fine (except for the tremendous delay). I even tested the local version of the file and that worked also.

It is amazing that it is so slow to show this very low res map when bing or google maps shows tremendous details very quickly. 
I am happy to hear that syncfusion is working on this and look forward to seeing a (hopefully much) faster version soon.

I have follow-on questions:
1) Why is 'TValue set to string when we are passing an object? Is there any documentation available for this and ShapeData that really explain what they are doing and what their possible values are? For example, how does the object containing dataOptions, async, and type relate to a GeoJSON object? 

2) Is there any way to cache this GeoJSON data with WebAssembly? It would seem that the file we are reading could be cached just like a standard web resource since it is static data but can you please help me understand more about our options with this? 

Thanks again!!!
I am getting really excited about SyncFusion! 


IR Indumathi Ravi Syncfusion Team April 3, 2021 09:43 AM UTC

Hi David, 
  
Thanks for your update. We have analyzed your queries and please find our responses below. 
  
Query1: Why is 'TValue set to string when we are passing an object? Is there any documentation available for this and ShapeData that really explain what they are doing and what their possible values are? For example, how does the object containing dataOptions, async, and type relate to a GeoJSON object 
  
The "ShapeData" property is used to render the map shapes that have bounded. However, MapData meaning is not expressed in TValue.  If we use the datasource property in that case,  we need to access the fields inside the datasource. For this scenario, we need to set the TValue as an object. Then, we can quickly access the datasource fields. For another case, we need to set the TValue as a string. Currently, there is no documentation to explain this scenario, but we will consider and include it in our upcoming days. 
  
Query2: Is there any way to cache this GeoJSON data with WebAssembly? It would seem that the file we are reading could be cached just like a standard web resource since it is static data but can you please help me understand more about our options with this?  
  
We have created a sample to cache the GeoJSON data with WebAssembly project. The following sample illustrates how the "dataValue" variable is used to store the MapData as a string and then transfer the string value to the script side. On the script side, we have converted the string value to an object and then stored it in localStorage. We then returned the local storage value to C# for the Maps component, where it will be deserialized and bound to the MapData. 

 
 
[JS]
<script>
        window.open = {
            setLocalData: function (data) {
                var dataObj = JSON.parse(data);
                localStorage["mapdata"] = JSON.stringify(dataObj);

            },
            getLocalData: function () {
                return localStorage["mapdata"];
            }
        }
    </script>

[C#]

    object MapData;
    string path;
    object dataValue;

    protected override async Task OnInitializedAsync()
    {
        path = "https://raw.githubusercontent.com/faeldon/philippines-json-maps/master/geojson/country/lowres/country.0.001.json";
        dataValue = await _client.GetStringAsync(path);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);
        if (dataValue != null)
        {
            await JSRuntime.InvokeAsync<string>("open.setLocalData", dataValue);
            MapData = Newtonsoft.Json.JsonConvert.DeserializeObject(await JSRuntime.InvokeAsync<string>("open.getLocalData"));
        }
    }

 
Thanks, 
Indumathi R 



DA David April 15, 2021 08:02 PM UTC

Thank you very much for your answers and even providing a working example!



SB Swetha Babu Syncfusion Team April 16, 2021 01:37 PM UTC

Hi David, 

Most Welcome! Please get back to us if you would require any further assistance.

Regards,
Swetha Babu 


Loader.
Up arrow icon