-
Notifications
You must be signed in to change notification settings - Fork 2
Chart Types
Try it live: Open the Playground to experiment with charts in your browser. Browse the Cookbook (25 pages) for copy-paste code examples with rendered output.
MatPlotLibNet v1.10 includes 78 series types across 13 categories, plus 4 streaming series, 13 map projections, and geographic polygon rendering via MatPlotLibNet.Geo. See the Geographic / Maps section near the bottom for the full geo surface.
Each of these renders is produced by the MatPlotLibNet fidelity suite and diffed pixel-for-pixel against a matplotlib reference. Click any tile to open the full-resolution PNG.
Every one of these chart types is available via a single fluent call — see the sections below for the builder signatures.

Plt.Create().Plot(x, y).Save("line.svg");
Plt.Create().Plot(x, y, s => { s.Color = Color.Blue; s.LineWidth = 2; s.Label = "Series A"; }).Save("line.svg");Plt.Create().Scatter(x, y).Save("scatter.svg");
Plt.Create().Scatter(x, y, s => { s.MarkerStyle = MarkerStyle.Circle; s.MarkerSize = 8; }).Save("scatter.svg");Plt.Create().Bar(["Q1", "Q2", "Q3", "Q4"], [100, 200, 150, 250]).Save("bar.svg");double[] data = /* ... */;
Plt.Create().Hist(data, bins: 20).Save("hist.svg");Plt.Create().Pie(["A", "B", "C"], [40, 35, 25]).Save("pie.svg");
Plt.Create().Donut(["A", "B", "C"], [40, 35, 25]).Save("donut.svg");Plt.Create().Step(x, y).Save("step.svg");
Plt.Create().Fill(x, y1, y2).Save("fill.svg"); // fill between two lines
Plt.Create().ErrorBar(x, y, yErr).Save("errbar.svg");
Plt.Create().Stem(x, y).Save("stem.svg");
Plt.Create().Box(groups).Save("box.svg"); // groups: double[][]
Plt.Create().Violin(groups).Save("violin.svg");For data ≥ 100 k points. Uses O(1) viewport slicing (uniform) or O(log n) binary search (non-uniform) + LTTB downsampling. Zero gen-2 GC allocation on the render path.
// Uniform sample rate (e.g. audio, sensor data) — O(1) IndexRangeFor
Plt.Create().Signal(samples, sampleRate: 44100).Save("audio.svg");
// Non-uniform ascending X — O(log n) binary search
Plt.Create().SignalXY(xNonUniform, y).Save("signal_xy.svg");
// One-liner shortcuts
QuickPlot.Signal(samples, sampleRate: 44100, title: "Audio").Save("audio.svg");
QuickPlot.SignalXY(x, y).Save("xy.svg");Plt.Create().Kde(data).Save("kde.svg");
Plt.Create().Ecdf(data).Save("ecdf.svg");
Plt.Create().Rugplot(data).Save("rug.svg");
Plt.Create().Stripplot(groups, labels).Save("strip.svg");
Plt.Create().Swarmplot(groups, labels).Save("swarm.svg");
Plt.Create().Pointplot(groups, labels).Save("point.svg");
Plt.Create().Eventplot(events).Save("event.svg");
Plt.Create().Regression(x, y).Save("regression.svg"); // with LeastSquares fit line
Plt.Create().Residual(x, y, yFit).Save("residual.svg");
Plt.Create().Count(categories).Save("count.svg");
FigureTemplates.FinancialDashboard produces a 3-panel layout (price 60 %, volume 15 %, oscillator 25 %) with grid lines on every panel, bar-center-aligned tick labels, and optional indicator overlays. Each indicator resolves against the most recently added price series — so .BollingerBands(20) followed by .Sma(5) computes the SMA over the raw close (not over the Bollinger middle band) because both draw their input from the underlying OHLC series; chained indicators that want to operate on a prior indicator's output pass the output array explicitly.
FigureTemplates.FinancialDashboard(open, high, low, close, volume,
title: "ACME Corp",
configurePricePanel: ax => {
ax.BollingerBands(20); // BB(20, 2σ) with semi-transparent fill
ax.Sma(5);
},
configureOscillatorPanel: ax => {
ax.Rsi(close, 14);
ax.AxHLine(70, rl => { rl.Color = Color.FromHex("#E24A33"); rl.LineStyle = LineStyle.Dashed; });
ax.AxHLine(30, rl => { rl.Color = Color.FromHex("#E24A33"); rl.LineStyle = LineStyle.Dashed; });
})
.WithSize(1200, 700)
.Save("financial_dashboard.svg");Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Candlestick(open, high, low, close)
.Sma(period: 20)
.WithLegend())
.Save("candlestick.svg");Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax.Ohlc(open, high, low, close))
.Save("ohlc.svg");
double[,] data = /* 2-D array */;
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Heatmap(data)
.WithColorMap("plasma")
.WithColorBar(cb => cb with { Label = "Intensity" }))
.Save("heatmap.svg");Plt.Create().Contour(x, y, z).Save("contour.svg");
Plt.Create().Contourf(x, y, z).Save("contourf.svg");Plt.Create().Image(bitmap).Save("image.svg");
Plt.Create().Hexbin(x, y).Save("hexbin.svg");
Plt.Create().Pcolormesh(x, y, z).Save("pcolor.svg");
Plt.Create().Tricontour(x, y, z).Save("tricontour.svg");
Plt.Create().Tripcolor(x, y, z).Save("tripcolor.svg");Twelve 3-D series with perspective camera, per-face lighting, numeric tick marks along the bounding-box edges, and interactive SVG rotation. 3-D axes share a single cross-series depth queue — multiple 3-D series on the same axes composite correctly regardless of the order in which you add them.
// Original 6
Plt.Create().Surface3D(x, y, z).Save("surface.svg");
Plt.Create().Wireframe3D(x, y, z).Save("wireframe.svg");
Plt.Create().Scatter3D(x, y, z).Save("scatter3d.svg");
Plt.Create().Bar3D(x, y, heights).Save("bar3d.svg");
Plt.Create().PlanarBar3D(x, y, heights).Save("planar_bars.svg");
Plt.Create().Stem3D(x, y, z).Save("stem3d.svg");
// v1.3.0 — 6 new series
Plt.Create().Plot3D(x, y, z).Save("line3d.svg"); // projected polyline
Plt.Create().Trisurf(x, y, z).Save("trisurf.svg"); // Delaunay triangulated surface
Plt.Create().Contour3D(x, y, z).Save("contour3d.svg"); // marching-squares contour lines
Plt.Create().Quiver3D(x, y, z, u, v, w).Save("quiver3d.svg"); // 3D vector field
Plt.Create().Voxels(filled).Save("voxels.svg"); // face-culled cubes
Plt.Create().Text3D(x, y, z, "label").Save("text3d.svg"); // 3D annotation
Matplotlib's ax.bar3d(...) equivalent — rectangular prisms rising from the XY plane with matplotlib-exact per-face shading (0.65 + 0.35·dot(n̂, l̂)). Each bar has six shaded faces; the renderer depth-sorts all faces from all bars so occlusion is correct regardless of camera angle. Add multiple Bar3D calls on one axes for a stacked grid — the shared 3-D depth queue handles compositing:

