About the Create a custom raster function Sample
[C#]
WatermarkFunction.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using ESRI.ArcGIS.DataSourcesGDB; using ESRI.ArcGIS.DataSourcesRaster; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.ADF.CATIDs; namespace CustomFunction { [Guid("168721E7-7010-4a36-B886-F644437B164D")] [ClassInterface(ClassInterfaceType.None)] [ProgId("CustomFunction.WatermarkFunction")] [ComVisible(true)] public class WatermarkFunction : IRasterFunction, IPersistVariant, IDocumentVersionSupportGEN, IXMLSerialize, IXMLVersionSupport { #region Private Members UID myUID; // UID for the Watermark Function. IRasterInfo myRasterInfo; // Raster Info for the Watermark Function rstPixelType myPixeltype; // Pixel Type of the Watermark Function. string myName; // Name of the Watermark Function. string myDescription; // Description of the Watermark Function. bool myValidFlag; // Flag to specify validity of the Watermark Function. string myWatermarkImagePath; // Path to the Watermark Image. double myBlendPercentage; // Percentage of the blending. double blendValue; // Actual value of the blend percentage. IRasterFunctionHelper myFunctionHelper; // Raster Function Helper object. Bitmap myWatermarkImage; // Watermark Image object. #endregion public WatermarkFunction() { myWatermarkImagePath = ""; myBlendPercentage = 50.00; // Default value for the blending percentage. blendValue = 0.50; // Default value for the blend value. myName = "WatermarkFunction"; myPixeltype = rstPixelType.PT_UNKNOWN; // Default value for the pixel type. myDescription = "Add a watermark to the request."; myValidFlag = true; myFunctionHelper = new RasterFunctionHelperClass(); myWatermarkImage = null; myUID = new UIDClass(); myUID.Value = "{" + "168721E7-7010-4a36-B886-F644437B164D" + "}"; } #region IRasterFunction Members /// <summary> /// Name of the Raster Function. /// </summary> public string Name { get { return myName; } set { myName = value; } } /// <summary> /// Pixel Type of the Raster Function /// </summary> public rstPixelType PixelType { get { return myPixeltype; } set { myPixeltype = value; } } /// <summary> /// Output Raster Info for the Raster Function /// </summary> public IRasterInfo RasterInfo { get { return myRasterInfo; } } /// <summary> /// Description of the Raster Function /// </summary> public string Description { get { return myDescription; } set { myDescription = value; } } /// <summary> /// Initialize the Raster function using the argument object. This is one of the two /// main functions to implement for a custom Raster function. The raster object is /// dereferenced if required and given to the RasterFuntionHelper object to bind. /// </summary> /// <param name="pArguments">Arguments object used for initialization</param> public void Bind(object pArguments) { try { // Check if the Arguments object is of the correct type. IWatermarkFunctionArguments watermarkFuncArgs = null; if (pArguments is IWatermarkFunctionArguments) { watermarkFuncArgs = (IWatermarkFunctionArguments)pArguments; myBlendPercentage = watermarkFuncArgs.BlendPercentage; myWatermarkImagePath = watermarkFuncArgs.WatermarkImagePath; object inputRaster = watermarkFuncArgs.Raster; if (watermarkFuncArgs.Raster is IRasterFunctionVariable) { IRasterFunctionVariable rasterFunctionVariable = (IRasterFunctionVariable)watermarkFuncArgs.Raster; inputRaster = rasterFunctionVariable.Value; } // Call the Bind method of the Raster Function Helper object. myFunctionHelper.Bind(inputRaster); } else { // Throw an error if incorrect arguments object is passed. throw new System.Exception( "Incorrect arguments object. Expected: IWatermarkFunctionArguments"); } // Get the raster info and Pixel Type from the RasterFunctionHelper object. myRasterInfo = myFunctionHelper.RasterInfo; myPixeltype = myRasterInfo.PixelType; // Convert blending percentage to blending value. if (myBlendPercentage >= 0.0 && myBlendPercentage <= 100.0) blendValue = myBlendPercentage / 100.0; else /// A value of 50% is used as default. blendValue = 0.50; if (myWatermarkImagePath != "") { // Load the watermark image from the path provided myWatermarkImage = new Bitmap(myWatermarkImagePath); // and check the pixel type of the loaded image to see if its compatible. if (myWatermarkImage.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb && myWatermarkImage.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb) { // Throw error if the image is not compatible. throw new System.Exception( "Invalid watermark image. Please provide one with 8 bits per band in ARGB or RGB format."); } // Cleanup myWatermarkImage.Dispose(); myWatermarkImage = null; } } catch (Exception exc) { #region Cleanup if (myWatermarkImage != null) myWatermarkImage.Dispose(); myWatermarkImage = null; #endregion System.Exception myExc = new System.Exception( "Exception caught in Bind method of Watermark Function. " + exc.Message, exc); throw myExc; } } /// <summary> /// Read pixels from the input Raster and fill the PixelBlock provided with processed pixels. /// The RasterFunctionHelper object is used to handle pixel type conversion and resampling. /// The watermark image is then blended to the bottom right corner of the pixel block. /// </summary> /// <param name="pTlc">Point to start the reading from in the Raster</param> /// <param name="pRaster">Reference Raster for the PixelBlock</param> /// <param name="pPixelBlock">PixelBlock to be filled in</param> public void Read(IPnt pTlc, IRaster pRaster, IPixelBlock pPixelBlock) { BitmapData wMBitmapData = null; double pixelValue = 0.0; int wmRow = 0; int wmCol = 0; try { // Call Read method of the Raster Function Helper object. myFunctionHelper.Read(pTlc, null, pRaster, pPixelBlock); int wMBandOffset = 0; #region Reference Raster Properties // Get the pixel type of the reference raster to see if // it is compatible (8 bit). IRasterProps referenceProps = (IRasterProps)pRaster; if (referenceProps.PixelType != rstPixelType.PT_UCHAR && referenceProps.PixelType != rstPixelType.PT_CHAR) { throw new System.Exception( "Function can only be applied to 8bit data."); } #endregion #region Load watermark image object // Create new image object for the watermark image. myWatermarkImage = new Bitmap(myWatermarkImagePath); // Read number of bands of the watermark image. if (myWatermarkImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) wMBandOffset = 4; else { if (myWatermarkImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb) wMBandOffset = 3; else { throw new System.Exception( "Invalid bitmap. Please provide one with 8bits per band in ARGB or RGB format."); } } #endregion int pBHeight = pPixelBlock.Height; int pBWidth = pPixelBlock.Width; int wMHeight = myWatermarkImage.Height; int wMWidth = myWatermarkImage.Width; int wMRowIndex = 0; int wMColIndex = 0; int pBRowIndex = 0; int pBColIndex = 0; bool waterStartCol = false; bool waterStartRow = false; // Calculate the row/column values that specify where to start the blending. #region Calculate Indices /// If the number of rows of the pixelblock are more than the watermark image if (pBHeight >= wMHeight) { /// Set the row index to start blending in the pixelblock. pBRowIndex = pBHeight - wMHeight; } else /// If the number of rows of the watermark image is more than that of the pixelblock. { /// Set the row index to start blending in the watermark image. wMRowIndex = (wMHeight - pBHeight); waterStartRow = true; } /// If the number of cols of the pixelblock are more than the watermark image if (pBWidth >= wMWidth) { /// Set the col index to start blending in the pixelblock. pBColIndex = pBWidth - wMWidth; } else /// If the number of cols of the watermark image is more than that of the pixelblock. { /// Set the col index to start blending in the watermark image. wMColIndex = (wMWidth - pBWidth); waterStartCol = true; } #endregion #region Prepare Watermark Image for reading // Get the pixels from the watermark image using the lockbits function. wMBitmapData = myWatermarkImage.LockBits(new Rectangle(0, 0, wMWidth, wMHeight), ImageLockMode.ReadOnly, myWatermarkImage.PixelFormat); System.IntPtr wMScan0 = wMBitmapData.Scan0; int wMStride = wMBitmapData.Stride; #endregion // The unsafe keyword is used so that pointers can be used to access pixels // from the watermark image. unsafe { int wMPaddingOffset = wMStride - (myWatermarkImage.Width * wMBandOffset); // Start filling from the correct row, col in the pixelblock // using the indices calculated above System.Array pixelValues; if (pPixelBlock.Planes == 3) { #region 3 Band PixelBlock for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand) { byte* wMStartByte = (byte*)(void*)wMScan0; /// If the number of rows of the watermark image are more than the request. if (waterStartRow) /// Skip to the correct row in the watermark image. wMStartByte += (wMStride * wMRowIndex); IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock; pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand)); for (int i = pBRowIndex; i < pBHeight; i++, ++wmRow) { /// If the number of cols of the watermark image are more than the request. if (waterStartCol) /// Skip to the correct column in the watermark image. wMStartByte += (wMColIndex * wMBandOffset); for (int k = pBColIndex; k < pBWidth; k++, ++wmCol) { pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i)); if (Convert.ToDouble(wMStartByte[3]) != 0.0 && Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) == 1) { // Blend the pixelValue from the PixelBlock with the corresponding // pixel from the watermark image. pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * Convert.ToDouble(wMStartByte[2 - nBand])); } pixelValues.SetValue(Convert.ToByte(pixelValue), k, i); wMStartByte += wMBandOffset; } wMStartByte += wMPaddingOffset; } ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues); } #endregion } else { #region Not 3 Band PixelBlock for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand) { byte* wMStartByte = (byte*)(void*)wMScan0; /// If the number of rows of the watermark image are more than the request. if (waterStartRow) /// Skip to the correct row in the watermark image. wMStartByte += (wMStride * wMRowIndex); IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock; pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand)); for (int i = pBRowIndex; i < pBHeight; i++, ++wmRow) { /// If the number of cols of the watermark image are more than the request. if (waterStartCol) /// Skip to the correct column in the watermark image. wMStartByte += (wMColIndex * wMBandOffset); for (int k = pBColIndex; k < pBWidth; k++, ++wmCol) { pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i)); if (Convert.ToDouble(wMStartByte[3]) != 0.0 && Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, i, k)) == 1) { // Calculate the average value of the pixels of the watermark image double avgValue = (Convert.ToDouble(wMStartByte[0]) + Convert.ToDouble(wMStartByte[1]) + Convert.ToDouble(wMStartByte[2])) / 3; // and blend it with the pixelValue from the PixelBlock. pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * avgValue); } pixelValues.SetValue(Convert.ToByte(pixelValue), k, i); wMStartByte += wMBandOffset; } wMStartByte += wMPaddingOffset; } ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues); } #endregion } } #region Cleanup myWatermarkImage.UnlockBits(wMBitmapData); myWatermarkImage.Dispose(); myWatermarkImage = null; wMBitmapData = null; wMScan0 = (System.IntPtr)null; wMStride = 0; #endregion } catch (Exception exc) { #region Cleanup if (wMBitmapData != null) myWatermarkImage.UnlockBits(wMBitmapData); wMBitmapData = null; if (myWatermarkImage != null) myWatermarkImage.Dispose(); myWatermarkImage = null; #endregion System.Exception myExc = new System.Exception( "Exception caught in Read method of Watermark Function. " + exc.Message, exc); throw myExc; } } /// <summary> /// Update the Raster Function /// </summary> public void Update() { try { } catch (Exception exc) { System.Exception myExc = new System.Exception( "Exception caught in Update method of Watermark Function", exc); throw myExc; } } /// <summary> /// Property that specifies whether the Raster Function is still valid. /// </summary> public bool Valid { get { return myValidFlag; } } #endregion #region IPersistVariant Members /// <summary> /// UID to identify the function. /// </summary> public UID ID { get { return myUID; } } /// <summary> /// Load the properties of the function from the stream provided /// </summary> /// <param name="Stream">Stream that contains the serialized form of the function</param> public void Load(IVariantStream Stream) { if (Stream is IDocumentVersion) { IDocumentVersion docVersion = (IDocumentVersion)Stream; if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10) { esriArcGISVersion streamVersion = (esriArcGISVersion)((int)Stream.Read()); if (streamVersion >= esriArcGISVersion.esriArcGISVersion10) { myName = (string)Stream.Read(); myDescription = (string)Stream.Read(); myPixeltype = (rstPixelType)((int)Stream.Read()); } } } } /// <summary> /// Save the properties of the function to the stream provided /// </summary> /// <param name="Stream">Stream to which to serialize the function into</param> public void Save(IVariantStream Stream) { if (Stream is IDocumentVersion) { IDocumentVersion docVersion = (IDocumentVersion)Stream; if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10) { Stream.Write((int)esriArcGISVersion.esriArcGISVersion10); Stream.Write(myName); Stream.Write(myDescription); Stream.Write((int)myPixeltype); } } } #endregion #region IDocumentVersionSupportGEN Members /// <summary> /// Convert the instance into an object supported by the given version /// </summary> /// <param name="docVersion">Version to convert to</param> /// <returns>Object that supports given version</returns> public object ConvertToSupportedObject(esriArcGISVersion docVersion) { return null; } /// <summary> /// Check if the object is supported at the given version /// </summary> /// <param name="docVersion">Version to check against</param> /// <returns>True if the object is supported</returns> public bool IsSupportedAtVersion(esriArcGISVersion docVersion) { if (docVersion >= esriArcGISVersion.esriArcGISVersion10) return true; else return false; } #endregion #region IXMLSerialize Members /// <summary> /// Deserialize the Raster Function from the datastream provided /// </summary> /// <param name="data">Xml stream to deserialize the function from</param> public void Deserialize(IXMLSerializeData data) { myName = data.GetString(data.Find("Name")); myDescription = data.GetString(data.Find("Description")); myPixeltype = (rstPixelType)(data.GetInteger(data.Find("PixelType"))); } /// <summary> /// Serialize the Raster Function into the stream provided. /// </summary> /// <param name="data">Xml stream to serialize the function into</param> public void Serialize(IXMLSerializeData data) { data.TypeName = "WatermarkFunction"; data.AddString("Name", myName); data.AddString("Description", myDescription); data.AddInteger("PixelType", (int)myPixeltype); } #endregion #region IXMLVersionSupport Members /// <summary> /// Returns the namespaces supported by the object /// </summary> public string MinNamespaceSupported { get { return @"http://www.esri.com/schemas/ArcGIS/10.0"; } } #endregion #region COM Registration Function(s) [ComRegisterFunction()] static void Reg(string regKey) { ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Register(regKey); } [ComUnregisterFunction()] static void Unreg(string regKey) { ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Unregister(regKey); } #endregion } [Guid("933A9DEF-D56F-4e37-911D-AC16982CA697")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IWatermarkFunctionArguments : IRasterFunctionArguments, IPersistVariant, IXMLSerialize { #region WatermarkFunctionArguments Members [DispId(0x50505001)] object Raster { get; set; } [DispId(0x50505002)] string WatermarkImagePath { get; set; } [DispId(0x50505003)] double BlendPercentage { get; set; } #endregion } [Guid("996DC8E5-086B-41b5-919A-A3B9B86F2B30")] [ClassInterface(ClassInterfaceType.None)] [ProgId("CustomFunction.WatermarkFunctionArguments")] [ComVisible(true)] public class WatermarkFunctionArguments : IWatermarkFunctionArguments, IPersistVariant, IDocumentVersionSupportGEN, IXMLSerialize, IXMLVersionSupport { #region Private Members string myName; string myDescription; UID myUID; // UID for the Watermark Function. IPropertySet myProperties; // Property set to store the properties of the arguments object. #endregion public WatermarkFunctionArguments() { myName = "WatermarkFunctionArguments"; myDescription = "Arguments object for the Watermark Function"; // Set default values myProperties = new PropertySetClass(); myProperties.SetProperty("Raster", null); myProperties.SetProperty("WatermarkImagePath", ""); myProperties.SetProperty("BlendPercentage", 50.00); myUID = new UIDClass(); myUID.Value = "{" + "996DC8E5-086B-41b5-919A-A3B9B86F2B30" + "}"; } #region WatermarkFunctionArguments Members /// <summary> /// Raster to apply the raster function to. /// </summary> public object Raster { get { return GetDereferencedValue("Raster"); } set { myProperties.SetProperty("Raster", value); } } /// <summary> /// Path to the image to blend into the raster. /// </summary> public string WatermarkImagePath { get { return (string)GetDereferencedValue("WatermarkImagePath"); } set { myProperties.SetProperty("WatermarkImagePath", value); } } /// <summary> /// Percentage value by which to blend the watermark image with the raster /// </summary> public double BlendPercentage { get { return (double)GetDereferencedValue("BlendPercentage"); } set { myProperties.SetProperty("BlendPercentage", value); } } /// <summary> /// Dereference and return the value of the property whose name is given if necessary. /// </summary> /// <param name="name">Name of the property to check.</param> /// <returns>Dereferenced value of the property corresponding to the name given.</returns> public object GetDereferencedValue(string name) { object value = myProperties.GetProperty(name); if (value != null && value is IRasterFunctionVariable && !(value is IRasterFunctionTemplate)) { IRasterFunctionVariable rFVar = (IRasterFunctionVariable)value; return rFVar.Value; } return value; } #endregion #region IRasterFunctionArguments Members /// <summary> /// A list of files associated with the raster /// </summary> public IStringArray FileList { get { object rasterObject = myProperties.GetProperty("Raster"); IRasterDataset rasterDataset = null; if (rasterObject is IRasterDataset) rasterDataset = (IRasterDataset)rasterObject; else if (rasterObject is IName) rasterDataset = (IRasterDataset)((IName)rasterObject).Open(); if (rasterDataset != null && rasterDataset is IRasterDatasetInfo) { IRasterDatasetInfo rasterDatasetInfo = (IRasterDatasetInfo)rasterDataset; return rasterDatasetInfo.FileList; } else return null; } } /// <summary> /// Get the value associated with the name provided. /// </summary> /// <param name="Name">Name of the property</param> /// <returns>Value of the property name provided</returns> public object GetValue(string Name) { return myProperties.GetProperty(Name); } /// <summary> /// A list of all the names in the property set. /// </summary> public IStringArray Names { get // Generate a list of names in the propertyset. { object names = null, values = null; myProperties.GetAllProperties(out names, out values); IStringArray myNames = new StrArray(); string[] nameArray = (string[])names; for (int i = 0; i < nameArray.GetLength(0);++i) myNames.Add(nameArray[i]); return myNames; } } /// <summary> /// Set the given property name to the given value /// </summary> /// <param name="Name">Name of the property</param> /// <param name="Value">Value of the property</param> public void PutValue(string Name, object Value) { myProperties.SetProperty(Name, Value); } /// <summary> /// Remove the value of the property name provided /// </summary> /// <param name="Name">Name of the property to be removed</param> public void Remove(string Name) { myProperties.RemoveProperty(Name); } /// <summary> /// Clear the property set of all names and values. /// </summary> public void RemoveAll() { myProperties = null; myProperties = new PropertySetClass(); } /// <summary> /// A list of all the values in the property set /// </summary> public IVariantArray Values { get // Generate a list of values in the propertyset. { object names = null, values = null; myProperties.GetAllProperties(out names, out values); IVariantArray myValues = new VarArray(); object[] valArray = (object[])values; for (int i = 0; i < valArray.GetLength(0); ++i) myValues.Add(valArray[i]); return myValues; } } /// <summary> /// Resolve variables containing field names with the corresponding values. /// </summary> /// <param name="pRow">The row corresponding to the function raster dataset.</param> /// <param name="pPropertySet">Property Set to add the list of the names and the resolved values to.</param> public void Resolve(IRow pRow, IPropertySet pPropertySet) { try { ResolveRasterVal(pRow); ResolveBlendPVal(pRow); ResolveWatermarkPathVal(pRow); } catch (Exception exc) { System.Exception myExc = new System.Exception( "Exception caught in Resolve: " + exc.Message, exc); throw myExc; } } /// <summary> /// Update the variables containing field names to their updated values. /// </summary> /// <param name="pRow">The row corresponding to the function raster dataset.</param> /// <param name="pPropertySet">Property Set to add the list of the names and the updated values to.</param> /// <param name="pTemplateArguments">The arguments object containing the properties to update if</param> public void Update(IRow pRow, IPropertySet pPropertySet, IRasterFunctionArguments pTemplateArguments) { Resolve(pRow, pPropertySet); } /// <summary> /// Resolve the 'Raster' variable if it contains field names with the corresponding values. /// </summary> /// <param name="pRow">The row corresponding to the function raster dataset.</param> private void ResolveRasterVal(IRow pRow) { try { // Get the Raster property. object myRasterObject = myProperties.GetProperty("Raster"); // Check to see if it is a variable if (myRasterObject is IRasterFunctionVariable) { IRasterFunctionVariable rasterVar = ((IRasterFunctionVariable)myRasterObject); object rasterVal = FindPropertyInRow(rasterVar, pRow); if (rasterVal != null && rasterVal is string) { // Open the Raster Dataset from the path provided. string datasetPath = (string)rasterVal; IRasterDataset rasterDataset = null; rasterVar.Value = rasterDataset; } } } catch (Exception exc) { System.Exception myExc = new System.Exception( "Exception caught in ResolveRasterVal: " + exc.Message, exc); throw myExc; } } /// <summary> /// Open the Raster Dataset given the path to the file. /// </summary> /// <param name="path">Path to the Raster Dataset file.</param> /// <returns>The opened Raster Dataset.</returns> private IRasterDataset OpenRasterDataset(string path) { try { string inputWorkspace = System.IO.Path.GetDirectoryName(path); string inputDatasetName = System.IO.Path.GetFileName(path); Type factoryType = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory"); IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType); IWorkspace workspace = workspaceFactory.OpenFromFile(inputWorkspace, 0); IRasterWorkspace rasterWorkspace = (IRasterWorkspace)workspace; IRasterDataset myRasterDataset = rasterWorkspace.OpenRasterDataset(inputDatasetName); return myRasterDataset; } catch (Exception exc) { throw exc; } } /// <summary> /// Resolve the 'BlendPercentage' variable if it contains field names with the corresponding values. /// </summary> /// <param name="pRow">The row corresponding to the function raster dataset.</param> private void ResolveBlendPVal(IRow pRow) { // Get the BlendPercentage property. object myRasterObject = myProperties.GetProperty("BlendPercentage"); // Check to see if it is a variable if (myRasterObject is IRasterFunctionVariable) { IRasterFunctionVariable bpVar = ((IRasterFunctionVariable)myRasterObject); object rasterVal = FindPropertyInRow(bpVar, pRow); if (rasterVal != null && rasterVal is string) { // Get the blend percentage value from string try { bpVar.Value = Convert.ToDouble((string)rasterVal); } catch (Exception) { } } } } /// <summary> /// Resolve the 'WatermarkImagePath' variable if it contains field names with the corresponding values. /// </summary> /// <param name="pRow">The row corresponding to the function raster dataset.</param> private void ResolveWatermarkPathVal(IRow pRow) { // Get the WatermarkImagePath property. object myRasterObject = myProperties.GetProperty("WatermarkImagePath"); // Check to see if it is a variable if (myRasterObject is IRasterFunctionVariable) { IRasterFunctionVariable wipVar = ((IRasterFunctionVariable)myRasterObject); object rasterVal = FindPropertyInRow(wipVar, pRow); if (rasterVal != null && rasterVal is string) { // Get the blend percentage value from string wipVar.Value = (string)rasterVal; } } } /// <summary> /// Check the Name and Alias properties of the given Raster Function Variable to see /// if they contain a reference to a field and get the value of the corresponding field if needed. /// </summary> /// <param name="rasterFunctionVar">The Raster Function Variable to check.</param> /// <param name="pRow">The row corresponding to the function raster dataset.</param> /// <returns></returns> private object FindPropertyInRow(IRasterFunctionVariable rasterFunctionVar, IRow pRow) { string varName = ""; IStringArray varNames = new StrArrayClass(); varName = rasterFunctionVar.Name; // If the name of the variable contains '@Field' if (varName.Contains("@Field.")) varNames.Add(varName); // Add it to the list of names. // Check the aliases of the variable for (int i = 0; i < rasterFunctionVar.Aliases.Count; ++i) { // Check the list of aliases for the '@Field' string varName = rasterFunctionVar.Aliases.get_Element(i); if (varName.Contains("@Field.")) varNames.Add(varName); // and add any that are found to the list of names. } // Use the list of names and find the value by looking up the appropriate field. for (int i = 0; i < varNames.Count; ++i) { // Get the variable name containing the field string varName = varNames.get_Element(i); // Replace the '@Field' with nothing to get just the name of the field. string fieldName = varName.Replace("@Field.", ""); IFields rowFields = pRow.Fields; // Look up the index of the field name in the row. int fieldIndex = rowFields.FindField(fieldName); // If it is a valid index and the field type is string, return the value. if (fieldIndex != -1 && ((rowFields.get_Field(fieldIndex)).Type == esriFieldType.esriFieldTypeString)) return pRow.get_Value(fieldIndex); } // If no value has been returned yet, return null. return null; } #endregion #region IPersistVariant Members /// <summary> /// UID to identify the object. /// </summary> public UID ID { get { return myUID; } } /// <summary> /// Load the properties of the argument object from the stream provided /// </summary> /// <param name="Stream">Stream that contains the serialized form of the argument object</param> public void Load(IVariantStream Stream) { if (Stream is IDocumentVersion) { IDocumentVersion docVersion = (IDocumentVersion)Stream; if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10) { esriArcGISVersion streamVersion = (esriArcGISVersion)((int)Stream.Read()); if (streamVersion >= esriArcGISVersion.esriArcGISVersion10) { myName = (string)Stream.Read(); myDescription = (string)Stream.Read(); myProperties = (IPropertySet)Stream.Read(); } } } } /// <summary> /// Save the properties of the argument object to the stream provided /// </summary> /// <param name="Stream">Stream to which to serialize the argument object into</param> public void Save(IVariantStream Stream) { if (Stream is IDocumentVersion) { IDocumentVersion docVersion = (IDocumentVersion)Stream; if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10) { object names = null, values = null; myProperties.GetAllProperties(out names, out values); string[] nameArray = (string[])names; object[] valArray = (object[])values; for (int i = 0; i < nameArray.GetLength(0); ++i) { if (valArray[i] is IDataset) { IName myDatasetName = ((IDataset)valArray[i]).FullName; myProperties.SetProperty(nameArray[i], myDatasetName); } } Stream.Write((int)esriArcGISVersion.esriArcGISVersion10); Stream.Write(myName); Stream.Write(myDescription); Stream.Write(myProperties); } } } #endregion #region IDocumentVersionSupportGEN Members /// <summary> /// Convert the instance into an object supported by the given version /// </summary> /// <param name="docVersion">Version to convert to</param> /// <returns>Object that supports given version</returns> public object ConvertToSupportedObject(esriArcGISVersion docVersion) { return null; } /// <summary> /// Check if the object is supported at the given version /// </summary> /// <param name="docVersion">Version to check against</param> /// <returns>True if the object is supported</returns> public bool IsSupportedAtVersion(esriArcGISVersion docVersion) { if (docVersion >= esriArcGISVersion.esriArcGISVersion10) return true; else return false; } #endregion #region IXMLSerialize Members /// <summary> /// Deserialize the argument object from the stream provided /// </summary> /// <param name="data">Xml stream to deserialize the argument object from</param> public void Deserialize(IXMLSerializeData data) { int nameIndex = data.Find("Names"); int valIndex = data.Find("Values"); if (nameIndex != -1 && valIndex != -1) { IStringArray myNames = (IStringArray)data.GetVariant(nameIndex); IVariantArray myValues = (IVariantArray)data.GetVariant(valIndex); for (int i = 0; i < myNames.Count; ++i) { myProperties.SetProperty(myNames.get_Element(i), myValues.get_Element(i)); } } } /// <summary> /// Serialize the argument object into the stream provided. /// </summary> /// <param name="data">Xml stream to serialize the argument object into</param> public void Serialize(IXMLSerializeData data) { object names = null, values = null; myProperties.GetAllProperties(out names, out values); IStringArray myNames = new StrArray(); string[] nameArray = (string[])names; IVariantArray myValues = new VarArray(); object[] valArray = (object[])values; for (int i = 0; i < nameArray.GetLength(0); ++i) { myNames.Add(nameArray[i]); if (valArray[i] is IDataset) { IName myDatasetName = ((IDataset)valArray[i]).FullName; myValues.Add(myDatasetName); } else myValues.Add(valArray[i]); } data.TypeName = "WatermarkFunctionArguments"; data.AddObject("Names", myNames); data.AddObject("Values", myValues); } #endregion #region IXMLVersionSupport Members /// <summary> /// Returns the namespaces supported by the object /// </summary> public string MinNamespaceSupported { get { return @"http://www.esri.com/schemas/ArcGIS/10.0"; } } #endregion } }
[Visual Basic .NET]
WatermarkFunction.vb
Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Drawing Imports System.Drawing.Imaging Imports System.Runtime.InteropServices Imports ESRI.ArcGIS.DataSourcesGDB Imports ESRI.ArcGIS.DataSourcesRaster Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.Geometry Imports ESRI.ArcGIS.ADF.CATIDs Namespace CustomFunction <Guid("637D3707-16B6-48d9-A970-7BC2CD99FB44")> _ <ClassInterface(ClassInterfaceType.None)> _ <ProgId("CustomFunction.WatermarkFunction")> _ <ComVisible(True)> _ Public Class WatermarkFunction Implements IRasterFunction Implements IPersistVariant Implements IDocumentVersionSupportGEN Implements IXMLSerialize Implements IXMLVersionSupport #Region "Private Members" Private myUID As UID ' UID for the Watermark Function. Private myRasterInfo As IRasterInfo ' Raster Info for the Watermark Function Private myPixeltype As rstPixelType ' Pixel Type of the Watermark Function. Private myName As String ' Name of the Watermark Function. Private myDescription As String ' Description of the Watermark Function. Private myValidFlag As Boolean ' Flag to specify validity of the Watermark Function. Private myWatermarkImagePath As String ' Path to the Watermark Image. Private myBlendPercentage As Double ' Percentage of the blending. Private blendValue As Double ' Actual value of the blend percentage. Private myFunctionHelper As IRasterFunctionHelper ' Raster Function Helper object. Private myWatermarkImage As Bitmap ' Watermark Image object. #End Region Public Sub New() myWatermarkImagePath = "" myBlendPercentage = 50.0 ' Default value for the blending percentage. blendValue = 0.5 ' Default value for the blend value. myName = "WatermarkFunction" myPixeltype = rstPixelType.PT_UCHAR myDescription = "Add a watermark to the request." myValidFlag = True myFunctionHelper = New RasterFunctionHelperClass() myWatermarkImage = Nothing myUID = New UIDClass() myUID.Value = "{" & "637D3707-16B6-48d9-A970-7BC2CD99FB44" & "}" End Sub #Region "IRasterFunction Members" ''' <summary> ''' Name of the Raster Function. ''' </summary> Public Property Name() As String Implements IRasterFunction.Name Get Return myName End Get Set(ByVal value As String) myName = value End Set End Property ''' <summary> ''' Pixel Type of the Raster Function ''' </summary> Public Property PixelType() As rstPixelType Implements IRasterFunction.PixelType Get Return myPixeltype End Get Set(ByVal value As rstPixelType) myPixeltype = value End Set End Property ''' <summary> ''' Output Raster Info for the Raster Function ''' </summary> Public ReadOnly Property RasterInfo() As IRasterInfo Implements IRasterFunction.RasterInfo Get Return myRasterInfo End Get End Property ''' <summary> ''' Description of the Raster Function ''' </summary> Public Property Description() As String Implements IRasterFunction.Description Get Return myDescription End Get Set(ByVal value As String) myDescription = value End Set End Property ''' <summary> ''' Initialize the Raster function using the argument object. This is one of the two ''' main functions to implement for a custom Raster function. The raster object is ''' dereferenced if required and given to the RasterFuntionHelper object to bind. ''' </summary> ''' <param name="pArguments">Arguments object used for initialization</param> Public Sub Bind(ByVal pArguments As Object) Implements IRasterFunction.Bind Try ' Check if the Arguments object is of the correct type. Dim watermarkFuncArgs As IWatermarkFunctionArguments = Nothing If TypeOf pArguments Is IWatermarkFunctionArguments Then watermarkFuncArgs = DirectCast(pArguments, IWatermarkFunctionArguments) myBlendPercentage = watermarkFuncArgs.BlendPercentage myWatermarkImagePath = watermarkFuncArgs.WatermarkImagePath Dim inputRaster As Object = watermarkFuncArgs.Raster If TypeOf watermarkFuncArgs.Raster Is IRasterFunctionVariable Then Dim rasterFunctionVariable As IRasterFunctionVariable = _ DirectCast(watermarkFuncArgs.Raster, IRasterFunctionVariable) inputRaster = rasterFunctionVariable.Value End If ' Call the Bind method of the Raster Function Helper object. myFunctionHelper.Bind(inputRaster) Else ' Throw an error if incorrect arguments object is passed. Throw New System.Exception("Incorrect arguments object. Expected: IWatermarkFunctionArguments") End If ' Get the raster info and Pixel Type from the RasterFunctionHelper object. myRasterInfo = myFunctionHelper.RasterInfo myPixeltype = myRasterInfo.PixelType ' Convert blending percentage to blending value. If myBlendPercentage >= 0.0 AndAlso myBlendPercentage <= 100.0 Then blendValue = myBlendPercentage / 100.0 Else ''' A value of 50% is used as default. blendValue = 0.5 End If If myWatermarkImagePath <> "" Then ' Load the watermark image from the path provided myWatermarkImage = New Bitmap(myWatermarkImagePath) ' and check the pixel type of the loaded image to see if its compatible. If myWatermarkImage.PixelFormat <> System.Drawing.Imaging.PixelFormat.Format32bppArgb AndAlso myWatermarkImage.PixelFormat <> System.Drawing.Imaging.PixelFormat.Format24bppRgb Then ' Throw error if the image is not compatible. Throw New System.Exception("Invalid watermark image. Please provide one with 8 bits per band in ARGB or RGB format.") End If ' Cleanup myWatermarkImage.Dispose() myWatermarkImage = Nothing End If Catch exc As Exception '#Region "Cleanup" If myWatermarkImage IsNot Nothing Then myWatermarkImage.Dispose() End If myWatermarkImage = Nothing '#End Region Dim myExc As New System.Exception("Exception caught in Bind method of Watermark Function. " & exc.Message, exc) Throw myExc End Try End Sub ''' <summary> ''' Read pixels from the input Raster and fill the PixelBlock provided with processed pixels. ''' The RasterFunctionHelper object is used to handle pixel type conversion and resampling. ''' The watermark image is then blended to the bottom right corner of the pixel block. ''' </summary> ''' <param name="pTlc">Point to start the reading from in the Raster</param> ''' <param name="pRaster">Reference Raster for the PixelBlock</param> ''' <param name="pPixelBlock">PixelBlock to be filled in</param> Public Sub Read(ByVal pTlc As IPnt, ByVal pRaster As IRaster, ByVal pPixelBlock As IPixelBlock) Implements IRasterFunction.Read Dim wMBitmapData As BitmapData = Nothing Dim pixelValue As Double = 0.0 Dim wmRow As Integer = 0 Dim wmCol As Integer = 0 Try ' Call Read method of the Raster Function Helper object. myFunctionHelper.Read(pTlc, Nothing, pRaster, pPixelBlock) Dim wMBandOffset As Integer = 0 '#Region "Reference Raster Properties" ' Get the pixel type of the reference raster to see if ' it is compatible (8 bit). Dim referenceProps As IRasterProps = DirectCast(pRaster, IRasterProps) If referenceProps.PixelType <> rstPixelType.PT_UCHAR AndAlso referenceProps.PixelType <> rstPixelType.PT_CHAR Then Throw New System.Exception("Function can only be applied to 8bit data.") End If '#End Region '#Region "Load watermark image object" ' Create new image object for the watermark image. myWatermarkImage = New Bitmap(myWatermarkImagePath) ' Read number of bands of the watermark image. If myWatermarkImage.PixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb Then wMBandOffset = 4 Else If myWatermarkImage.PixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb Then wMBandOffset = 3 Else Throw New System.Exception("Invalid bitmap. Please provide one with 8bits per band in ARGB or RGB format.") End If End If '#End Region Dim pBHeight As Integer = pPixelBlock.Height Dim pBWidth As Integer = pPixelBlock.Width Dim wMHeight As Integer = myWatermarkImage.Height Dim wMWidth As Integer = myWatermarkImage.Width Dim wMRowIndex As Integer = 0 Dim wMColIndex As Integer = 0 Dim pBRowIndex As Integer = 0 Dim pBColIndex As Integer = 0 Dim waterStartCol As Boolean = False Dim waterStartRow As Boolean = False ' Calculate the row/column values that specify where to start the blending. '#Region "Calculate Indices" ''' If the number of rows of the pixelblock are more than the watermark image If pBHeight >= wMHeight Then ''' Set the row index to start blending in the pixelblock. pBRowIndex = pBHeight - wMHeight Else ''' If the number of rows of the watermark image is more than that of the pixelblock. ''' Set the row index to start blending in the watermark image. wMRowIndex = (wMHeight - pBHeight) waterStartRow = True End If ''' If the number of cols of the pixelblock are more than the watermark image If pBWidth >= wMWidth Then ''' Set the col index to start blending in the pixelblock. pBColIndex = pBWidth - wMWidth Else ''' If the number of cols of the watermark image is more than that of the pixelblock. ''' Set the col index to start blending in the watermark image. wMColIndex = (wMWidth - pBWidth) waterStartCol = True End If '#End Region '#Region "Prepare Watermark Image for reading" ' Get the pixels from the watermark image using the lockbits function. wMBitmapData = myWatermarkImage.LockBits(New Rectangle(0, 0, wMWidth, wMHeight), _ ImageLockMode.[ReadOnly], myWatermarkImage.PixelFormat) Dim wMScan0 As System.IntPtr = wMBitmapData.Scan0 Dim wMStride As Integer = wMBitmapData.Stride '#End Region Dim wMBufferSize As Integer = wMStride * wMHeight Dim wMLocalBuffer(wMBufferSize) As Byte Call Marshal.Copy(wMScan0, wMLocalBuffer, 0, wMBufferSize) Dim wMLocalBufferIndex As Integer = 0 Dim wMPaddingOffset As Integer = wMStride - (myWatermarkImage.Width * wMBandOffset) ' Start filling from the correct row, col in the pixelblock ' using the indices calculated above Dim pixelValues As System.Array If pPixelBlock.Planes = 3 Then '#Region "3 Band PixelBlock" For nBand As Integer = 0 To pPixelBlock.Planes - 1 'Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte)) wMLocalBufferIndex = 0 ' If the number of rows of the watermark image are more than the request. If waterStartRow Then 'Skip to the correct row in the watermark image. wMLocalBufferIndex += (wMStride * wMRowIndex) End If Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3) pixelValues = DirectCast((ipPixelBlock.PixelData(nBand)), System.Array) Dim i As Integer = pBRowIndex While i < pBHeight ' If the number of cols of the watermark image are more than the request. If waterStartCol Then ' Skip to the correct column in the watermark image. wMLocalBufferIndex += (wMColIndex * wMBandOffset) End If Dim k As Integer = pBColIndex While k < pBWidth pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i)) If Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 3)) <> 0.0R _ AndAlso Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) = 1 Then ' Blend the pixelValue from the PixelBlock with the corresponding ' pixel from the watermark image. pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * _ Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + (2 - nBand)))) End If pixelValues.SetValue(Convert.ToByte(pixelValue), k, i) wMLocalBufferIndex += wMBandOffset k += 1 wmCol += 1 End While wMLocalBufferIndex += wMPaddingOffset i += 1 wmRow += 1 End While DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues '#End Region Next Else '#Region "Not 3 Band PixelBlock" For nBand As Integer = 0 To pPixelBlock.Planes - 1 'Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte)) wMLocalBufferIndex = 0 ' If the number of rows of the watermark image are more than the request. If waterStartRow Then ' Skip to the correct row in the watermark image. wMLocalBufferIndex += (wMStride * wMRowIndex) End If Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3) pixelValues = DirectCast((ipPixelBlock.PixelData(nBand)), System.Array) Dim i As Integer = pBRowIndex While i < pBHeight ' If the number of cols of the watermark image are more than the request. If waterStartCol Then ' Skip to the correct column in the watermark image. wMLocalBufferIndex += (wMColIndex * wMBandOffset) End If Dim k As Integer = pBColIndex While k < pBWidth pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i)) If Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 3)) <> 0.0R AndAlso Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, i, k)) = 1 Then ' Calculate the average value of the pixels of the watermark image Dim avgValue As Double = (Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 0)) + _ Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 1)) + _ Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 2))) / 3 ' and blend it with the pixelValue from the PixelBlock. pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * avgValue) End If pixelValues.SetValue(Convert.ToByte(pixelValue), k, i) wMLocalBufferIndex += wMBandOffset k += 1 wmCol += 1 End While wMLocalBufferIndex += wMPaddingOffset i += 1 wmRow += 1 End While DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues '#End Region Next End If '#Region "Cleanup" myWatermarkImage.UnlockBits(wMBitmapData) myWatermarkImage.Dispose() myWatermarkImage = Nothing wMBitmapData = Nothing wMScan0 = CType(Nothing, System.IntPtr) '#End Region wMStride = 0 Catch exc As Exception '#Region "Cleanup" If wMBitmapData IsNot Nothing Then myWatermarkImage.UnlockBits(wMBitmapData) End If wMBitmapData = Nothing If myWatermarkImage IsNot Nothing Then myWatermarkImage.Dispose() End If myWatermarkImage = Nothing '#End Region Dim myExc As New System.Exception("Exception caught in Read method of Watermark Function. " & exc.Message, exc) Throw myExc End Try End Sub ''' <summary> ''' Update the Raster Function ''' </summary> Public Sub Update() Implements IRasterFunction.Update Try Catch exc As Exception Dim myExc As New System.Exception("Exception caught in Update method of Watermark Function", exc) Throw myExc End Try End Sub ''' <summary> ''' Property that specifies whether the Raster Function is still valid. ''' </summary> Public ReadOnly Property Valid() As Boolean Implements IRasterFunction.Valid Get Return myValidFlag End Get End Property #End Region #Region "IPersistVariant Members" ''' <summary> ''' UID to identify the function. ''' </summary> Public ReadOnly Property ID() As UID Implements IPersistVariant.ID Get Return myUID End Get End Property ''' <summary> ''' Load the properties of the function from the stream provided ''' </summary> ''' <param name="Stream">Stream that contains the serialized form of the function</param> Public Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load If TypeOf Stream Is IDocumentVersion Then Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion) If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then Dim streamVersion As esriArcGISVersion = CType(CInt(Stream.Read()), esriArcGISVersion) If streamVersion >= esriArcGISVersion.esriArcGISVersion10 Then myName = DirectCast(Stream.Read(), String) myDescription = DirectCast(Stream.Read(), String) myPixeltype = CType(CInt(Stream.Read()), rstPixelType) End If End If End If End Sub ''' <summary> ''' Save the properties of the function to the stream provided ''' </summary> ''' <param name="Stream">Stream to which to serialize the function into</param> Public Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save If TypeOf Stream Is IDocumentVersion Then Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion) If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then Stream.Write(CInt(esriArcGISVersion.esriArcGISVersion10)) Stream.Write(myName) Stream.Write(myDescription) Stream.Write(CInt(myPixeltype)) End If End If End Sub #End Region #Region "IDocumentVersionSupportGEN Members" ''' <summary> ''' Convert the instance into an object supported by the given version ''' </summary> ''' <param name="docVersion">Version to convert to</param> ''' <returns>Object that supports given version</returns> Public Function ConvertToSupportedObject(ByVal docVersion As esriArcGISVersion) As Object Implements IDocumentVersionSupportGEN.ConvertToSupportedObject Return Nothing End Function ''' <summary> ''' Check if the object is supported at the given version ''' </summary> ''' <param name="docVersion">Version to check against</param> ''' <returns>True if the object is supported</returns> Public Function IsSupportedAtVersion(ByVal docVersion As esriArcGISVersion) As Boolean Implements IDocumentVersionSupportGEN.IsSupportedAtVersion If docVersion >= esriArcGISVersion.esriArcGISVersion10 Then Return True Else Return False End If End Function #End Region #Region "IXMLSerialize Members" ''' <summary> ''' Deserialize the Raster Function from the datastream provided ''' </summary> ''' <param name="data">Xml stream to deserialize the function from</param> Public Sub Deserialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Deserialize myName = data.GetString(data.Find("Name")) myDescription = data.GetString(data.Find("Description")) myPixeltype = CType(data.GetInteger(data.Find("PixelType")), rstPixelType) End Sub ''' <summary> ''' Serialize the Raster Function into the stream provided. ''' </summary> ''' <param name="data">Xml stream to serialize the function into</param> Public Sub Serialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Serialize data.TypeName = "WatermarkFunction" data.AddString("Name", myName) data.AddString("Description", myDescription) data.AddInteger("PixelType", CInt(myPixeltype)) End Sub #End Region #Region "IXMLVersionSupport Members" ''' <summary> ''' Returns the namespaces supported by the object ''' </summary> Public ReadOnly Property MinNamespaceSupported() As String Implements IXMLVersionSupport.MinNamespaceSupported Get Return "http://www.esri.com/schemas/ArcGIS/10.0" End Get End Property #End Region #Region "COM Registration Function(s)" <ComRegisterFunction()> _ Private Shared Sub Reg(ByVal regKey As String) ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Register(regKey) End Sub <ComUnregisterFunction()> _ Private Shared Sub Unreg(ByVal regKey As String) ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Unregister(regKey) End Sub #End Region End Class <Guid("3D6CF7CC-A7CE-452c-90E1-43558CC6A006")> _ <InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _ Public Interface IWatermarkFunctionArguments Inherits IRasterFunctionArguments Inherits IPersistVariant Inherits IXMLSerialize #Region "WatermarkFunctionArguments Members" <DispId(&H50505001)> _ Property Raster() As Object <DispId(&H50505002)> _ Property WatermarkImagePath() As String <DispId(&H50505003)> _ Property BlendPercentage() As Double #End Region End Interface <Guid("DD84BE72-E4B8-4355-B630-83EFF930C024")> _ <ClassInterface(ClassInterfaceType.None)> _ <ProgId("CustomFunction.WatermarkFunctionArguments")> _ <ComVisible(True)> _ Public Class WatermarkFunctionArguments Implements IWatermarkFunctionArguments Implements IPersistVariant Implements IDocumentVersionSupportGEN Implements IXMLSerialize Implements IXMLVersionSupport #Region "Private Members" Private myName As String Private myDescription As String Private myUID As UID ' UID for the Watermark Function. Private myProperties As IPropertySet ' Property set to store the properties of the arguments object. #End Region Public Sub New() myName = "WatermarkFunctionArguments" myDescription = "Arguments object for the Watermark Function" ' Set default values myProperties = New PropertySetClass() myProperties.SetProperty("Raster", Nothing) myProperties.SetProperty("WatermarkImagePath", "") myProperties.SetProperty("BlendPercentage", 50.0) myUID = New UIDClass() myUID.Value = "{" & "DD84BE72-E4B8-4355-B630-83EFF930C024" & "}" End Sub #Region "WatermarkFunctionArguments Members" ''' <summary> ''' Raster to apply the raster function to. ''' </summary> Public Property Raster() As Object Implements IWatermarkFunctionArguments.Raster Get Return GetDereferencedValue("Raster") End Get Set(ByVal value As Object) myProperties.SetProperty("Raster", value) End Set End Property ''' <summary> ''' Path to the image to blend into the raster. ''' </summary> Public Property WatermarkImagePath() As String Implements IWatermarkFunctionArguments.WatermarkImagePath Get Return DirectCast(GetDereferencedValue("WatermarkImagePath"), String) End Get Set(ByVal value As String) myProperties.SetProperty("WatermarkImagePath", value) End Set End Property ''' <summary> ''' Percentage value by which to blend the watermark image with the raster ''' </summary> Public Property BlendPercentage() As Double Implements IWatermarkFunctionArguments.BlendPercentage Get Return CDbl(GetDereferencedValue("BlendPercentage")) End Get Set(ByVal value As Double) myProperties.SetProperty("BlendPercentage", value) End Set End Property Public Function GetDereferencedValue(ByVal name As String) As Object Dim value As Object = myProperties.GetProperty(name) If TypeOf value Is IRasterFunctionVariable AndAlso Not (TypeOf value Is IRasterFunctionTemplate) Then Dim rFVar As IRasterFunctionVariable = DirectCast(value, IRasterFunctionVariable) Return rFVar.Value End If Return value End Function #End Region #Region "IRasterFunctionArguments Members" ''' <summary> ''' A list of files associated with the raster ''' </summary> Public ReadOnly Property FileList() As IStringArray Implements IRasterFunctionArguments.FileList Get Dim rasterObject As Object = myProperties.GetProperty("Raster") Dim rasterDataset As IRasterDataset = Nothing If TypeOf rasterObject Is IRasterDataset Then rasterDataset = DirectCast(rasterObject, IRasterDataset) ElseIf TypeOf rasterObject Is IName Then rasterDataset = DirectCast(DirectCast(rasterObject, IName).Open(), IRasterDataset) End If If rasterDataset IsNot Nothing Then Dim rasterDatasetInfo As IRasterDatasetInfo = DirectCast(rasterDataset, IRasterDatasetInfo) Return rasterDatasetInfo.FileList Else Return Nothing End If End Get End Property ''' <summary> ''' Get the value associated with the name provided. ''' </summary> ''' <param name="Name">Name of the property</param> ''' <returns>Value of the property name provided</returns> Public Function GetValue(ByVal Name As String) As Object Implements IRasterFunctionArguments.GetValue Return myProperties.GetProperty(Name) End Function ''' <summary> ''' A list of all the names in the property set. ''' </summary> Public ReadOnly Property Names() As IStringArray Implements IRasterFunctionArguments.Names Get ' Generate a list of names in the propertyset. Dim propNames As Object = Nothing, values As Object = Nothing myProperties.GetAllProperties(propNames, values) Dim myNames As IStringArray = New StrArray() Dim nameArray As String() = DirectCast(propNames, String()) For i As Integer = 0 To nameArray.GetLength(0) - 1 myNames.Add(nameArray(i)) Next Return myNames End Get End Property ''' <summary> ''' Set the given property name to the given value ''' </summary> ''' <param name="Name">Name of the property</param> ''' <param name="Value">Value of the property</param> Public Sub PutValue(ByVal Name As String, ByVal Value As Object) Implements IRasterFunctionArguments.PutValue myProperties.SetProperty(Name, Value) End Sub ''' <summary> ''' Remove the value of the property name provided ''' </summary> ''' <param name="Name">Name of the property to be removed</param> Public Sub Remove(ByVal Name As String) Implements IRasterFunctionArguments.Remove myProperties.RemoveProperty(Name) End Sub ''' <summary> ''' Clear the property set of all names and values. ''' </summary> Public Sub RemoveAll() Implements IRasterFunctionArguments.RemoveAll myProperties = Nothing myProperties = New PropertySetClass() End Sub ''' <summary> ''' A list of all the values in the property set ''' </summary> Public ReadOnly Property Values() As IVariantArray Implements IRasterFunctionArguments.Values Get ' Generate a list of values in the propertyset. Dim names As Object = Nothing, propValues As Object = Nothing myProperties.GetAllProperties(names, propValues) Dim myValues As IVariantArray = New VarArray() Dim valArray As Object() = DirectCast(propValues, Object()) For i As Integer = 0 To valArray.GetLength(0) - 1 myValues.Add(valArray(i)) Next Return myValues End Get End Property ''' <summary> ''' Resolve variables containing field names with the corresponding values. ''' </summary> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> ''' <param name="pPropertySet">Property Set to add the list of the names and the resolved values to.</param> Public Sub Resolve(pRow As IRow, pPropertySet As IPropertySet) Implements IRasterFunctionArguments.Resolve Try ResolveRasterVal(pRow) ResolveBlendPVal(pRow) ResolveWatermarkPathVal(pRow) Catch exc As Exception Dim myExc As New System.Exception("Exception caught in Resolve: " & exc.Message, exc) Throw myExc End Try End Sub ''' <summary> ''' Update the variables containing field names to their updated values. ''' </summary> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> ''' <param name="pPropertySet">Property Set to add the list of the names and the updated values to.</param> ''' <param name="pTemplateArguments">The arguments object containing the properties to update if</param> Public Sub Update(pRow As IRow, pPropertySet As IPropertySet, pTemplateArguments As IRasterFunctionArguments) Implements IRasterFunctionArguments.Update Resolve(pRow, pPropertySet) End Sub ''' <summary> ''' Resolve the 'Raster' variable if it contains field names with the corresponding values. ''' </summary> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> Private Sub ResolveRasterVal(ByVal pRow As IRow) Try ' Get the Raster property. Dim myRasterObject As Object = myProperties.GetProperty("Raster") ' Check to see if it is a variable If TypeOf myRasterObject Is IRasterFunctionVariable Then Dim rasterVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable) Dim rasterVal As Object = FindPropertyInRow(rasterVar, pRow) If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then ' Open the Raster Dataset from the path provided. Dim datasetPath As String = DirectCast(rasterVal, String) Dim rasterDataset As IRasterDataset = Nothing rasterVar.Value = rasterDataset End If End If Catch exc As Exception Dim myExc As New System.Exception("Exception caught in ResolveRasterVal: " & exc.Message, exc) Throw myExc End Try End Sub ''' <summary> ''' Open the Raster Dataset given the path to the file. ''' </summary> ''' <param name="path">Path to the Raster Dataset file.</param> ''' <returns>The opened Raster Dataset.</returns> Private Function OpenRasterDataset(path As String) As IRasterDataset Try Dim inputWorkspace As String = System.IO.Path.GetDirectoryName(path) Dim inputDatasetName As String = System.IO.Path.GetFileName(path) Dim factoryType As Type = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory") Dim workspaceFactory As IWorkspaceFactory = DirectCast(Activator.CreateInstance(factoryType), IWorkspaceFactory) Dim workspace As IWorkspace = workspaceFactory.OpenFromFile(inputWorkspace, 0) Dim rasterWorkspace As IRasterWorkspace = DirectCast(workspace, IRasterWorkspace) Dim myRasterDataset As IRasterDataset = rasterWorkspace.OpenRasterDataset(inputDatasetName) Return myRasterDataset Catch exc As Exception Throw exc End Try End Function ''' <summary> ''' Resolve the 'BlendPercentage' variable if it contains field names with the corresponding values. ''' </summary> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> Private Sub ResolveBlendPVal(ByVal pRow As IRow) ' Get the BlendPercentage property. Dim myRasterObject As Object = myProperties.GetProperty("BlendPercentage") ' Check to see if it is a variable If TypeOf myRasterObject Is IRasterFunctionVariable Then Dim bpVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable) Dim rasterVal As Object = FindPropertyInRow(bpVar, pRow) If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then ' Get the blend percentage value from string Try bpVar.Value = Convert.ToDouble(DirectCast(rasterVal, String)) Catch generatedExceptionName As Exception End Try End If End If End Sub ''' <summary> ''' Resolve the 'WatermarkImagePath' variable if it contains field names with the corresponding values. ''' </summary> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> Private Sub ResolveWatermarkPathVal(ByVal pRow As IRow) ' Get the WatermarkImagePath property. Dim myRasterObject As Object = myProperties.GetProperty("WatermarkImagePath") ' Check to see if it is a variable If TypeOf myRasterObject Is IRasterFunctionVariable Then Dim wipVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable) Dim rasterVal As Object = FindPropertyInRow(wipVar, pRow) If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then ' Get the blend percentage value from string wipVar.Value = DirectCast(rasterVal, String) End If End If End Sub ''' <summary> ''' Check the Name and Alias properties of the given Raster Function Variable to see ''' if they contain a field name and get the value of the corresponding field if needed. ''' </summary> ''' <param name="rasterFunctionVar">The Raster Function Variable to check.</param> ''' <param name="pRow">The row corresponding to the function raster dataset.</param> ''' <returns></returns> Private Function FindPropertyInRow(ByVal rasterFunctionVar As IRasterFunctionVariable, ByVal pRow As IRow) As Object Dim varName As String = "" Dim varNames As IStringArray = New StrArrayClass() varName = rasterFunctionVar.Name ' If the name of the variable contains '@Field' If varName.Contains("@Field.") Then varNames.Add(varName) End If ' Add it to the list of names. ' Check the aliases of the variable For i As Integer = 0 To rasterFunctionVar.Aliases.Count - 1 ' Check the list of aliases for the '@Field' string varName = rasterFunctionVar.Aliases.Element(i) If varName.Contains("@Field.") Then varNames.Add(varName) ' and add any that are found to the list of names. End If Next ' Use the list of names and find the value by looking up the appropriate field. For i As Integer = 0 To varNames.Count - 1 ' Get the variable name containing the field string varName = varNames.Element(i) ' Replace the '@Field' with nothing to get just the name of the field. Dim fieldName As String = varName.Replace("@Field.", "") Dim rowFields As IFields = pRow.Fields ' Look up the index of the field name in the row. Dim fieldIndex As Integer = rowFields.FindField(fieldName) ' If it is a valid index and the field type is string, return the value. If fieldIndex <> -1 AndAlso ((rowFields.Field(fieldIndex)).Type = esriFieldType.esriFieldTypeString) Then Return pRow.Value(fieldIndex) End If Next ' If no value has been returned yet, return null. Return Nothing End Function #End Region #Region "IPersistVariant Members" ''' <summary> ''' UID to identify the object. ''' </summary> Public ReadOnly Property ID() As UID Implements IPersistVariant.ID Get Return myUID End Get End Property ''' <summary> ''' Load the properties of the argument object from the stream provided ''' </summary> ''' <param name="Stream">Stream that contains the serialized form of the argument object</param> Public Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load If TypeOf Stream Is IDocumentVersion Then Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion) If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then Dim streamVersion As esriArcGISVersion = CType(CInt(Stream.Read()), esriArcGISVersion) If streamVersion >= esriArcGISVersion.esriArcGISVersion10 Then myName = DirectCast(Stream.Read(), String) myDescription = DirectCast(Stream.Read(), String) myProperties = DirectCast(Stream.Read(), IPropertySet) End If End If End If End Sub ''' <summary> ''' Save the properties of the argument object to the stream provided ''' </summary> ''' <param name="Stream">Stream to which to serialize the argument object into</param> Public Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save If TypeOf Stream Is IDocumentVersion Then Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion) If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then Dim names As Object = Nothing, values As Object = Nothing myProperties.GetAllProperties(names, values) Dim nameArray As String() = DirectCast(names, String()) Dim valArray As Object() = DirectCast(values, Object()) For i As Integer = 0 To nameArray.GetLength(0) - 1 If TypeOf valArray(i) Is IDataset Then Dim myDatasetName As IName = DirectCast(valArray(i), IDataset).FullName myProperties.SetProperty(nameArray(i), myDatasetName) End If Next Stream.Write(CInt(esriArcGISVersion.esriArcGISVersion10)) Stream.Write(myName) Stream.Write(myDescription) Stream.Write(myProperties) End If End If End Sub #End Region #Region "IDocumentVersionSupportGEN Members" ''' <summary> ''' Convert the instance into an object supported by the given version ''' </summary> ''' <param name="docVersion">Version to convert to</param> ''' <returns>Object that supports given version</returns> Public Function ConvertToSupportedObject(ByVal docVersion As esriArcGISVersion) As Object Implements IDocumentVersionSupportGEN.ConvertToSupportedObject Return Nothing End Function ''' <summary> ''' Check if the object is supported at the given version ''' </summary> ''' <param name="docVersion">Version to check against</param> ''' <returns>True if the object is supported</returns> Public Function IsSupportedAtVersion(ByVal docVersion As esriArcGISVersion) As Boolean Implements IDocumentVersionSupportGEN.IsSupportedAtVersion If docVersion >= esriArcGISVersion.esriArcGISVersion10 Then Return True Else Return False End If End Function #End Region #Region "IXMLSerialize Members" ''' <summary> ''' Deserialize the argument object from the stream provided ''' </summary> ''' <param name="data">Xml stream to deserialize the argument object from</param> Public Sub Deserialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Deserialize Dim nameIndex As Integer = data.Find("Names") Dim valIndex As Integer = data.Find("Values") If nameIndex <> -1 AndAlso valIndex <> -1 Then Dim myNames As IStringArray = DirectCast(data.GetVariant(nameIndex), IStringArray) Dim myValues As IVariantArray = DirectCast(data.GetVariant(valIndex), IVariantArray) For i As Integer = 0 To myNames.Count - 1 myProperties.SetProperty(myNames.Element(i), myValues.Element(i)) Next End If End Sub ''' <summary> ''' Serialize the argument object into the stream provided. ''' </summary> ''' <param name="data">Xml stream to serialize the argument object into</param> Public Sub Serialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Serialize Dim names As Object = Nothing, values As Object = Nothing myProperties.GetAllProperties(names, values) Dim myNames As IStringArray = New StrArray() Dim nameArray As String() = DirectCast(names, String()) Dim myValues As IVariantArray = New VarArray() Dim valArray As Object() = DirectCast(values, Object()) For i As Integer = 0 To nameArray.GetLength(0) - 1 myNames.Add(nameArray(i)) If TypeOf valArray(i) Is IDataset Then Dim myDatasetName As IName = DirectCast(valArray(i), IDataset).FullName myValues.Add(myDatasetName) Else myValues.Add(valArray(i)) End If Next data.TypeName = "WatermarkFunctionArguments" data.AddObject("Names", myNames) data.AddObject("Values", myValues) End Sub #End Region #Region "IXMLVersionSupport Members" ''' <summary> ''' Returns the namespaces supported by the object ''' </summary> Public ReadOnly Property MinNamespaceSupported() As String Implements IXMLVersionSupport.MinNamespaceSupported Get Return "http://www.esri.com/schemas/ArcGIS/10.0" End Get End Property #End Region End Class End Namespace