Skip to content

Commit

Permalink
#14: redesigned the external data source system to optimize JsonSeria…
Browse files Browse the repository at this point in the history
…lizer memory usage
  • Loading branch information
bmotmans committed Aug 9, 2023
1 parent c6901a8 commit fe5bad9
Show file tree
Hide file tree
Showing 20 changed files with 456 additions and 265 deletions.
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,34 141,50 @@ See [full example](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vi

### External Data Sources (fetch)

Any `Data` property of type `object?` accepts a `ExternalDataSource` allowing you to specify the external data source.
Any `Data` property of type `object?` accepts a `ExternalDataSourceRef` allowing you to specify a reference to an external data source.
```
... = new ExternalDataSourceRef(dataSource); // also accepts a path parameter referring to a child of the data source, e.g.: children.nodes
```

An array of `ExternalDataSource` instances must be supplied to the the `EChart` `ExternalDataSources` parameter.
```
<Vizor.ECharts.EChart ExternalDataSources="@(new[] { extData })" ... />
```

Some examples on how to construct `ExternalDataSource` instances:
```
Data = new ExternalDataSource("https://example.com/api/data/sunburst_simple.json")
... = new ExternalDataSource("https://example.com/api/data/sunburst_simple.json")
```
See [full example](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vizor.ECharts.Samples/Areas/Sunburst/SimpleSunburstChart.razor).

It is also possible to provide a *simple* path expression to retrieve only a part of the external data:
```
Data = new ExternalDataSource("https://example.com/api/data/sankey_simple.json") { Path = "nodes" }
... = new ExternalDataSource("https://example.com/api/data/sankey_simple.json", path: "nodes")
```
See [full example](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vizor.ECharts.Samples/Areas/Sankey/SankeyWithLevelsChart.razor).

Or you can execute a function after load to manipulate the loaded data:
```
private static ExternalDataSource graph = new ExternalDataSource("/data/les-miserables.json", ExternalDataFetchAs.Json)
... = new ExternalDataSource("/data/les-miserables.json", ExternalDataFetchAs.Json)
{
AfterLoad = new JavascriptFunction(@"function (graph) {
graph.nodes.forEach(function (node) { node.symbolSize = 5; });
return graph;
}")
};
```
See *Javascript functions* for more details.
See [full example](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vizor.ECharts.Samples/Areas/Graph/ForceLayoutGraphChart.razor).

The *Javascript functions* chapter in the readme for more details about JS functions.


Additional credentials, headers, policies, ... can also be supplied.
See [ExternalDataSource](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vizor.ECharts/Types/ExternalDataSource.cs) and [FetchOptions](https://github.com/datahint-eu/vizor-echarts/blob/main/src/Vizor.ECharts/Types/FetchOptions.cs) for more details.


**Remark: never make an `ExternalDataSource` static, you need 1 instance per chart**


### External Data Source References

In case you need to re-use an external data source multiple times, you can use an `ExternalDataSourceRef`.
Expand Down
49 changes: 31 additions & 18 deletions src/Vizor.ECharts.Samples/Areas/Geo/BelgianMunicipalityMap.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 2,49 @@
@using Vizor.ECharts;
@using Microsoft.AspNetCore.Components.Web;

<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" Width="1000px" Height="1000px" @ref="@chart" />
@if (isInitialized)
{
<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" ExternalDataSources="@(new[] { mapSource })" Width="1000px" Height="1000px" @ref="@chart" />
}

@code {

private EChart? chart = null;
private ExternalDataSource? mapSource = null;
private IMapDefinition[]? maps = null;
private bool isInitialized = false;

// Geo data: https://github.com/bmesuere/belgium-topojson
protected override void OnInitialized()
{
mapSource = new("/data/BE_mun.json");

maps = new[]
{
new GeoMapDefinition("BE", mapSource)
};

isInitialized = true;
}

private ChartOptions mapOptions = new()
{
Tooltip = new()
{
Tooltip = new()
{
},
Series = new()
},
Series = new()
{
new MapSeries()
{
new MapSeries()
Id = "population",
Roam = Roam.True,
Map = "BE",
ItemStyle = new()
{
Id = "population",
Roam = Roam.True,
Map = "BE",
ItemStyle = new()
{
AreaColor = "white",
}
AreaColor = "white",
}
}
};

private IMapDefinition[] maps = new[]
{
new GeoMapDefinition("BE", new ExternalDataSource("/data/BE_mun.json"))
}
};
}
25 changes: 20 additions & 5 deletions src/Vizor.ECharts.Samples/Areas/Geo/FlightSeatsGeoMap.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 2,34 @@
@using Vizor.ECharts;
@using Microsoft.AspNetCore.Components.Web;

<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" Width="1000px" Height="1000px" @ref="@chart" />
@if (isInitialized)
{
<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" ExternalDataSources="@(new[] { mapSource })" Width="1000px" Height="1000px" @ref="@chart" />
}

@code {

// see https://echarts.apache.org/examples/en/editor.html?c=geo-seatmap-flight
private EChart? chart = null;
private ExternalDataSource? mapSource = null;
private IMapDefinition[]? maps = null;
private bool isInitialized = false;

private static readonly string[] takenSeatNames = new[] { "26E", "26D", "26C", "25D", "23C", "21A", "20F" };

protected override void OnInitialized()
{
mapSource = new("/data/flight-seats.svg", fetchAs: ExternalDataFetchAs.String);

maps = new[]
{
new SvgMapDefinition("flight-seats", mapSource)
};

isInitialized = true;
}

private ChartOptions mapOptions = new()
{
Tooltip = new()
Expand Down Expand Up @@ -97,8 115,5 @@
return regions;
}

private IMapDefinition[] maps = new[]
{
new SvgMapDefinition("flight-seats", new ExternalDataSource("/data/flight-seats.svg", fetchAs: ExternalDataFetchAs.String))
};

}
25 changes: 18 additions & 7 deletions src/Vizor.ECharts.Samples/Areas/Geo/UsaGeoMap.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 2,20 @@
@using Vizor.ECharts;
@using Microsoft.AspNetCore.Components.Web;

<div class="btn btn-primary" @onclick="ToggleChart">Toggle Bar &lt;&gt; Map</div>
<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" Width="1000px" Height="1000px" @ref="@chart" />
@if (isInitialized)
{
<div class="btn btn-primary" @onclick="ToggleChart">Toggle Bar &lt;&gt; Map</div>
<Vizor.ECharts.EChart Options="@mapOptions" Maps="maps" ExternalDataSources="@(new[] { mapSource })" Width="1000px" Height="1000px" @ref="@chart" />
}

@code {

// see https://echarts.apache.org/examples/en/editor.html?c=map-bar-morph
private EChart? chart = null;
private ExternalDataSource? mapSource = null;
private IMapDefinition[]? maps = null;
private bool isInitialized = false;

private static readonly List<StateData> data;
static UsaGeoMap()
Expand Down Expand Up @@ -73,7 79,17 @@
data.Sort((x, y) => x.Value.CompareTo(y.Value));
}

protected override void OnInitialized()
{
mapSource = new("/data/USA.json");

maps = new[]
{
new GeoMapDefinition("USA", mapSource, new UsaMapOptions())
};

isInitialized = true;
}

private ChartOptions mapOptions = new()
{
Expand Down Expand Up @@ -137,11 153,6 @@
}
};

private IMapDefinition[] maps = new[]
{
new GeoMapDefinition("USA", new ExternalDataSource("/data/USA.json"), new UsaMapOptions())
};

public struct StateData
{
public string Name { get; set; }
Expand Down
88 changes: 50 additions & 38 deletions src/Vizor.ECharts.Samples/Areas/Graph/ForceLayoutGraphChart.razor
Original file line number Diff line number Diff line change
@@ -1,53 1,65 @@
@using Vizor.ECharts;

<Vizor.ECharts.EChart Options="@options" Width="1000px" Height="1000px" />
@if (isInitialized)
{
<Vizor.ECharts.EChart Options="@options" ExternalDataSources="@(new[] { graph })" Width="1000px" Height="1000px" />
}

@code {

// see https://echarts.apache.org/examples/en/editor.html?c=graph-force
// see https://echarts.apache.org/examples/en/editor.html?c=graph-force
private static ExternalDataSource graph = new ExternalDataSource("/data/les-miserables.json", ExternalDataFetchAs.Json)
{
AfterLoad = new JavascriptFunction(@"function (graph) {
graph.nodes.forEach(function (node) { node.symbolSize = 5; });
return graph;
}")
};
private ExternalDataSource? graph = null;
private ChartOptions? options = null;
private bool isInitialized = false;

private ChartOptions options = new()
protected override void OnInitialized()
{
Title = new()
graph = new ExternalDataSource("/data/les-miserables.json", ExternalDataFetchAs.Json)
{
Text = "Les Miserables",
Subtext = "Default layout",
Top = "top",
Left = "center"
},
Tooltip = new() { },
Legend = new()
{
Top = 100
//Data = new JavascriptFunction("graph.categories.map(function (a) { return a.name; })") //TODO: this won't work, 'graph' is not defined
},
Series = new()
AfterLoad = new JavascriptFunction(@"function (graph) {
graph.nodes.forEach(function (node) { node.symbolSize = 5; });
return graph;
}")
};

options = new()
{
new GraphSeries()
Title = new()
{
Name = "Les Miserables",
Layout = GraphLayout.Force,
Data = new ExternalDataSourceRef(graph, "nodes"),
Links = new ExternalDataSourceRef(graph, "links"),
Categories = new ExternalDataSourceRef(graph, "categories"),
Roam = Roam.True,
Label = new()
{
Position = LabelPosition.Right
},
Force = new()
Text = "Les Miserables",
Subtext = "Default layout",
Top = "top",
Left = "center"
},
Tooltip = new() { },
Legend = new()
{
Top = 100
//Data = new JavascriptFunction("graph.categories.map(function (a) { return a.name; })") //TODO: this won't work, 'graph' is not defined
},
Series = new()
{
new GraphSeries()
{
Repulsion = 100
Name = "Les Miserables",
Layout = GraphLayout.Force,
Data = new ExternalDataSourceRef(graph, "nodes"),
Links = new ExternalDataSourceRef(graph, "links"),
Categories = new ExternalDataSourceRef(graph, "categories"),
Roam = Roam.True,
Label = new()
{
Position = LabelPosition.Right
},
Force = new()
{
Repulsion = 100
}
}
}
}
};
};

isInitialized = true;
}
}
22 changes: 18 additions & 4 deletions src/Vizor.ECharts.Samples/Areas/Sankey/SankeyWithLevelsChart.razor
Original file line number Diff line number Diff line change
@@ -1,12 1,23 @@
@using Vizor.ECharts;