record Row(double Y, Color Color);
Row[] rows = [new(0, Colors.Red), new(1, Colors.Green),
new(2, Colors.Blue), new(3, Colors.Cyan), new(4, Colors.Gold)];
Plt.Create()
.WithTitle("3D Bar Chart — Grouped rows")
.WithSize(780, 620)
.AddSubPlot(1, 1, 1, ax =>
{
ax.WithCamera(elevation: 25, azimuth: -60)
.WithLighting(dx: -0.4, dy: -0.8, dz: 0.45);
foreach (var (y, color) in rows)
{
double[] ys = Enumerable.Repeat(y, 20).ToArray();
double[] zs = /* ... */;
ax.Bar3D(xs, ys, zs, s => { s.Color = color; s.BarWidth = 0.4; });
}
})
.Save("bar3d_grouped.svg");
Matplotlib's ax.bar(xs, heights, zs=y, zdir='y') equivalent — also known as a "skyscraper plot" or "2D bars in different planes". Each bar is a single flat translucent rectangle in the XZ plane at a fixed Y value (no cuboid, no shading), so you can read bars through the rear planes. Great for comparing many time-series or categorical distributions stacked on planes.
Plt.Create()
.WithTitle("Planar 3D Bars")
.WithSize(780, 620)
.AddSubPlot(1, 1, 1, ax =>
{
ax.WithCamera(elevation: 25, azimuth: -60)
.SetXLabel("X").SetYLabel("Y").SetZLabel("Z");
foreach (var (y, color) in rows)
{
double[] ys = Enumerable.Repeat(y, 20).ToArray();
double[] zs = /* ... */;
ax.PlanarBar3D(xs, ys, zs, s =>
{
s.Color = color;
s.BarWidth = 0.8;
s.Alpha = 0.8; // translucency
});
}
})
.Save("planar_bars.svg");PlanarBar3DSeries.Colors is a parallel Color[]? array (same convention as ScatterSeries.Colors / PieSeries.Colors). When set, it overrides the per-series Color on a per-bar basis — enabling three colour lookup modes from one API:
-
Per Y / per plane — set
s.Color = planeColoron each series -
Per X — set
s.Colors = xs.Select(x => lookup(x)).ToArray() -
Combined — set both:
Colors[i]wins where defined,Coloris the fallback
The sample below highlights every bar at x = 0 in dark cyan, regardless of which plane it sits on:

