Skip to content

Vector Tiles Support

ThinkGeo renders Mapbox Vector Tiles (MVT) that are styled with the MapLibre Style Spec — the open specification for GL vector-tile styling (originally the Mapbox GL style format). The data can come from a remote tile service or from a self-contained local file, and the styling always comes from a Style JSON document. Three layers in ThinkGeo.Core cover the common cases:

Layer Where the tile data lives Typical use
MvtTilesAsyncLayer Wherever the Style JSON's sources point (remote XYZ endpoints and/or local files) Point the layer at a Style JSON and render exactly what it describes.
VectorPmTilesAsyncLayer A local .pmtiles archive Serve MVT from a single-file PMTiles archive, optionally reusing a Style JSON (local file or online).
VectorMbTilesAsyncLayer A local .mbtiles SQLite database Serve MVT from a local MBTiles file, optionally reusing a Style JSON (local file or online) for the look.

VectorPmTilesAsyncLayer and VectorMbTilesAsyncLayer both derive from MvtTilesAsyncLayer, so they inherit all of its styling and rendering behavior. The difference is that they read the vector tiles from a local file you supply, rather than from the locations declared in the Style JSON's sources — so you can reuse an existing style's appearance while serving the tile data from your own .pmtiles or .mbtiles file.

All three are AsyncLayers: you add them to a LayerOverlay on the MapView, exactly like any other layer. They work in Spherical Mercator (GeographyUnit.Meter, EPSG:3857) and use a 512 px tile matrix over the Web-Mercator extent by default, which matches how MVT tilesets are cut. Each layer paints its own background from the Style JSON, so you do not need to set the map background yourself.

MvtTilesAsyncLayer

MvtTilesAsyncLayer is the base vector-tile layer. You give it a Style JSON — either an http(s) URL or a local file path — and it does the rest: it parses the sources and layers from the document, downloads (or reads) the vector tiles that each source refers to, and draws them with the styles, filters, and zoom ranges declared in the Style JSON. vector sources (remote XYZ templates or local .mbtiles paths) and inline geojson sources are both supported; raster, raster-dem, image, and video sources are ignored.

Example Usage with Desktop Edition

// With ThinkGeo.UI.Wpf or ThinkGeo.UI.WinForms
private async void MapView_OnLoaded(object sender, RoutedEventArgs e)
{
    // Set up the MapView and Overlay.
    MapView.MapUnit = GeographyUnit.Meter;
    var layerOverlay = new LayerOverlay();
    MapView.Overlays.Add(layerOverlay);

    // Point the layer at a Style JSON (URL or local file). It will pull the
    // vector tiles from whatever sources the style declares and render them.
    // (This is MapLibre's public demo style, intended for testing.)
    var layer = new MvtTilesAsyncLayer("https://demotiles.maplibre.org/style.json");

    // Optional: cache remote vector tiles on disk so they are not re-downloaded.
    layer.VectorTileCache = new FileTileCache(@"path\to\cache\folder");

    layerOverlay.Layers.Add(layer);

    await layer.OpenAsync();
    MapView.CurrentExtent = MaxExtents.SphericalMercator;
    await MapView.RefreshAsync();
}

MvtTilesAsyncLayer reads both its styles and its data locations from the Style JSON, so on its own it needs one to draw anything — with an empty StyleJsonUri it has no sources and renders nothing but the background. The built-in default line/area/point styles it falls back to in that case only produce output through the local-file subclasses below, which supply the tiles themselves regardless of the style (see their Data only mode).

VectorPmTilesAsyncLayer

VectorPmTilesAsyncLayer reads vector tiles directly from a local PMTiles archive. A PMTiles archive is a single file that holds an entire vector tileset, which makes it easy to ship or host on static/cloud storage. The layer reads the archive's header for the dataset bounds and maximum zoom (MaxZoomOfData), exposes the raw archive metadata through MetaDataJson and the parsed Header, and transparently gunzips gzip-compressed MVT payloads. PMTiles always addresses tiles with the XYZ (slippy-map) convention.

You can use it two ways:

  • Data only — pass just the .pmtiles path and get ThinkGeo's built-in default styling.
  • Data + Style JSON — pass the .pmtiles path and a Style JSON (local file or URL). The tile data comes from your archive while the appearance comes from the style.

When you supply a Style JSON, the source id and source-layer names referenced by its layers must match the layer names inside your data file for the styles to bind. Styles authored against a well-known schema such as OpenMapTiles line up automatically when your data follows the same schema; otherwise, edit the source-layer values in the style (for example with Maputnik) to match your data.

Example Usage with Desktop Edition

// With ThinkGeo.UI.Wpf or ThinkGeo.UI.WinForms
private async void MapView_OnLoaded(object sender, RoutedEventArgs e)
{
    MapView.MapUnit = GeographyUnit.Meter;
    var layerOverlay = new LayerOverlay();
    MapView.Overlays.Add(layerOverlay);

    // Data comes from the local .pmtiles; styling comes from the Style JSON.
    var layer = new VectorPmTilesAsyncLayer(@"path\to\sample.pmtiles", @"path\to\sample-style.json");
    layerOverlay.Layers.Add(layer);

    await layer.OpenAsync();
    MapView.CurrentExtent = layer.GetBoundingBox();
    await MapView.RefreshAsync();
}

VectorMbTilesAsyncLayer

