Skip to content

DHI.Services.PostgreSQL for GIS MapStyle — Internal Developer Guide

This document is for DHI developers who want to store and manage map styles in PostgreSQL and use them across the GIS Maps stack (e.g., MIKECore/DFS2-DFSU maps, GeoServer WMS, custom map sources).

For deeper provider details and shared infrastructure, see PostgreSQL Providers.


1) What this provider is

DHI.Services.Provider.PostgreSQL.MapStyleRepository is a drop-in implementation of IMapStyleRepository backed by PostgreSQL. It’s intentionally format-agnostic:

  • For MIKECore sources, MapStyle.StyleCode typically contains palette/threshold definitions used by the contouring pipeline.
  • For GeoServer WMS, StyleCode can hold SLD XML (or a zipped SLD) if you choose to store it centrally.
  • For any custom source, StyleCode is an opaque text blob you interpret in your renderer.

Why use it

  • Centralize styles (edit once, every renderer updates).
  • Share the same IDs across services (WebApi + background jobs).
  • Built-in table bootstrap on start (creates table if missing).
  • Simple CRUD with minimal coupling.

2) Data model & bootstrap

At startup, the repository ensures the (default) table exists:

CREATE TABLE IF NOT EXISTS public.map_style (
  id         varchar(255) PRIMARY KEY,
  name       varchar(255),
  style_code text
);

You can override the table (and/or schema) via the connection string using the Table= addition (details below).

The bootstrap does not create non-public schemas. If you point to gis.map_style, make sure gis exists.


3) Repository API (what you can do)

MapStyleRepository implements IMapStyleRepository:

Method Description
Add(MapStyle) Insert a new style (id, name, style_code).
Contains(id) SELECT EXISTS… for a quick existence check.
Get(id) Fetch a single style (id, name, style_code).
GetAll() Enumerate all styles.
GetIds() Enumerate all IDs only.
Remove(id) Delete by id.
Update(MapStyle) Update name and style_code by id.
Count() Count rows.

Internally it uses Npgsql and is intentionally synchronous to keep surface area small in web hosts.


4) StyleCode conventions (consumer-driven)

The repository does not enforce a format. Recommended patterns:

4.1 MIKECore palettes (example)

Used by Dfs2MapSource / DfsuMapSource:

0~10:#0000FF,#00FFFF,#00FF00,#FFFF00,#FF0000
  • Left side defines the range (0~10).
  • Right side are band colors (discrete) in order.
  • Optionally keep JSON alongside if you prefer a richer schema.

4.2 GeoServer SLD (example)

When used for WMS styling:

<?xml version="1.0" encoding="UTF-8"?>
<sld:StyledLayerDescriptor >
  <!-- your rules, color maps, etc. -->
</sld:StyledLayerDescriptor>

Your GeoServer map source may still upload styles through GeoServer’s REST API; this repo is just a neutral store if you want one source of truth.


5) Connection strings & options

Minimum:

Server=localhost;Port=5432;Database=ProviderTest;User Id=postgres;Password=your_pw

Optional table override:

Server=…;Database=…;User Id=…;Password=…;Table=gis.map_style
  • If Table= is present, the repo will use that instead of the default public.map_style.
  • The bootstrap checks for table existence and creates it in that schema when missing.

Security/ops (recommended):

  • Use pooled connections (Npgsql default).
  • Prefer a least-privilege role limited to SELECT/INSERT/UPDATE/DELETE on the map_style table.
  • Use SSL in production (Ssl Mode=Require;Trust Server Certificate=false if properly CA-signed).

6) Wiring it up

You can integrate the PostgreSQL MapStyle repository via connections.json (WebApi hosts) or programmatically.

A) Using the Connections module (WebApi hosts)

Add this to connections.json (as requested):

"dfs2-map-postgresql": {
  "$type": "DHI.Services.GIS.WebApi.MapServiceConnection, DHI.Services.GIS.WebApi",
  "MapSourceConnectionString": "[AppData]dfs2\\R20141001.dfs2",
  "MapSourceType": "DHI.Services.Provider.MIKECore.Dfs2MapSource, DHI.Services.Provider.MIKECore",
  "MapStyleConnectionString": "Server=localhost;Port=5432;Database=ProviderTest;User Id=postgres;Password=Solutions!",
  "MapStyleRepositoryType": "DHI.Services.Provider.PostgreSQL.MapStyleRepository, DHI.Services.Provider.PostgreSQL",
  "Name": "DFS2 with PostgreSQL MapStyle",
  "Id": "dfs2-map-postgresql"
}

That gives you a MapService where:

  • Source = MIKECore DFS2 (Dfs2MapSource)
  • Style repo = PostgreSQL (MapStyleRepository)

You can now request maps by style id stored in PG:

