MOLE layer type detection
MoleLayers.cpp
// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 


#include <stdio.h>
#include <QtGui/QtGui>
#include <ArcSDK.h>
#include <Ao/AoControls.h>
#include <AxCtl/qtaxctl.h>
#include <olb/esridefensesolutions.h>


// implemented in libDefenseSolutionsSDK.so
#if defined(ESRI_UNIX)
    void DsInitialize();
#endif


#define SIC_FIELD_NAME "Symbol_ID"
#define PARENT_FIELD_NAME "Parent"


#define ABORT {                                              \
    printf ("abort on line %d of %s\n", __LINE__, __FILE__); \
    shutdown();                                              \
}


// the first macro expands the symbol defined on the command line; the second puts quotes around it
#define EXPANDSTRING(x) STRINGIFY(x)
#define STRINGIFY(x) #x


// get the full path of a dataset in the Defense Solutions sample data
CComBSTR dsDataPath (const char *relativeDatasetPath)
{
#if defined(ESRI_UNIX)
    CComBSTR path (EXPANDSTRING(DATADIRECTORY) "/Samples/data/defensesolutions/");
#elif defined(ESRI_WINDOWS)
    USES_CONVERSION;
    CComBSTR path( L"C://Program Files//ArcGIS//DeveloperKit10.0//Samples//data//defensesolutions//");
#endif

    path.Append (relativeDatasetPath);
    return path.Copy();
}


// shut down arc objects and terminate the application
static void shutdown()
{
    IAoInitializePtr ipInit(CLSID_AoInitialize);
    ipInit->Shutdown();
    ::AoUninitialize();
    AoExit(0);
}


// check to see if the feature class has a string field with the indicated characteristics
static bool HasStringField (IFeatureClassPtr ipFeatureClass, const char *name, long minimumFieldLength = -1)
{
    // first see if we can find the field by name or alias
    CComBSTR fieldName (name);
    long fieldIndex = -1;
    IFieldsPtr ipFields;
    ipFeatureClass->get_Fields (&ipFields);
    ipFields->FindFieldByAliasName (fieldName, &fieldIndex);
    if ( fieldIndex < 0 )
        ipFields->FindField (fieldName, &fieldIndex);
    if ( fieldIndex >= 0 )
    {
        // if the field exists, check its type
        IFieldPtr ipField;
        ipFields->get_Field (fieldIndex, &ipField);
        esriFieldType type;
        ipField->get_Type (&type);
        if ( type != esriFieldTypeString )
            fieldIndex = -1;
        else if ( minimumFieldLength >= 0 )
        {
            // it is a string field, so check its length if specified
            long actualFieldLength = -1;
            ipField->get_Length (&actualFieldLength);
            if ( actualFieldLength < minimumFieldLength )
                fieldIndex = -1;
        }
    }
    return ( fieldIndex >= 0 );
}


// check to see if the feature class has the indicated extension name with a valid namespace
static bool CheckExtensionCLSID (IFeatureClassPtr ipFeatureClass, const char *className)
{
    static const unsigned int numPrefixes = 4;
    static const char *prefixes[numPrefixes] = {
        "esriDefenseSolutions.",
        "esriMOLE.",
        "moleClassExtensions.",
        "moleClassExtensions2."
    };
    IUIDPtr ipActualUID (CLSID_UID);
    ipFeatureClass->get_EXTCLSID (&ipActualUID);
    if ( ! ipActualUID ) return false;
    for ( unsigned int i = 0; i < numPrefixes; ++i )
    {
        // compare the class name using each of four possible prefixes
        CComBSTR bstrCLSID (prefixes[i]);
        bstrCLSID.Append (className);
        IUIDPtr ipTargetUID (CLSID_UID);
        ipTargetUID->put_Value (CComVariant(bstrCLSID));
        VARIANT_BOOL isEqual;
        ipActualUID->Compare (ipTargetUID, &isEqual);
        if ( isEqual == VARIANT_TRUE )
            return true;
    }
    return false;
}