var darkCyan = Color.FromHex("#008B8B");
ax.PlanarBar3D(xs, ys, zs, s =>
{
s.Color = planeColor;
s.BarWidth = 0.8;
s.Alpha = 0.8;
// Per-X override: every bar at x == 0 is dark cyan.
s.Colors = xs.Select(x => x == 0 ? darkCyan : planeColor).ToArray();
});No callbacks, no new API surface — one Color[]? array handles all three modes. See Advanced for the full multi-series depth-compositing story.
Camera, lighting, Z-axis label/range, and interactive rotation — applies to every 3-D series:
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Surface3D(x, y, z)
.SetXLabel("x").SetYLabel("y").SetZLabel("sin(r)/r")
.SetZLim(-0.3, 1.0)
.WithCamera(elevation: 35, azimuth: -55, distance: 8)
.WithLighting(ambient: 0.4, diffuse: 0.6, dirX: 1.0)
.With3DRotation())
.Save("surface3d.svg");distance controls perspective (orthographic when omitted). SetZLabel / SetZLim configure the Z-axis just like SetXLabel / SetYLabel do on 2-D charts — they write to the Axes.ZAxis (Axis3D) model property. With3DRotation() embeds JS for mouse-drag and keyboard rotation (arrow keys + Home reset). See Advanced for more.
Note on builder placement (v1.1.4+) — call
WithCamera/WithLightinginside theAddSubPlot(..., ax => ax...)lambda so they apply to the actual 3-D subplot. Calling them at thePlt.Create().WithCamera(...)level only configures the (unused) figure-level default axes on multi-subplot figures.