GET /api/maps/dfs2-map-postgresql?request=GetMap&width=1280&height=720
    &styles=rain                       // <-- style id in PG
    &crs=EPSG:3857
    &bbox=1110000,6400000,1130000,6420000
    &item=Water%20Level
    &timestamp=2014-10-01T10:00:00Z

B) Programmatic registration

using DHI.Services;
using DHI.Services.GIS.Maps;
using DHI.Services.Provider.MIKECore;
using DHI.Services.Provider.PostgreSQL;

// Map source: MIKECore DFS2
var dfs2Path   = Path.Combine(appData, "dfs2", "R20141001.dfs2");
var mapSource  = new Dfs2MapSource(dfs2Path);

// Map styles: PostgreSQL
var pg = "Server=localhost;Port=5432;Database=ProviderTest;User Id=postgres;Password=Solutions!";
// Optional: override table (schema must exist)
// pg += ";Table=gis.map_style";

var styleRepo  = new DHI.Services.Provider.PostgreSQL.MapStyleRepository(pg);
var styleSvc   = new MapStyleService(styleRepo);

// Register a MapService under a key
ServiceLocator.Register(new MapService(mapSource, styleSvc), "dfs2-map-postgresql");

// Example usage later:
var bbox = BoundingBox.Parse("1110000,6400000,1130000,6420000");
using var img = ServiceLocator.Resolve<MapService>("dfs2-map-postgresql").GetMap(
  style: "rain",              // style id stored in PostgreSQL
  crs: "EPSG:3857",
  boundingBox: bbox, width: 1280, height: 720,
  sourceId: "",               // ctor already fixed the file
  dateTime: new DateTime(2014,10,1,10,0,0, DateTimeKind.Utc),
  item: "Water Level",
  parameters: Parameters.Empty);

7) Seeding & managing styles

You can seed styles either via SQL or through the repo API.

7.1 SQL

INSERT INTO public.map_style (id, name, style_code)
VALUES
('rain', 'Rainfall (0–10mm, discrete)',
 '0~10:#0000FF,#00FFFF,#00FF00,#FFFF00,#FF0000');

7.2 Repository (C#)

var repo = new DHI.Services.Provider.PostgreSQL.MapStyleRepository(pgConnString);
if (!repo.Contains("rain"))
{
  repo.Add(new MapStyle("rain", "Rainfall (0–10mm, discrete)",
                        "0~10:#0000FF,#00FFFF,#00FF00,#FFFF00,#FF0000"));
}

var style = repo.Get("rain").OrDefault();
repo.Update(new MapStyle(style.Id, "Rainfall (v2)",
                         "0~20:#0000FF,#00BFFF,#00FFFF,#00FF7F,#7CFC00,#FFFF00,#FFA500,#FF0000"));

8) End-to-end example with MIKECore

var pg = "Server=localhost;Port=5432;Database=ProviderTest;User Id=postgres;Password=Solutions!";
var styleSvc = new MapStyleService(new DHI.Services.Provider.PostgreSQL.MapStyleRepository(pg));
var svc = new MapService(new Dfs2MapSource(@"C:\data\dfs2\R20141001.dfs2"), styleSvc);

// Render using a style stored in PG
var bbox = BoundingBox.Parse("1110000,6400000,1130000,6420000");
using var bmp = svc.GetMap(
  style: "rain", crs: "EPSG:3857",
  boundingBox: bbox, width: 1024, height: 768,
  sourceId: "", dateTime: null, item: "3",
  parameters: new Parameters { { "isoline", "Contour" } });

using var fs = File.OpenWrite("dfs2-postgres-style.png");
bmp.Encode(SKEncodedImageFormat.Png, 90).SaveTo(fs);

9) Troubleshooting

  • Schema not found: If you set Table=gis.map_style, ensure CREATE SCHEMA gis; exists. The bootstrap creates the table, not the schema.
  • Permissions: Use a role with DML on the table, not superuser.
  • Migrations: The table shape is small and stable. If you need additional fields, prefer a new table (Table= override) to avoid breaking existing services.
  • Connection pooling: Npgsql pools by default; keep your repository instances long-lived (scoped/singleton in DI).
  • Large SLDs: style_code is TEXT; it’s safe for large SLDs or JSON payloads.

10) What you’ll actually touch

  • DHI.Services.Provider.PostgreSQL.MapStyleRepository
    • Ctors: (connectionString) or (connectionString, ILogger)
    • Optional: ;Table=<schema>.<table> in connection string
  • MapService wiring:
    • Any IMapSource (e.g., MIKECore DFS2/DFSU)
    • This style repo for centralized styles

With the PostgreSQL MapStyle repository in place, you can standardize styles across all map sources, swap style backends without touching renderers, and manage styles centrally via SQL or the repository API—while the rest of the GIS Maps pipeline remains unchanged.