<Vizor.ECharts.EChart Options="@options" Width="800px" Height="800px" />
@if (isInitialized)
{
<Vizor.ECharts.EChart Options="@options" ExternalDataSources="@(new[] { extData })" Width="800px" Height="800px" />
}

@code {

// see https://echarts.apache.org/examples/en/editor.html?c=sankey-levels
private ChartOptions options = new()
private ExternalDataSource? extData = null;
private ChartOptions? options = null;
private bool isInitialized = false;

protected override void OnInitialized()
{
extData = new ExternalDataSource("data/sankey_simple.json", ExternalDataFetchAs.Json);

options = new()
{
Title = new()
{
Expand All @@ -21,8 32,8 @@
{
new SankeySeries()
{
Data = new ExternalDataSource("data/sankey_simple.json") { Path = "nodes" },
Links = new ExternalDataSource("data/sankey_simple.json") { Path = "links" }, // in theory this is a double call, but the browser will retrieve it from cache
Data = new ExternalDataSourceRef(extData, path: "nodes"),
Links = new ExternalDataSourceRef(extData, path: "links"),
Emphasis = new()
{
Focus = "adjacency"
Expand Down Expand Up @@ -89,4 100,7 @@
}
}
};

isInitialized = true;
}
}
Loading

0 comments on commit fe5bad9

Please sign in to comment.