I am trying implement raster mosaicking to have a finer grain control over overlapping rasters.
From my limited research, I believe chaining raster functions seems the most promising approach but the documentation for this is confusing at best.
For example, MosaicDatasetRaster extends Raster but doesn't have the option to set the RasterFunction however there does seem to be an option on the AddRastersParameters class to provide a raster function template file. The documentation states "The path to the raster function template".
So my research leads me to ArcGIS desktop which has the ability to save such a template however every attempt I have made to load a file crashes with an Invalid argument: Failed to read 6107 bytes from <path>.
My first approach was to attempt the JSON, second attempt to use the XML schema.
So to summarise my question(s)
MosaicDatasetRaster rasterMosaic = MosaicDatasetRaster.Create(@"mosaic.sqlite", "mosaic_rasters", SpatialReferences.Wgs84);
rasterMosaic.LoadStatusChanged += async (s, e) =>
{
if (e.Status == Esri.ArcGISRuntime.LoadStatus.Loaded)
{
AddRastersParameters parameters = new()
{
InputDirectory = @"tiff",
RasterFunctionTemplateFile = @"mask_function.xml"
};
await rasterMosaic.AddRastersAsync(parameters);
}
};
await rasterMosaic.LoadAsync();
RasterLayer rasterLayer = new(rasterMosaic)
{
Opacity = 0.5
};
Map.OperationalLayers.Add(rasterLayer);
<RasterFunctionTemplate xsi:type='typens:RasterFunctionTemplate'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:typens='http://www.esri.com/schemas/ArcGIS/3.3.0'>
<Name>Raster Function Template</Name>
<Description>xxcxdf</Description>
<Function xsi:type='typens:MaskFunction' id='ID1'>
<Name>Mask</Name>
<Description>Sets values that you do not want to display.</Description>
<PixelType>UNKNOWN</PixelType>
</Function>
<Arguments xsi:type='typens:MaskFunctionArguments' id='ID2'>
<Names xsi:type='typens:ArrayOfString' id='ID3'>
<String>Raster</String>
<String>NoDataInterpretation</String>
<String>NoDataValues</String>
<String>IncludedRanges</String>
<String>Invert</String>
</Names>
<Values xsi:type='typens:ArrayOfAnyType' id='ID4'>
<AnyType xsi:type='typens:Raster' id='ID5'>
<RasterBandCollectionNames xsi:type='typens:ArrayOfRasterBandCollectionName' id='ID6'>
<RasterBandCollectionName xsi:type='typens:RasterBandCollectionName' id='ID7'>
<NameString></NameString>
<DatasetName xsi:type='typens:RasterDatasetName' id='ID8'>
<WorkspaceName xsi:type='typens:WorkspaceName' id='ID9'>
<NameString>Raster Workspace = C:\Work\gtiff\;</NameString>
<PathName>C:\Work\gtiff\</PathName>
<BrowseName>Raster Data</BrowseName>
<WorkspaceFactoryProgID>esriDataSourcesRaster.RasterWorkspaceFactory</WorkspaceFactoryProgID>
<WorkspaceType>esriFileSystemWorkspace</WorkspaceType>
<ConnectionProperties xsi:type='typens:PropertySet' id='ID10'>
<PropertyArray xsi:type='typens:ArrayOfPropertySetProperty' id='ID11'>
<PropertySetProperty xsi:type='typens:PropertySetProperty' id='ID12'>
<Key>DATABASE</Key>
<Value xsi:type='xs:string'>C:\Work\gtiff\</Value>
</PropertySetProperty>
</PropertyArray>
</ConnectionProperties>
</WorkspaceName>
</DatasetName>
<BandIDs xsi:type='typens:ArrayOfInt' id='ID13'>
<Int>0</Int>
<Int>1</Int>
<Int>2</Int>
<Int>3</Int>
</BandIDs>
</RasterBandCollectionName>
</RasterBandCollectionNames>
</AnyType>
<AnyType xsi:type='typens:RasterFunctionVariable' id='ID14'>
<Name>NoDataInterpretation</Name>
<Description></Description>
<Value xsi:type='xs:int'>1</Value>
<IsDataset>false</IsDataset>
</AnyType>
<AnyType xsi:type='typens:RasterFunctionVariable' id='ID15'>
<Name>NoDataValues</Name>
<Description></Description>
<Value xsi:type='typens:ArrayOfString' id='ID16'>
<String>255</String>
<String>255</String>
<String>255</String>
<String>0</String>
</Value>
<IsDataset>false</IsDataset>
</AnyType>
<AnyType xsi:type='typens:RasterFunctionVariable' id='ID17'>
<Name>IncludedRanges</Name>
<Description></Description>
<Value xsi:type='typens:ArrayOfDouble' id='ID18'>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
<Double>0</Double>
</Value>
<IsDataset>false</IsDataset>
</AnyType>
<AnyType xsi:type='typens:RasterFunctionVariable' id='ID19'>
<Name>Invert</Name>
<Description></Description>
<Value xsi:type='xs:boolean'>false</Value>
<IsDataset>false</IsDataset>
</AnyType>
</Values>
</Arguments>
<Help></Help>
<Type>0</Type>
<Thumbnail xsi:type='xs:string'></Thumbnail>
<Definition></Definition>
<Group></Group>
<Tag></Tag>
<ThumbnailEx></ThumbnailEx>
<Properties xsi:type='typens:PropertySet' id='ID20'>
<PropertyArray xsi:type='typens:ArrayOfPropertySetProperty' id='ID21'>
<PropertySetProperty xsi:type='typens:PropertySetProperty' id='ID22'>
<Key>MatchVariable</Key>
<Value xsi:type='typens:RasterFunctionVariable' id='ID23'>
<Name>MatchVariable</Name>
<Description></Description>
<Value xsi:type='xs:int'>1</Value>
<IsDataset>false</IsDataset>
</Value>
</PropertySetProperty>
<PropertySetProperty xsi:type='typens:PropertySetProperty' id='ID24'>
<Key>UnionDimension</Key>
<Value xsi:type='typens:RasterFunctionVariable' id='ID25'>
<Name>UnionDimension</Name>
<Description></Description>
<Value xsi:type='xs:int'>0</Value>
<IsDataset>false</IsDataset>
</Value>
</PropertySetProperty>
</PropertyArray>
</Properties>
</RasterFunctionTemplate>
Working example on a single raster using the Mask Function
RasterFunction rasterFunction = RasterFunction.FromJson(
@"
{
""raster_function"":{""type"":""Mask_function""},
""raster_function_arguments"":
{
""nodata_values"":
{
""double_array"":[255, 255, 255, 0],
""type"":""Raster_function_variable""
},
""nodata_interpretation"":
{
""nodata_interpretation"":""all"",
""type"":""Raster_function_variable""
},
""raster"":
{
""name"":""raster"",
""is_raster"":true,
""type"":""Raster_function_variable""
},
""type"":""Raster_function_arguments""
},
""type"":""Raster_function_template""
}"
);
IReadOnlyList<string> myRasterNames = rasterFunction.Arguments.GetRasterNames();
rasterFunction.Arguments.SetRaster(myRasterNames[0], new Raster(@"C:\Work\gtiff\raster.tiff"));
RasterLayer rasterLayer = new(new Raster(rasterFunction))
{
Opacity = 0.5
};
Map.OperationalLayers.Add(rasterLayer);
Hi,
I reached out to raster SME on my team and he was able to use the code you provide. The only difference being parameters
are added before the mosaic raster is loaded and before setting the Raster.
Also need to set the Filter
on the AddRasterParameters
object.
Here is the sample code he used that worked as expected.
MosaicDatasetRaster rasterMosaic = MosaicDatasetRaster.Create(@"c:\gtiff\mosaic.sqlite", "mosaic_rasters", SpatialReferences.Wgs84);
AddRastersParameters parameters = new()
{
InputDirectory = @"c:\gtiff\images",
Filter = ".*tif"
};
await rasterMosaic.AddRastersAsync(parameters);
RasterFunction rasterFunction = RasterFunction.FromJson(
@"
{
""raster_function"":{""type"":""Mask_function""},
""raster_function_arguments"":
{
""nodata_values"":
{
""double_array"":[255, 255, 255, 0],
""type"":""Raster_function_variable""
},
""nodata_interpretation"":
{
""nodata_interpretation"":""all"",
""type"":""Raster_function_variable""
},
""raster"":
{
""name"":""raster"",
""is_raster"":true,
""type"":""Raster_function_variable""
},
""type"":""Raster_function_arguments""
},
""type"":""Raster_function_template""
}"
);
IReadOnlyList<string> myRasterNames = rasterFunction.Arguments.GetRasterNames();
rasterFunction.Arguments.SetRaster(myRasterNames[0], rasterMosaic);
RasterLayer rasterLayer = new(new Raster(rasterFunction))
{
Opacity = 0.5
};
Map.OperationalLayers.Add(rasterLayer);
er.