// return true if the feature class can be a force element feature class
static bool IsForceElementFC (IFeatureClassPtr ipFeatureClass)
{
    // first try to distinguish by the extension class ID
    if ( CheckExtensionCLSID(ipFeatureClass, "ForceElements") )
        return true;

    // the extension is not required for MOLE graphics to be rendered; we can also go by feature type and fields
    // use the presence of the parent field to distinguish force elements from tactical points
    esriGeometryType shapeType;
    ipFeatureClass->get_ShapeType (&shapeType);
    return (
        shapeType == esriGeometryPoint &&
        HasStringField(ipFeatureClass, SIC_FIELD_NAME, 15) &&
        HasStringField(ipFeatureClass, PARENT_FIELD_NAME)
    );
}


// return true if the feature class can be a tactical graphic feature class
static bool IsTacticalGraphicFC (IFeatureClassPtr ipFeatureClass)
{
    // first try to distinguish by one of the extension class IDs
    if (
        CheckExtensionCLSID(ipFeatureClass, "TacticalPoints") ||
        CheckExtensionCLSID(ipFeatureClass, "TacticalLines")  ||
        CheckExtensionCLSID(ipFeatureClass, "TacticalAreas")
    )
        return true;
    else if ( HasStringField(ipFeatureClass, SIC_FIELD_NAME, 15) )
    {
        // an extension is not required for MOLE graphics to be rendered; we can also go by feature type and fields
        // use the presence of the parent field to distinguish force elements from tactical points
        esriGeometryType shapeType;
        ipFeatureClass->get_ShapeType (&shapeType);
        if ( shapeType == esriGeometryPoint && ( ! HasStringField(ipFeatureClass, PARENT_FIELD_NAME)) )
            return true;
        else
            return ( shapeType == esriGeometryPolyline || shapeType == esriGeometryPolygon );
    }
    else
        return false;
}


// load a feature layer or MOLE group layer, depending on the type of feature class detected
static ILayerPtr LoadLayer (IFeatureWorkspacePtr ipFeatureWS, const char *featureClassName)
{
    ILayerPtr ipLayer;
    IFeatureClassPtr ipFeatureClass;
    ipFeatureWS->OpenFeatureClass (CComBSTR(featureClassName), &ipFeatureClass);
    if ( ! ipFeatureClass )
        printf ("failed to open %s\n", featureClassName);
    else
    {
        // figure out what kind of feature class we have opened
        printf ("opened %s:  ", featureClassName);
        bool isFE = IsForceElementFC (ipFeatureClass);
        bool isTG = ( isFE ? false : IsTacticalGraphicFC(ipFeatureClass) );
        IFeatureLayerPtr ipFeatureLayer (CLSID_FeatureLayer);
        ipFeatureLayer->putref_FeatureClass (ipFeatureClass);
        if ( isFE || isTG )
        {
            // group the feature layer with a MOLE graphic layer; don't render the features, just the graphics
            ipFeatureLayer->put_Name (CComBSTR("Features"));
            ipFeatureLayer->put_Visible (VARIANT_FALSE);
            IGroupLayerPtr ipGroupLayer (CLSID_GroupLayer);
            ipGroupLayer->put_Name (CComBSTR(featureClassName));
            ipGroupLayer->Add (ipFeatureLayer);
            IEnvelopePtr ipEnvelope;
            IGeoDatasetPtr(ipFeatureClass)->get_Extent (&ipEnvelope);
            double height;
            ipEnvelope->get_Height (&height);
            if ( isFE )
            {
                // the commented lines will dynamically scale the graphics with the size of the display
                printf ("force elements\n");
                IForceElementLayerPtr ipForceElementLayer (CLSID_ForceElementLayer);
                ipForceElementLayer->put_Size (0.05 * height);
                //ipForceElementLayer->put_SizeIsRatio (VARIANT_TRUE);
                //ipForceElementLayer->put_Size (0.03);
                ICachedGraphicFeatureLayerPtr ipCachedGraphicFeatureLayer (ipForceElementLayer);
                ipCachedGraphicFeatureLayer->putref_FeatureLayer (IGeoFeatureLayerPtr(ipFeatureLayer));
                ipLayer = ipForceElementLayer;
                ipLayer->put_Name (CComBSTR("Graphics"));
                ipGroupLayer->Add (ipLayer);
            }
            else
            {
                printf ("tactical graphics\n");
                ITacticalGraphicLayerPtr ipTacticalGraphicLayer (CLSID_TacticalGraphicLayer);
                ipTacticalGraphicLayer->put_TextSize (0.03 * height);
                ICachedGraphicFeatureLayerPtr ipCachedGraphicFeatureLayer (ipTacticalGraphicLayer);
                HRESULT hr = ipCachedGraphicFeatureLayer->putref_FeatureLayer (IGeoFeatureLayerPtr(ipFeatureLayer));
                ipLayer = ipTacticalGraphicLayer;
                ipLayer->put_Name (CComBSTR("Graphics"));
                ipGroupLayer->Add (ipLayer);
            }
            ipLayer = ipGroupLayer;
        }
        else
        {
            // this is just a plain old feature layer
            printf ("non-MOLE features\n");
            ipFeatureLayer->put_Name (CComBSTR(featureClassName));
            ipLayer = ipFeatureLayer;
        }
    }
    return ipLayer;
}


