DHI.Services.Rasters.WebApi — Internal Developer Guide¶
This document explains what the Rasters WebApi does, how it is structured, how to host it, and most importantly, how to use it from other services or tools. You don’t need the source code to follow along; everything you need is described here with concrete request/response examples.
1) What this service is¶
Rasters WebApi exposes HTTP endpoints for:
- Serving radar images from on-disk repositories (several formats supported)
- Performing zone-based rainfall analytics (intensity, depth, min/max/avg) over time ranges
- Returning ready-to-draw PNG bitmaps of radar images and color legends
- Managing zones (create, list, delete)
It’s designed as a thin HTTP facade over the domain library DHI.Services.Rasters:
- The WebApi wires repositories (where and how images are loaded), services (business logic), and JSON serialization (so types travel cleanly over the wire).
- It uses JWT Bearer authentication and ASP.NET Core authorization policies.
- It ships with opinionated JSON serialization options (e.g., support for the
Enumerationpattern and custom value converters).
2) High-level architecture¶
HTTP Client
|
v
ASP.NET Core (Controllers)
|
+--> RadarImagesController<TImage> --------+
| |
| Services.Get<RadarImageService<TImage>>(connectionId)
| |
+--> ZoneService <--------------------+----+
| |
v v
Radar repositories Zone repository
(on-disk; format-specific) (JSON file in App_Data)
Key parts:
RadarImagesController<TImage>— generic controller that works for any radar image type you choose (e.g., ASCII, IRIS CAPPI).Services.Get<...>(connectionId)— a simple service locator that resolves a namedRadarImageService<TImage>you registered at startup (see “Hosting & wiring”).ZoneService+ZoneRepository— zone storage in a JSON file under App_Data.- SerializerOptionsDefault — a singleton bundle of JSON options and converters used everywhere.
3) Authentication & authorization¶
- AuthN: JWT Bearer (
Authorization: Bearer <token>). - AuthZ:
- Most endpoints require an authenticated user.
- Zone creation and deletion require policy
AdministratorsOnly, implemented as a claimhttp://schemas.microsoft.com/windows/2008/06/identity/claims/groupsid = "Administrators".
Typical appsettings¶
Tokens:Issuer,Tokens:Audience, and an RSA public key (Tokens:PublicRSAKey) are required to validate tokens.- HSTS settings and Swagger metadata can also be provided via configuration.
4) Connections, repositories, and image types¶
A “connection” is just a name you give to a RadarImageService<TImage> that knows how to read a specific folder/filename pattern.
At startup you register one or more connections:
ServiceLocator.Register(
new RadarImageService<AsciiImage>(
new DelimitedAsciiRepository(
"[AppData]RadarImages;PM_{datetimeFormat}.txt;yyyyMMddHH_$$$".Resolve()
)
),
"ascii" // <-- connectionId
);
connectionIdis the URL segment you will use in requests (e.g.,/api/radarimages/ascii/...).DelimitedAsciiRepository(and other repositories) point at a folder with radar files and specify how to parse the timestamp from file names.- The connection string syntax is
"<folder>;<filePattern>;<dateTimeFormat>". Example with a counter per hour:C:\data\RadarImages;PM_{datetimeFormat}.txt;yyyyMMddHH_$$$$$$= “hours since base date” counter###= “days since base date” counter{datetimeFormat}is replaced by the parser according to thedateTimeFormat.
The token
[AppData]is resolved at runtime to the application’s App_Data folder (see “Hosting & wiring”).
Supported image formats (out of the box)¶
- Delimited ASCII:
DHI.Services.Rasters.Radar.DELIMITEDASCII.AsciiImage(+DelimitedAsciiRepository) - ESRI ASCII:
DHI.Services.Rasters.Radar.ESRIASCII.AsciiImage(+EsriAsciiRepository) - IRIS CAPPI:
DHI.Services.Rasters.Radar.IRISCAPPI.RadarImage(+ IRIS CAPPI-compatible repository; wire it similarly)
You can add more formats by introducing a new repository and registering a connection the same way.
5) API Endpoints¶
All routes below are prefixed with:
/api/radarimages/{connectionId}
…where {connectionId} is the name you registered (e.g., ascii).
5.1 Radar image retrieval¶
Get image by timestamp¶
GET /api/radarimages/{connectionId}/{date}
dateis an ISO datetime (YYYY-MM-DDTHH:mm:ss).- Returns the image object (JSON). If the image format stores reflectivity, you still receive raw values; conversion to intensity happens in analytics/bitmap endpoints when required.
Get last image¶
GET /api/radarimages/{connectionId}/last
Get last image before a timestamp¶
GET /api/radarimages/{connectionId}/lastbefore/{date}
Get first image after a timestamp¶
GET /api/radarimages/{connectionId}/firstafter/{date}
Batch “last before” for many timestamps¶
POST /api/radarimages/{connectionId}/list/lastbefore
Body: ["2025-02-01T10:00:00","2025-02-01T11:15:00"]
Batch “first after” for many timestamps¶
POST /api/radarimages/{connectionId}/list/firstafter
Body: ["2025-02-01T10:00:00","2025-02-01T11:15:00"]
5.2 Image availability (datetimes)¶
Last / first datetime¶
- GET
/api/radarimages/{connectionId}/datetime/last - GET
/api/radarimages/{connectionId}/datetime/first
List datetimes in an interval¶
GET /api/radarimages/{connectionId}/datetimes?from=...&to=...
- Both
fromandtoare optional; without them you get the full range.
“First after” and “Last before” for many datetimes¶
- POST
/api/radarimages/{connectionId}/datetimes/firstafter - POST
/api/radarimages/{connectionId}/datetimes/lastbeforeBody:["2025-02-01T10:00:00","2025-02-02T01:00:00"]
5.3 Zone-based analytics¶
All zone endpoints require a valid zone id. See “Zones API” for creating and listing zones.
Units
- Intensities are usually
mm/h(millimeters per hour).- Depth (accumulated rainfall) is
mm.- If the underlying image is reflectivity, the API converts to intensity using Marshall–Palmer and default coefficients (unless specified otherwise in the domain layer).
Accumulated rainfall (depth) in an interval¶
GET /api/radarimages/{connectionId}/depth/{zoneId}?from=2025-02-01T00:00:00&to=2025-02-01T06:00:00
- If
tois omitted, it uses the last available image time.
Accumulated rainfall (depth) in the last N hours¶
GET /api/radarimages/{connectionId}/depth/{zoneId}/hours/{hours}
Time series of average intensity (mm/h)¶
GET /api/radarimages/{connectionId}/intensities/{zoneId}?from=...&to=...
- Returns
{ "2025-02-01T00:10:00Z": 0.8, "2025-02-01T00:20:00Z": 1.1, ... }.
Max / Average intensity (mm/h) in an interval¶
- GET
/api/radarimages/{connectionId}/intensity/max/{zoneId}?from=...&to=... - GET
/api/radarimages/{connectionId}/intensity/average/{zoneId}?from=...&to=...
Average intensity (mm/h) in the last N hours¶
GET /api/radarimages/{connectionId}/intensity/average/{zoneId}/hours/{hours}
Limits
- For performance, the analytics layer imposes a maximum analysis span (default: 30 days). Larger requests will be rejected.
- Internally, long spans are broken into smaller batches, so you can safely query multi-day windows up to that limit.
5.4 Bitmaps (PNG images)¶
You can request ready-to-draw bitmaps (image/png). The service auto-converts reflectivity to intensity when needed.
Image bitmap at a timestamp¶
GET /api/radarimages/{connectionId}/{date}/bitmap?style=IntensityDefault
- If
styleis omitted,IntensityDefaultis used.
Last image bitmap¶
GET /api/radarimages/{connectionId}/last/bitmap?style=IntensityDefault
Legend / color style bitmap¶
GET /api/radarimages/{connectionId}/style/{style}/bitmap?height=300&width=100
Available style values (case-sensitive display names):
IntensityDefaultIntensityLightYellowToRedIntensityLightYellowToRedLogarithmicReflectivityDefaultReflectivityLightYellowToRed
6) Zones API¶
Base path: /api/zones
Zones are collections of pixel weights (1-based raster coordinates) that define an area of interest. Pixel weights must sum (≈) to 1.0 (validated).
Endpoints¶
-
Get zone by id GET
/api/zones/{id} -
List zones GET
/api/zones -
Count zones GET
/api/zones/count -
List zone ids GET
/api/zones/ids -
Create zone (AdministratorsOnly) POST
/api/zonesBody is aZoneDTO:{ "Id": "City.Center", "Name": "City Center", "Type": "Polygon", "ImageSize": { "Width": 480, "Height": 480 }, "PixelWeights": [ { "Pixel": { "Col": 101, "Row": 210 }, "Weight": { "Value": 0.25 } }, { "Pixel": { "Col": 102, "Row": 210 }, "Weight": { "Value": 0.25 } }, { "Pixel": { "Col": 101, "Row": 211 }, "Weight": { "Value": 0.25 } }, { "Pixel": { "Col": 102, "Row": 211 }, "Weight": { "Value": 0.25 } } ] }Notes:
Typemust be one of:Point,Line string,Polygon(exact display names).ImageSizemust match the raster dimensions the zone applies to.PixelWeightsmust (approximately) sum to 1.0.
-
Delete zone (AdministratorsOnly) DELETE
/api/zones/{id}
Storage¶
By default, zones are stored in a JSON file under App_Data (see “Hosting & wiring”). The repository class resolves the actual path from your configuration and environment.
7) JSON formats & serialization¶
The API uses a standardized System.Text.Json configuration via SerializerOptionsDefault:
- No camel-casing by default (property naming policy is
null). - Nulls are omitted.
- Converters for:
- .NET enums as strings.
- The internal
Enumerationpattern (so types likeColorGradientType,ZoneType, etc. serialize cleanly). - Polymorphic connection & parameter types (for scenarios where connections are created from configuration).
- Zone types and zone dictionaries.
PixelValueType(reflectivity vs intensity).
You generally don’t need to worry about these—just consume and produce the JSON shown in the examples above.
8) Hosting & wiring¶
Minimal host¶
-
Program.cs Standard ASP.NET Core
CreateHostBuilder(...).UseStartup<Startup>(). -
Startup.ConfigureServices(...)
-
Configure JWT authentication.
- Add authorization policies.
- Enable API Versioning (default
1.0; via headerapi-versionor queryapi-version/version/ver). - Add Controllers + JSON options from
SerializerOptionsDefault. - Add HSTS/Swagger (optional but recommended).
-
Register a ZoneRepository that points to a JSON file in App_Data:
services.AddScoped<IZoneRepository>(provider => new Rasters.WebApi.ZoneRepository("zones.json")); -
Startup.Configure(...)
-
Standard ASP.NET Core pipeline + Swagger UI, HTTPS redirection, response compression, authn/z, etc.
-
Set App_Data:
var contentRootPath = Configuration.GetValue("AppConfiguration:ContentRootPath", env.ContentRootPath); AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(contentRootPath, "App_Data")); -
Register at least one radar connection:
ServiceLocator.Register( new RadarImageService<AsciiImage>( new DelimitedAsciiRepository( "[AppData]RadarImages;PM_{datetimeFormat}.txt;yyyyMMddHH_$$$".Resolve() ) ), "ascii" ); -
Controller binding Add a concrete controller for the image type you’re exposing:
public class RadarImagesController : RadarImagesController<AsciiImage>
{
public RadarImagesController(IZoneRepository zoneRepository) : base(zoneRepository) {}
}
You can host multiple concrete controllers if you want to expose multiple image types under different routing prefixes.
Running¶
- Place your radar files in the folder referenced by your repository connection string (often under
App_Data\RadarImages). - Place your zones.json (or let the API write it after creating zones).
- Start the WebApi; open Swagger UI to explore the endpoints.
9) Practical examples¶
Replace
{host}with your base URL and{token}with a valid JWT. Replaceasciiwith your actualconnectionId.
Get the latest image metadata¶
curl -H "Authorization: Bearer {token}" \
"{host}/api/radarimages/ascii/last?api-version=1"
Get datetimes available today¶
curl -H "Authorization: Bearer {token}" \
"{host}/api/radarimages/ascii/datetimes?from=2025-02-01T00:00:00&to=2025-02-01T23:59:59"
Get intensity time series for a zone¶
curl -H "Authorization: Bearer {token}" \
"{host}/api/radarimages/ascii/intensities/City.Center?from=2025-02-01T00:00:00&to=2025-02-01T06:00:00"
Example response:
{
"2025-02-01T00:10:00": 0.7,
"2025-02-01T00:20:00": 0.9,
"2025-02-01T00:30:00": 1.2
}
Get accumulated depth (mm) in last 6 hours¶
curl -H "Authorization: Bearer {token}" \
"{host}/api/radarimages/ascii/depth/City.Center/hours/6"
Get a PNG bitmap for the latest image (IntensityDefault)¶
curl -H "Authorization: Bearer {token}" \
-o latest.png \
"{host}/api/radarimages/ascii/last/bitmap?style=IntensityDefault"
Get a PNG legend for a style¶
curl -H "Authorization: Bearer {token}" \
-o legend.png \
"{host}/api/radarimages/ascii/style/IntensityLightYellowToRed/bitmap?height=300&width=120"
Create a zone¶
curl -X POST -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
-d '{
"Id": "City.Center",
"Name": "City Center",
"Type": "Polygon",
"ImageSize": { "Width": 480, "Height": 480 },
"PixelWeights": [
{ "Pixel": { "Col": 101, "Row": 210 }, "Weight": { "Value": 0.25 } },
{ "Pixel": { "Col": 102, "Row": 210 }, "Weight": { "Value": 0.25 } },
{ "Pixel": { "Col": 101, "Row": 211 }, "Weight": { "Value": 0.25 } },
{ "Pixel": { "Col": 102, "Row": 211 }, "Weight": { "Value": 0.25 } }
]
}' \
"{host}/api/zones"
10) Troubleshooting¶
-
404 / Not found
- The
connectionIdmust be registered at startup. - The requested timestamp must exactly match an available image time (use the
datetimesendpoint to discover). - The
zoneIdmust exist.
- The
-
400/500 when requesting bitmaps
- The
stylemust match one of the knownColorGradientTypedisplay names exactly.
- The
-
“Maximum analysis timespan exceeded”
- Analytics requests are bounded (default 30 days). Split long requests into smaller windows.
-
Pixel weights must sum to \~1.0
- Zone creation will be rejected if weights don’t add up (rounded to 3 decimals).
-
ESRI/Delimited/IRIS CAPPI file locations
- Double-check the connection string and that
[AppData]resolves to your content root’s App_Data (set inStartup.Configure).
- Double-check the connection string and that
11) Extensibility¶
-
Add a new radar format
- Implement a repository that loads your files into a radar image type (conforming to
IRadarImage). - Register it at startup with a new
connectionId. - Optionally, expose a dedicated
RadarImagesController<YourImageType>for clarity.
- Implement a repository that loads your files into a radar image type (conforming to
-
Dynamic connections via connection types
- The WebApi includes helper connection wrappers (e.g.,
EsriAsciiRadarImageServiceConnection,DelimitedAsciiRadarImageServiceConnection,IrisCappiRadarImageServiceConnection) that support[AppData]resolution when creating services dynamically (e.g., from a connections catalog). If you use such a catalog, supply:RepositoryType— fully-qualified .NET type name of your repository.ConnectionString— using the same<folder>;<filePattern>;<dateTimeFormat>syntax.
- The wrapper
Create()method will instantiate the repository and produce aRadarImageService<TImage>.
- The WebApi includes helper connection wrappers (e.g.,
-
Custom color styles
- Define new
ColorGradientTypes in the domain library and they’ll immediately work in/style/{style}/bitmapand image rendering.
- Define new