
Previous Post
Carmenta Engine on Linux/ARM
Build and deploy Carmenta Engine applications to Linux/ARM (Raspberry Pi) in C++, .NET Core, or Java, with sample setup and run instructions.
View PostLand classification provides information on what kind of surface is present such as land, water, urban area, forest, etc.
In an application, it can have many uses such as:
In this article, we will explore a scenario where land classification is used as a means to assist a user placing units on a map by removing features created on the wrong surface. A C# sample is provided to demonstrate the scenario.
Land classification is available in raster or vector format and can be visualized using Carmenta Studio as shown below:

In the sample, we have access to TIFF data and use an ImageDataSet to read it. The data provider should provide specifications describing what each raster value represents. In our case, the provider gave another file detailing the meaning of each possible value:
NLCD Land Cover Classification System Land Cover Class Definitions Water 11 Open Water 12 Perennial Ice/Snow Developed 21 Low Intensity Residential 22 High Intensity Residential 23 Commercial/Industrial/Transportation Barren 31 Bare Rock/Sand/Clay 32 Quarries/Strip Mines/Gravel Pits 33 Transitional Forested Upland 41 Deciduous Forest 42 Evergreen Forest 43 Mixed Forest Shrubland 51 Shrubland Non-natural Woody 61 Orchards/Vineyards/Other Herbaceous Upland 71 Grasslands/Herbaceous Herbaceous Planted/Cultivated 81 Pasture/Hay 82 Row Crops 83 Small Grains 84 Fallow 85 Urban/Recreational Grasses Wetlands 91 Woody Wetlands 92 Emergent Herbaceous Wetlands
For the visualization, each raster value can be mapped to a color:

This gives the following result:

Tip: In the sample, the visualization is accessible through the LandUse layer. There is also a LandUseSimplify layer (disabled by default) showcasing the RasterReclassificationOperator used to convert raster values to other values, with a basic visualization: green for land and blue for water.
While it can be quick to visualize land cover, Carmenta Engine does not provide a built-in function to give the same information from the code. However, you can call GetFeatures or GetFloatValueAt and use the result to decide validity based on your own conditions.
A basic function could look like:
internal enum SurfaceCategoriesEnum
{
Land = 0,
Water = 1,
}
private SurfaceCategoriesEnum GetSurfaceCategory(Feature feature)
{
Point loc = feature.GetGeometryAsPoint().Point;
// Convert to match dataset crs
if (feature.Crs != _landCoverageDataSet.Crs)
{
loc = feature.Crs.ProjectTo(_landCoverageDataSet.Crs, feature.GetGeometryAsPoint().Point);
}
// Retrieve raster value
var rasterValue = _landCoverageDataSet.GetFloatValueAt(loc);
if (rasterValue is 11 or 12)
{
return SurfaceCategoriesEnum.Water;
}
return SurfaceCategoriesEnum.Land;
}
In the sample, the land data is in TIFF format. To access a cell value from the raster, we use Dataset.GetFloatValueAt. We then test the result against 11 or 12, since those values match water surfaces as defined by the data provider. For vector data, you would use GetFeatures(), parse the features, and read the attribute containing the information. As before, the data provider should specify which attributes each object carries.
A detailed function would probably test all potential values and return a more appropriate state for each, but for our sample knowing if it’s land or water is enough.
The sample uses tactical symbols from MIL-STD-2525D, the US Department of Defense standard for joint military symbology. Each symbol carries a 20-character SIDC (Symbol Identification Code), and characters 5 and 6 identify which symbol set it belongs to (ground, air, sea surface, sea subsurface, space, and so on).
Once we know the type of surface, the following function can be used to test the sidc code and find the symbol set, telling us whether the symbol is a ground or sea tactical symbol. The function is used by the FeatureCreated event handler in the MapViewModel class. Each time a feature is created, it will be tested and deleted if invalid.
private void ValidateFeatureCreation(object sender, FeatureCreatedEventArgs args)
{
if (args.Feature.GeometryType == GeometryType.Point)
{
var res = GetSurfaceCategory(args.Feature);
var sidc = args.Feature.Attributes["sidc"].Value.ToString();
var symbolSet = sidc.Substring(4, 2);
bool invalidFeature = false;
switch (res)
{
case SurfaceCategoriesEnum.Land:
invalidFeature = (symbolSet == "30" || symbolSet == "35");
break;
case SurfaceCategoriesEnum.Water:
invalidFeature = !(symbolSet == "30" || symbolSet == "35");
break;
default:
break;
}
if (invalidFeature)
{
var ds = _dataSetLookup[args.Feature.Id.DataSetId];
using var guard = new Guard(ds);
ds.Remove(args.Feature.Id);
}
}
}
From the MIL-STD-2525D standard, we know the symbol set is defined by characters 5 and 6, with sea surface and subsurface symbols using values 30 and 35 respectively.
Tip: If you are not using tactical symbols, you can set an attribute on your feature to identify whether it is a ground or water feature. Update the function to read the attribute and define the condition to mark a feature as invalid.
Today we learned about land classification and how it can be used to assist users in their tasks. While our sample explores a solution where we create features, it is possible to use a custom tool and have live feedback.
As mentioned in the introduction, land classification has many uses in different fields, from visualization to terrain routing and landing zones for UAS. It can even be used in a vertical profile to define a custom height based on classification.
The complete C# sample project used in this article can be downloaded here: LandClassification.zip.

Build and deploy Carmenta Engine applications to Linux/ARM (Raspberry Pi) in C++, .NET Core, or Java, with sample setup and run instructions.
View Post