3-D polyline through arbitrary (x, y, z) points. Segments are depth-sorted and projected through the Projection3D pipeline.
double[] t = Enumerable.Range(0, 200).Select(i => i * 0.1).ToArray();
double[] x = t.Select(v => Math.Cos(v)).ToArray();
double[] y = t.Select(v => Math.Sin(v)).ToArray();
double[] z = t;
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Plot3D(x, y, z, s => { s.Color = Colors.Blue; s.Label = "Helix"; })
.WithCamera(elevation: 25, azimuth: -60)
.With3DRotation())
.Save("line3d.svg");
Takes unstructured (x, y, z) point clouds and generates a triangulated surface mesh via Delaunay triangulation. Each triangle face is depth-sorted and shaded via Vec3.FaceNormal + Color.Shade() extensions.
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Trisurf(x, y, z, s => { s.Color = Colors.Teal; s.Alpha = 0.8; })
.WithCamera(elevation: 30, azimuth: -45)
.With3DRotation())
.Save("trisurf.svg");
Computes contour lines on a grid using marching squares, then projects them into 3-D space. Each contour sits at its corresponding Z level.
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Contour3D(x, y, z, s => { s.Levels = 10; })
.WithCamera(elevation: 35, azimuth: -55)
.With3DRotation())
.Save("contour3d.svg");
Draws arrows in 3-D space. Each arrow has a shaft (line segment) and a cone head. Useful for visualising electromagnetic fields, fluid flow, or gradient vectors.
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Quiver3D(x, y, z, u, v, w, s => { s.Color = Colors.Red; s.ArrowLength = 0.3; })
.WithCamera(elevation: 25, azimuth: -60)
.With3DRotation())
.Save("quiver3d.svg");
Renders a bool[,,] voxel grid as face-culled cubes. Adjacent filled voxels suppress shared faces, reducing draw count. All visible faces are depth-sorted through DepthQueue3D.
var filled = new bool[5, 5, 5];
// fill some voxels...
filled[2, 2, 2] = true;
filled[2, 2, 3] = true;
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Voxels(filled, s => { s.Color = Colors.Orange; s.Alpha = 0.8; })
.WithCamera(elevation: 30, azimuth: -50)
.With3DRotation())
.Save("voxels.svg");
Places text labels at arbitrary (x, y, z) positions. The 3-D coordinate is projected to 2-D pixel space and the text is drawn at the projected position.
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.Surface3D(x, y, z)
.Text3D(0, 0, 1.0, "Peak", s => { s.Color = Colors.Red; })
.WithCamera(elevation: 35, azimuth: -55)
.With3DRotation())
.Save("text3d.svg");double[] r = [1, 2, 3, 4, 5];
double[] theta = Enumerable.Range(0, 5).Select(i => i * Math.PI / 2.5).ToArray();
Plt.Create().PolarPlot(r, theta).Save("polar_line.svg");
Plt.Create().PolarScatter(r, theta).Save("polar_scatter.svg");
// Wind rose / radar bar
double[] speeds = [5, 10, 8, 3, 7, 12, 6, 9];
double[] dirs = Enumerable.Range(0, 8).Select(i => i * Math.PI / 4).ToArray();
Plt.Create().PolarBar(speeds, dirs, b => b.BarWidth = 0.7).Save("windrose.svg");var tree = new TreeNode
{
Label = "Revenue",
Children =
[
new TreeNode { Label = "Products", Value = 400 },
new TreeNode { Label = "Services", Value = 300 },
new TreeNode { Label = "Licensing", Value = 200 }
]
};
Plt.Create().Treemap(tree).Save("treemap.svg");
Plt.Create().Sunburst(tree).Save("sunburst.svg");Hierarchical-clustering visualisation rendered as the canonical "U"-shape segments — every internal node is a merge whose vertical position equals the merge distance (carried in TreeNode.Value). Matches SciPy's scipy.cluster.hierarchy.dendrogram rendering convention.
Plt.Create()
.Dendrogram(tree, s =>
{
s.Orientation = DendrogramOrientation.Top; // Top, Bottom, Left, Right
s.CutHeight = 1.5; // dashed reference line + cluster colours
s.CutLineColor = Colors.Red;
s.ColorByCluster = true; // qualitative IColorMap (default Tab10)
})
.Save("dendrogram.svg");CutHeight uses strict less-than (node.Value < cut), matching SciPy's dendrogram(color_threshold=…) visual convention. Single-leaf trees render the lone label at the plot centre; all-zero merge distances collapse the U-shapes to the leaf baseline (a unit maxMerge fallback avoids division by zero). See the cookbook entry for a full walkthrough.
Composites a heatmap with optional row and column dendrograms into a single subplot — the seaborn sns.clustermap idiom. When trees are provided, rows and/or columns are automatically reordered to match the dendrogram leaf traversal order so cells align visually with the tree structure.
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax.Clustermap(data, s =>
{
s.RowTree = rowTree; // leaf Value = original row index
s.ColumnTree = colTree; // leaf Value = original column index
s.RowDendrogramWidth = 0.15; // fraction of width, default 0.15, clamped [0, 0.9]
s.ColumnDendrogramHeight = 0.15;
s.ColorMap = ColorMaps.RdBu;
s.ShowLabels = true;
s.LabelFormat = "F2";
}))
.ToSvg();Leaf TreeNode.Value must be the zero-based original index of the row or column. Internal nodes carry the merge distance. Malformed trees (out-of-range indices, duplicates, wrong count) fall back to identity order silently. See the cookbook entry for a full walkthrough.
N×N matrix of subplots from N variables — the seaborn pairplot idiom. Diagonal cells show
univariate distributions (histogram or KDE); off-diagonal cells show bivariate scatters of
(i, j). Optional hue groups colour the off-diagonal scatters by category — the killer
feature for cluster validation and category-aware EDA.
double[][] vars = [petalLength, petalWidth, sepalLength, sepalWidth];
int[] hue = species.Select(s => (int)s).ToArray();
string[] hLab = ["Setosa", "Versicolor", "Virginica"];
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax.PairGrid(vars, s =>
{
s.Labels = ["Petal L", "Petal W", "Sepal L", "Sepal W"];
s.HueGroups = hue;
s.HueLabels = hLab;
s.DiagonalKind = PairGridDiagonalKind.Kde; // Histogram (default), Kde, None
s.Triangular = PairGridTriangle.LowerOnly; // Both (default), LowerOnly, UpperOnly
}))
.ToSvg();Diagonal histograms are stacked-overlapping per hue group with Alpha = 0.6; KDE renders
one curve per group. Off-diagonal scatter dot radius is controlled by MarkerSize.
OffDiagonalKind = None produces a diagonal-only "marginals" view; OffDiagonalKind = Hexbin
renders flat-top hexagonal density grids for high-cardinality EDA where scatter overplots
(activated in v1.10; hue is ignored when Hexbin is active). Cell gutters (CellSpacing,
default 0.02) are clamped to [0, 0.2].
See the cookbook entry for a full walkthrough.