// compute the union of the envelope with the AOI of the layer
static void unionEnvelope (ILayerPtr &ipLayer, IEnvelopePtr &ipEnvelope)
{
    if ( ! ipEnvelope )
        ipLayer->get_AreaOfInterest (&ipEnvelope);
    else
    {
        IEnvelopePtr ipAOI;
        ipLayer->get_AreaOfInterest (&ipAOI);
        ipEnvelope->Union (ipAOI);
    }
}


// add the named tool to the toolbar control
static void addTool (IToolbarControlPtr &ipToolbar, const char *name)
{
    CComBSTR toolID ("esriControlCommands.");
    toolID.Append (name);
    long itemIndex;
    ipToolbar->AddItem (CComVariant(toolID), 0, -1, VARIANT_FALSE, 0, esriCommandStyleIconOnly, &itemIndex);
}


int main(int argc, char **argv)
{
    printf ("calling AoInitialize\n");

    ::AoInitialize(NULL);

#if defined(ESRI_UNIX)
    ::DsInitialize();
#endif
    {
        printf ("initializing license\n");

        IAoInitializePtr ipInit (CLSID_AoInitialize);
        esriLicenseStatus status;
        ipInit->Initialize (esriLicenseProductCodeEngine, &status);
        if ( status != esriLicenseCheckedOut )
        {
            printf ("Invalid Licensing.\n");
            AoExit (0);
        }
    }

  printf ("creating application\n");

    QApplication qapp(argc, argv);

    printf ("creating widgets\n");

    QSplitter split      (NULL);
    QAxCtl    tlbControl (AoPROGID_ToolbarControl, NULL,   "Toolbar Control");
    QAxCtl    tocControl (AoPROGID_TOCControl,     &split, "TOC Control");
    QAxCtl    mapControl (AoPROGID_MapControl,     &split, "Map Control");

    tlbControl.setMinimumHeight (28);
    tlbControl.setMaximumHeight (28);
    split.setOpaqueResize (false);
    QList<int> sizes (split.sizes());
    sizes[0] = 250;
    sizes[1] = 550;
    split.setSizes (sizes);

    QVBoxLayout rootLayout;
    rootLayout.addWidget (&tlbControl);
    rootLayout.addWidget (&split);
    QWidget window;
    window.resize (800, 600);
    window.setLayout (&rootLayout);
    window.setWindowTitle ("Mole Layers");
    window.show();

    printf ("opening workspace\n");

    IWorkspaceFactoryPtr ipWSFact (CLSID_FileGDBWorkspaceFactory);
    IWorkspacePtr ipWorkspace;
  CComBSTR bstmp;
    if ( argc > 1 && strcmp(argv[1], "noexts") == 0 )
        ipWSFact->OpenFromFile ((bstmp = dsDataPath("oplan_no_exts.gdb")), 0, &ipWorkspace);
    else
        ipWSFact->OpenFromFile ((bstmp = dsDataPath("oplan_93_exts.gdb")), 0, &ipWorkspace);
  SysFreeString(bstmp);
    IFeatureWorkspacePtr ipFeatureWS (ipWorkspace);
    if ( ! ipFeatureWS ) ABORT;

    printf ("loading layers\n");

    ILayerPtr ipLayerFE      (LoadLayer(ipFeatureWS, "force_elements"));
    ILayerPtr ipLayerPointTG (LoadLayer(ipFeatureWS, "tactical_graphic_points"));
    ILayerPtr ipLayerLineTG  (LoadLayer(ipFeatureWS, "tactical_graphic_lines"));
    ILayerPtr ipLayerAreaTG  (LoadLayer(ipFeatureWS, "tactical_graphic_areas"));
    ILayerPtr ipLayerAOI     (LoadLayer(ipFeatureWS, "area_of_interest"));
    if ( (!ipLayerFE) || (!ipLayerAreaTG) || (!ipLayerLineTG) || (!ipLayerPointTG) || (!ipLayerAOI) ) ABORT;

    printf ("setting up the controls\n");

    IUnknownPtr ipUnknown;
    mapControl.getInterface (&ipUnknown);
    IMapControl4Ptr ipMap (ipUnknown);
    tlbControl.getInterface (&ipUnknown);
    IToolbarControlPtr ipToolbar (ipUnknown);
    tocControl.getInterface (&ipUnknown);
    ITOCControlPtr ipToc (ipUnknown);
    if ( (!ipMap) || (!ipToolbar) || (!ipToc) ) ABORT;

    // buddy up and load the toolbar
    ipToolbar->SetBuddyControl (ipMap);
    ipToc->SetBuddyControl (ipMap);
    addTool (ipToolbar, "ControlsMapZoomInTool");
    addTool (ipToolbar, "ControlsMapZoomOutTool");
    addTool (ipToolbar, "ControlsMapZoomInFixedCommand");
    addTool (ipToolbar, "ControlsMapZoomOutFixedCommand");
    addTool (ipToolbar, "ControlsMapPanTool");
    addTool (ipToolbar, "ControlsMapFullExtentCommand");
    addTool (ipToolbar, "ControlsMapZoomToLastExtentBackCommand");
    addTool (ipToolbar, "ControlsMapZoomToLastExtentForwardCommand");

    printf ("setting up the map\n");

    ipMap->AddLayer (ipLayerFE,      0);
    ipMap->AddLayer (ipLayerPointTG, 1);
    ipMap->AddLayer (ipLayerLineTG,  2);
    ipMap->AddLayer (ipLayerAreaTG,  3);
    ipMap->AddLayer (ipLayerAOI,     4);

    IEnvelopePtr ipEnvelope;
    unionEnvelope (ipLayerFE, ipEnvelope);
    unionEnvelope (ipLayerPointTG, ipEnvelope);
    unionEnvelope (ipLayerLineTG, ipEnvelope);
    unionEnvelope (ipLayerAreaTG, ipEnvelope);
    ipEnvelope->Expand (1.1, 1.1, VARIANT_TRUE);
    ipMap->put_Extent (ipEnvelope);

    printf ("passing control to Qt\n");

    qapp.exec();

    shutdown();
    return 0;
}