Table of Contents

Maps in reports

A Map in a report draws as a ReportPageMap: its polygons, markers and lines become the same shapes charts and gauges use, so a map reaches PDF, images, the browser viewer, Word and Excel. Every rule was measured against Microsoft's report engine.

This guide uses territories.rdl: four squares carrying a Region field, coloured by sales through a map data region and a three-bucket colour rule, a layer of shops read from a dataset, and a title.

Give the map its data

using DocWright.Reporting.Data;

var sales = new ReportDataTable()
    .AddColumn("Region", typeof(string))
    .AddColumn("Amount", typeof(double))
    .AddRow("North", 120.0)
    .AddRow("South", 40.0)
    .AddRow("East", 75.0)
    .AddRow("North", 30.0);

// A spatial field holds WKB bytes, base64 WKB or WKT text: here, WKT.
var stores = new ReportDataTable()
    .AddColumn("Store", typeof(string))
    .AddColumn("Location", typeof(string))
    .AddRow("Harbour", "POINT (5 17)")
    .AddRow("Station", "POINT (17 5)");

var provider = new ReportInMemoryDataProvider().Add("Sales", sales).Add("Stores", stores);

A layer's shapes come from embedded VectorData (base64 WKB) or, with MapSpatialDataSet, from a dataset: one shape per row, read from a field holding WKB bytes, base64 WKB or WKT text. DocWright has no SQL Server types, so a geometry column reaches it as STAsBinary() or STAsText(). The fields MapFieldNames lists become the shape's fields.

Read and draw the map

using DocWright.Fonts;
using DocWright.Reporting.Pagination;
using DocWright.Reporting.Processing;

var fonts = new FontEngine();
using ReportInstance instance = ReportProcessor.Process(definition, provider, new ReportRunOptions());
ReportPageModel pages = ReportPaginator.Paginate(instance, fonts);

ReportPageMap map = pages.Pages[0].Items.OfType<ReportPageMap>().Single();
Console.WriteLine($"{map.Name}: {map.Kind}, {map.Width.Value / 1440.0}in x {map.Height.Value / 1440.0}in, {map.Shapes.Count} shapes");
foreach (ReportShapeRegion region in map.Shapes.OfType<ReportShapeRegion>())
{
    Console.WriteLine($"  region filled #{region.Fill!.Value.R:X2}{region.Fill.Value.G:X2}{region.Fill.Value.B:X2}");
}

Console.WriteLine($"  labels: {string.Join(", ", map.Shapes.OfType<ReportShapeLabel>().Select(l => l.Text))}");

Output:

Territories: Map, 4in x 3in, 11 shapes
  region filled #FF0000
  region filled #008000
  region filled #008000
  region filled #D3D3D3
  labels: Sales by region
MaxReportMapPoints: The maps of this report read more than 10 coordinates.
using DocWright.Core.Diagnostics;
using DocWright.Core.Rendering;
using DocWright.Renderers.Pdf;

using (FileStream pdf = File.Create("territories.pdf"))
{
    new PdfLayoutRenderer(fonts).Render(pages.ToLayoutDocument(), pdf, new RenderOptions(), new CollectingConversionDiagnostics());
}
territories.pdf · page 1
A map of four squares coloured red, green, green and grey by sales, two black shop markers and the title Sales by region
The map on page 1. Download: territories.pdf.

North sums to 150 and falls in the top of three equal intervals over 40–150, so it is red. South (40) and East (75) are in the first interval. West has no data, so no group binds it and it keeps its template's grey.

  • Coordinates: Planar maps draw their data as given. Geographic maps project longitude and latitude with the named projection: Equirectangular, Mercator, Robinson, Fahey, Eckert1, Eckert3, Wagner3, HammerAitoff (drawn as Hammer's, as the reference does) and Bonne (drawn as sinusoidal, as the reference does).
  • Binding: a layer that names a MapDataRegion matches each group to the shapes whose fields equal its MapBindingFieldPairs.
  • Rules: colour range rules (EqualInterval, EqualDistribution, Custom buckets), custom colour rules, and the BrightPastel, Light, SemiTransparent and Pacific palettes.
  • Styles: fills, holes, transparency, every border style, the eight marker shapes, lines, labels, meridians and parallels with their labels, and titles.
Note

Optimal, the default distribution of a colour rule, is drawn as equal intervals: Reporting Services' rule for it has only been matched at two and three buckets. Set DistributionType explicitly when the colours must match exactly.

What a map doesn't draw

Everything a map holds that DocWright doesn't draw is named in one DXP9409 diagnostic per map, and the rest of the map draws: tile layers (the reference fetches them over the network), shapefiles, spatial data from a data region, the Optimal distribution and the Random palette (which is random in Reporting Services too), size, marker and centre-point rules, legends, the colour scale, the distance scale, border skins and custom views. A map that Reporting Services itself refuses to draw keeps its box, empty, where Reporting Services prints an error.

Limit the coordinates a report can read

using DocWright.Core;
using DocWright.Reporting.Pagination;
using DocWright.Reporting.Processing;

var bounded = new ReportRunOptions();
bounded.Limits.MaxReportMapPoints = 10;
using ReportInstance small = ReportProcessor.Process(definition, provider, bounded);
try
{
    ReportPaginator.Paginate(small, fonts);
}
catch (ResourceLimitExceededException error)
{
    Console.WriteLine($"{error.LimitName}: {error.Message}");
}

MaxReportMapPoints (ten million by default, in ReportRunOptions.Limits) counts every coordinate the maps of one pagination read, embedded or from datasets. Setting it to null doesn't remove the bound: pagination never reads more than fifty million. A map's own MaximumTotalPointCount stops it sooner and is reported rather than thrown.

Next steps