Steady-pictures UX (v1.7.2 Phase W). The initial interactive view is pixel-identical to the static SVG — every node at every depth (incl. depth-3 and beyond) is visible on first paint. Z-order means children paint OVER parents, so the deepest visible label is what the user reads in any overlapping region. Clicking a parent rectangle now collapses its entire subtree (transitively, via an ancestry-walk visibility model — descendants' own collapse state is preserved); click again to restore. Multiple subtrees can be collapsed independently. Leaves are not clickable.
Why default-expanded? Earlier (Phase P) the script started with only depth-1 visible and required clicks to expand — but that meant entering interactive mode visually shifted the chart (the root header strip went blank), and the user had to click to discover content. Phase W flipped this: the user sees everything by default ("when no browserInteraction he sees all" applies in both modes), and clicks become an opt-in focus gesture instead of a discovery requirement.
Keyboard-accessible via tabindex="0" and ARIA roles. The interactive script is embedded in the SVG output when WithTreemapDrilldown() is called on the FigureBuilder, and every rect is tagged with data-treemap-node / -depth / -parent so the script can navigate the hierarchy without re-parsing the structure. For static SVG output with deep trees, call .WithAutoSize(root) so the canvas is big enough to fit every label cleanly without overflow.
Plt.Create()
.WithTreemapDrilldown()
.AddSubPlot(1, 1, 1, ax => ax.Treemap(catalogue, s => s.ShowLabels = true))
.Save("treemap_drilldown.svg");
A two-level TreeNode rendered as an inner pie + outer breakdown ring. Convenience wrapper around Sunburst(...) with InnerRadius = 0.
var departments = new TreeNode
{
Label = "Revenue",
Children = [
new() { Label = "Electronics", Children = [
new() { Label = "Phones", Value = 40 },
new() { Label = "Laptops", Value = 35 },
new() { Label = "Audio", Value = 25 }] },
/* more departments... */
]
};
Plt.Create()
.WithTitle("Revenue by department and product")
.AddSubPlot(1, 1, 1, ax => ax.NestedPie(departments))
.Save("nested_pie.svg");Nodes-and-edges-in-2D for correlation networks (Pearson edge weights), lead-lag flow
(TransferEntropy directed edges), Louvain community visualisation (node colour = community
ID), and minimum spanning trees. Three deterministic layouts ship in v1.10 PR 1
(Manual / Circular / Hierarchical); ForceDirected (Fruchterman–Reingold) follows in
PR 2.
GraphNode[] nodes =
[
new("AAPL", ColorScalar: 0.2),
new("MSFT", ColorScalar: 0.2),
new("GOOG", ColorScalar: 0.7),
];
GraphEdge[] edges =
[
new("AAPL", "MSFT", Weight: 0.85),
new("AAPL", "GOOG", Weight: 0.42, IsDirected: true),
new("MSFT", "GOOG", Weight: 0.55, IsDirected: true),
];
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax.NetworkGraph(nodes, edges, s =>
{
s.Layout = GraphLayout.Circular; // Manual / Circular / Hierarchical
s.ColorMap = ColorMaps.Viridis;
s.ShowNodeLabels = true;
s.NodeRadiusScale = 8.0;
}))
.ToSvg();IsDirected edges get an arrowhead at the target end (reuses ArrowHeadBuilder.FancyArrow).
EdgeThicknessScale multiplies per-edge Weight to stroke width;
NodeRadiusScale multiplies per-node SizeScalar to circle radius.
DataFrame extension: df.NetworkGraph("source", "target", weightCol: "weight", directedCol: "directed") derives nodes from the union of source/target columns.