VectorMbTilesAsyncLayer reads vector tiles directly from a local .mbtiles SQLite database. It supports the same two modes as VectorPmTilesAsyncLayer (data only for default styling, or data plus a Style JSON for a custom look). Reading its metadata table gives it the dataset's bounds (used by GetBoundingBox), its maximum zoom level (MaxZoomOfData), and the full set of key/value entries through the MetaData dictionary. Both the xyz and tms tile-row schemes are detected automatically from the scheme metadata.

Example Usage with MAUI Edition

// With ThinkGeo.UI.Maui
private bool _initialized;
private async void MapView_OnSizeChanged(object sender, EventArgs e)
{
    if (_initialized)
        return;
    _initialized = true;

    MapView.MapUnit = GeographyUnit.Meter;

    var layerOverlay = new LayerOverlay();
    layerOverlay.TileType = TileType.MultiTile;
    MapView.Overlays.Add(layerOverlay);

    var dataFilePath = Path.Combine(FileSystem.Current.AppDataDirectory, "sample.mbtiles");
    var jsonFilePath = Path.Combine(FileSystem.Current.AppDataDirectory, "sample-style.json");

    var layer = new VectorMbTilesAsyncLayer(dataFilePath, jsonFilePath);
    layerOverlay.Layers.Add(layer);

    await layer.OpenAsync();

    // Set up the MapScale and Center Point from the dataset bounds.
    var bbox = layer.GetBoundingBox();
    MapView.MapScale = MapUtil.GetScale(bbox, MapView.CanvasWidth, GeographyUnit.Meter);
    MapView.CenterPoint = bbox.GetCenterPoint();

    await MapView.RefreshAsync();
}

Common properties

Because VectorPmTilesAsyncLayer and VectorMbTilesAsyncLayer inherit from MvtTilesAsyncLayer, the following members are available on all three layers. The HTTP-related members apply only when tiles are fetched over the network — that is, to a remote-source MvtTilesAsyncLayer. The local-file subclasses inherit them but read their tiles from disk, so setting them there has no effect.

Member Description
StyleJsonUri The Style JSON to load; an http(s) URL or a local file path.
LabelDisplayMode Controls how labels are drawn (e.g. shapes and labels, shapes only, labels only).
ScaleFactor Scales the sprite icons defined in the Style JSON.
MaxConcurrentTileLoads Caps how many tiles are fetched/decoded at once. Defaults to a higher value for the local-file layers, which read from disk.
ProjectionConverter Reprojects the data if the map is not in the layer's native Spherical Mercator.
VectorTileCache A FileTileCache used to cache remote vector tiles on disk.
HttpClient, WebProxy, Credentials, UserAgent, TimeoutInSeconds Configure the outbound HTTP requests used to fetch remote vector tiles (remote-source MvtTilesAsyncLayer only).
SendingHttpRequest, ReceivedHttpResponse events Hooks to inspect or cancel each remote tile HTTP request/response (for example, to inject an auth header).

Where to get data and styles

Vector tile data

  • MapTiler offers global and regional MBTiles/PMTiles downloads (license or payment required for most datasets).
  • OpenMapTiles provides the widely used vector schema and downloadable extracts.
  • MapLibre publishes a free demo tileset and Style JSON.
  • Protomaps can build and host PMTiles archives, including a downloadable planet build.

Styles

  • Maputnik is an online editor for authoring and exporting MapLibre / Mapbox style documents; it is also the easiest place to retarget a style's source-layer names to your own data.

Note on data and style licensing. ThinkGeo draws vector tiles with its own rendering engine, so the tiles, styles, fonts, and sprites you load must be licensed for use with a third-party (non-Mapbox) renderer. In particular, Mapbox-hosted resources — mapbox:// URLs or api.mapbox.com tiles, styles, glyphs, and sprites — are governed by the Mapbox Terms of Service, which generally do not allow Mapbox tiles, styles, or data to be used with a non-Mapbox rendering engine. Prefer open or self-hosted data (OpenMapTiles, MapLibre, PMTiles) or data you have licensed for third-party rendering (for example, MapTiler). The Mapbox Vector Tile and MapLibre style formats themselves are open specifications; the restriction is on Mapbox-hosted content, not on the format.

PMTiles vs. MBTiles

Both formats hold the same Mapbox Vector Tiles and render identically in ThinkGeo, which reads either one from a local file. So the choice is about your data pipeline and how you distribute the tileset, not about the map itself.

PMTiles (VectorPmTilesAsyncLayer) MBTiles (VectorMbTilesAsyncLayer)
File size Typically smaller — identical tiles are stored once (built-in dedup) with no database overhead Larger — SQLite container
Cutting a subset Fast: extracting a smaller area or zoom range is mostly copying tile bytes, with no re-encoding (pmtiles extract) Requires querying and rewriting the SQLite database
Serving over the web Deploy the file as-is to static/cloud storage (S3, R2, CDN, GitHub Pages); clients read only the byte ranges they need — no tile server required Usually needs a tile server to serve over the web
Maturity & tooling Newer and cloud-native Older and ubiquitous — almost every tile tool reads and writes it, and it is a normal SQLite database you can query or update

Rules of thumb:

  • Pick PMTiles for a small, portable, immutable file you can ship inside an app or drop on static hosting.
  • Pick MBTiles if your toolchain already produces it, or you want a mutable SQLite database you can query and edit.

The "no server" advantage of PMTiles applies to any other clients you serve the same file to — the ThinkGeo layer itself always opens a local file either way.