v1.1.4 rewrote the Sankey renderer around an 8-step pipeline: explicit column assignment, node alignment, iterative vertical relaxation, gradient link colour modes, sub-labels, inside-labels, hover emphasis, and a full vertical orientation. Five new samples ship in images/sankey_*.png:
-
sankey_process_distribution.svg— 5-column process-industry cascade with tonnage sub-labels, gradient links, hover emphasis enabled (hover a node to isolate its reachable flow chain) -
sankey_income_statement.svg— J&J Q1 FY25 income statement with semantic green/red node colouring and "+2% Y/Y" sub-label deltas -
sankey_customer_journey.svg— 4-timestep alluvial whereHomelegitimately reappears in multiple columns (uses explicitSankeyNode.Columnoverride to pin each node to its semantic timestep regardless of link topology) -
sankey_un_expenses.svg— 2-column baseline, outside labels, cleanHideAllAxes()canvas -
sankey_severity_cascade.svg— 4-column patient severity state transitions with 24 relaxation iterations minimising crossings in a dense many-to-many topology -
sankey_vertical.svg— vertical orientation (top-to-bottom flow) conversion funnel
Minimal example:
SankeyNode[] nodes = [
new("Coal", Color.FromHex("#6B8E23")),
new("Gas", Color.FromHex("#4682B4")),
new("Electricity", Color.FromHex("#FFD700"), SubLabel: "80 TWh"),
new("Heat", Color.FromHex("#D2691E"), SubLabel: "40 TWh")];
SankeyLink[] links = [new(0, 2, 50), new(1, 2, 30), new(1, 3, 20)];
Plt.Create()
.WithSankeyHover() // v1.1.4 hover emphasis script
.AddSubPlot(1, 1, 1, ax => ax
.HideAllAxes()
.Sankey(nodes, links, s =>
{
s.NodeWidth = 24;
s.Iterations = 20; // vertical relaxation passes
s.LinkColorMode = SankeyLinkColorMode.Gradient;
s.Orient = SankeyOrientation.Horizontal; // or Vertical
}))
.Save("sankey.svg");Key properties (v1.1.4):
| Property | Meaning |
|---|---|
SankeyNode.SubLabel / SubLabelColor
|
Secondary label drawn one line below at 80 % font size; useful for "$13.9B +2%" metric/delta pairs |
SankeyNode.Column |
Explicit column override; pins node to a semantic column regardless of BFS topology (alluvial / time-step Sankeys) |
SankeySeries.NodeAlignment |
Justify (default) / Left / Right / Center — matches D3 sankey* modes |
SankeySeries.Iterations |
Vertical relaxation passes (default 6); higher for denser topologies |
SankeySeries.LinkColorMode |
Source / Target / Gradient (default). Gradient emits an SVG <linearGradient> per link. |
SankeySeries.Orient |
Horizontal (default) / Vertical — top-to-bottom flow |
SankeySeries.InsideLabels |
Draw labels centred inside the node rect (when NodeWidth is wide enough) |
Plt.Create().Quiver(x, y, u, v).Save("quiver.svg");
Plt.Create().Barbs(x, y, u, v).Save("barbs.svg");
Plt.Create().Streamplot(x, y, u, v).Save("stream.svg");
Plt.Create().Radar(categories, values).Save("radar.svg");
Plt.Create().Waterfall(labels, deltas).Save("waterfall.svg");
Plt.Create().Funnel(labels, values).Save("funnel.svg");
Plt.Create().Gauge(value: 0.72, min: 0, max: 1).Save("gauge.svg");
Plt.Create().ProgressBar(value: 0.65).Save("progress.svg");
Plt.Create().Sparkline(timeSeries).Save("sparkline.svg");
Plt.Create().Table(headers, rows).Save("table.svg");
Plt.Create().Spectrogram(signal, sampleRate: 44100).Save("spec.svg");
Plt.Create().BrokenBar(yranges, xranges).Save("brokenbar.svg");The MatPlotLibNet.Geo package adds 13 map projections, the GeoPolygonSeries (renders any closed ring as a styled polygon — choropleth, ocean fill, land fill, custom shapes), and embedded Natural Earth 110m data (coastlines + countries, no external download required).
using MatPlotLibNet.Geo.Projections;
Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax
.WithProjection(new Mercator())
.Ocean(new Mercator(), new Color(180, 220, 255))
.Land(new Mercator(), new Color(240, 230, 200))
.Coastlines(new Mercator(), Colors.Black, lineWidth: 0.5)
.Borders(new Mercator(), Colors.Gray, lineWidth: 0.3))
.Save("worldmap.svg");| Family | Projection | Notes |
|---|---|---|
| Cylindrical | PlateCarree |
Equirectangular — the simplest 1:1 lat/lon mapping |
| Cylindrical | Mercator |
Conformal — preserves angles (web maps) |
| Cylindrical | TransverseMercator |
Mercator rotated 90° — UTM zones |
| Pseudo-cylindrical | Mollweide |
Equal-area; world maps |
| Pseudo-cylindrical | Robinson |
Compromise; common in atlases |
| Pseudo-cylindrical | Sinusoidal |
Equal-area; meridian-true |
| Pseudo-cylindrical | EqualEarth |
Modern equal-area (Šavrič et al. 2018) |
| Pseudo-cylindrical | NaturalEarthProjection |
Compromise; aesthetic for global view |
| Conic | AlbersEqualArea |
Equal-area conic — ideal for mid-latitude regions (US, Europe) |
| Conic | LambertConformal |
Conformal conic — aviation charts |
| Azimuthal | Orthographic |
"View from space" — globe rendering |
| Azimuthal | Stereographic |
Conformal azimuthal — polar regions |
| Azimuthal | AzimuthalEquidistant |
Distances from centre preserved |
All projections implement IGeoProjection and round-trip through Forward(lon, lat) → (x, y) and Inverse(x, y) → (lon, lat)?.
// Choropleth: colour countries by a metric.
var ax = Plt.Create()
.AddSubPlot(1, 1, 1, ax => ax.WithProjection(new Robinson()));
foreach (var (country, value) in countryData)
{
var poly = NaturalEarth110m.Countries.First(c => c.Properties["NAME"] == country);
ax.AddSeries(new GeoPolygonSeries(poly, new Robinson())
{
FillColor = ColorMaps.Viridis.GetColor(value / maxValue),
EdgeColor = Colors.White,
EdgeWidth = 0.3,
});
}Bundled GeoJSON for the Natural Earth 110m cultural and physical datasets. No HTTP fetch, no download step, no external dependencies.
| Property | Contents |
|---|---|
NaturalEarth110m.Coastlines |
World coastline rings (134 features) |
NaturalEarth110m.Countries |
World countries with Properties["NAME"], ["ISO_A2"], etc. (177 features) |
.WithProjection(IGeoProjection) // Sets axes coordinate transform
.Coastlines(projection, color, lineWidth) // Adds Natural Earth 110m coastlines
.Borders(projection, color, lineWidth) // Adds Natural Earth 110m country borders
.Ocean(projection, color) // Fills entire map background
.Land(projection, color) // Fills land massesEach extension method is built on GeoPolygonSeries — the lowest-level primitive — so you can mix the convenience helpers with hand-rolled GeoPolygonSeries for choropleth or custom-shape rendering in the same axes.











