SimplePoint Plug-in Data Source
SimplePointDatasetHelper.cpp
// Copyright 2012 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.
// 



// SimplePointDatasetHelper.cpp : Implementation of CSimplePointDatasetHelper
#include "stdafx.h"
#include "SimplePointVC.h"
#include "SimplePointDatasetHelper.h"
#include "GeneralUtils.h"

/////////////////////////////////////////////////////////////////////////////
// CSimplePointDatasetHelper

STDMETHODIMP CSimplePointDatasetHelper::InterfaceSupportsErrorInfo(REFIID riid)
{
  static const IID* arr[] = 
  {
    &IID_ISimplePointDatasetHelper
  };
  for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
  {
    if (InlineIsEqualGUID(*arr[i],riid))
      return S_OK;
  }
  return S_FALSE;
}

// IPlugInDatasetInfo
STDMETHODIMP CSimplePointDatasetHelper::get_LocalDatasetName(BSTR *localName)
{
  if (! localName) return E_POINTER;
  
  return m_sDatasetName.CopyTo(localName);
}

STDMETHODIMP CSimplePointDatasetHelper::get_DatasetType(esriDatasetType *DatasetType)
{
  if (! DatasetType) return E_POINTER;
  
  *DatasetType = esriDTFeatureClass;
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_GeometryType(esriGeometryType *GeometryType)
{
  if (! GeometryType) return E_POINTER;
  
  *GeometryType = esriGeometryPoint;
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_ShapeFieldName(BSTR *ShapeFieldName)
{
  if (! ShapeFieldName) return E_POINTER;

  CComBSTR str(L"Shape");
  *ShapeFieldName = str.Detach();
  return S_OK;
}

// IPlugInDatasetHelper
STDMETHODIMP CSimplePointDatasetHelper::get_ClassCount(long *Count)
{
  if (! Count) return E_POINTER;

  // This is a standalone feature class
  *Count = 1;

  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_ClassName(long Index, BSTR *Name)
{
  if (! Name) return E_POINTER;

  return m_sDatasetName.CopyTo(Name);
}

STDMETHODIMP CSimplePointDatasetHelper::get_ClassIndex(BSTR Name, long *Index)
{
  if (! Index) return E_POINTER;

  // This is a standalone feature class, so class index doesnt apply
  *Index = 0;

  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_Bounds(IEnvelope **Bounds)
{
  HRESULT hr;
  if (! Bounds) return E_POINTER;

  // Get an envelope for the extent of the dataset
  // We will have to calculate the extent by opening a cursor
  // on the dataset, and building up a minimum bounding rectangle 
  
  // Prepare to open a cursor.
  // Make the fieldmap, with values of -1 (attributes do not need fetching)

  long lNumFields;
  IFieldsPtr ipFields;
  
  hr = get_Fields(0, &ipFields);
  if (FAILED(hr)) return hr;

  hr = ipFields->get_FieldCount(&lNumFields);
  if (FAILED(hr)) return hr;

  CComVariant vFieldMap;
  vFieldMap.vt = VT_ARRAY | VT_I4;
  vFieldMap.parray = ::SafeArrayCreateVector(VT_I4, 0, lNumFields);
  long HUGEP *pMap = 0; // raw pointer to array
  hr = SafeArrayAccessData(vFieldMap.parray, (void HUGEP **)&pMap);
  if (FAILED(hr)) return hr;
  long i;
  for (i=0; i < lNumFields; i++)
    pMap[i] = -1;
  SafeArrayUnaccessData(vFieldMap.parray);  

  // Open the cursor
  IPlugInCursorHelperPtr ipPlugInCursor;
  hr = FetchAll(0, L"", vFieldMap, &ipPlugInCursor);
  if (FAILED(hr)) return hr;

  // loop through the data recording min/max X and Y values.
  double dxMin =  9999999;
  double dxMax = -9999999;
  double dyMin =  9999999;
  double dyMax = -9999999;
  double x,y;

  VARIANT_BOOL bEOF = VARIANT_FALSE;
  IGeometryPtr ipGeom;
  hr = ipGeom.CreateInstance(CLSID_Point);
  if (FAILED(hr)) return hr;

  IPointPtr ipPoint;

  long lNumRecords = 0;
  do
  {
    lNumRecords++;
    hr = ipPlugInCursor->QueryShape(ipGeom);
    if (FAILED(hr)) return hr;
    
    ipPoint = ipGeom;
    if (ipPoint == NULL) return E_FAIL;
    hr = ipPoint->QueryCoords(&x, &y);
    if (FAILED(hr)) return hr;

    if (x > dxMax) dxMax = x;
    if (x < dxMin) dxMin = x;
    if (y > dyMax) dyMax = y;
    if (y < dyMin) dyMin = y;

    hr = ipPlugInCursor->NextRecord();
    if (hr != S_OK)
    {
      hr = ipPlugInCursor->IsFinished(&bEOF);
      if (FAILED(hr)) return hr;
    }
  } while (!bEOF);

  // Handle special case of single point in file
  // - add a small amount, so that we will end up with an envelope rather than a point
  if (lNumRecords == 1)
  {
    double dDelta = 0.01;
    if (dxMax != 0) 
      dDelta = dxMax / 1000;
    dxMax += dDelta;
    dyMax += dDelta;
  }

  // Create the envelope object, setting the appropriate spatial reference
  IEnvelopePtr ipEnv;
  hr = ipEnv.CreateInstance(CLSID_Envelope);
  if (FAILED(hr)) return hr;

  ISpatialReferencePtr ipSR;
  hr = ipSR.CreateInstance(CLSID_UnknownCoordinateSystem);
  if (FAILED(hr)) return hr;

  hr = ipEnv->putref_SpatialReference(ipSR);
  if (FAILED(hr)) return hr;

  hr = ipEnv->PutCoords(dxMin, dyMin, dxMax, dyMax);
  if (FAILED(hr)) return hr;

  *Bounds = ipEnv.Detach(); // pass ownership of object to client
  
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_Fields(long ClassIndex, IFields **FieldSet)
{
  if (! FieldSet) return E_POINTER;
  
  HRESULT hr;
  
  // First get the standard fields for an feature class
  IObjectClassDescriptionPtr ipOCDesc;
  hr = ipOCDesc.CreateInstance(CLSID_FeatureClassDescription);
  if (FAILED(hr)) return hr;

  IFieldsPtr ipFields;
  hr = ipOCDesc->get_RequiredFields(&ipFields);
  if (FAILED(hr)) return hr;

  IFieldPtr ipField;

  // We will have: a shape field name of "shape"
  // an UnknownCoordinateSystem
  // Just need to change the geometry type to Point
  long i;
  long lNumFields;
  esriFieldType eFieldType;
  hr = ipFields->get_FieldCount(&lNumFields);
  if (FAILED(hr)) return hr;

  for (i=0; i < lNumFields; i++)
  {
    hr = ipFields->get_Field(i, &ipField);
    if (FAILED(hr)) return hr;

    hr = ipField->get_Type(&eFieldType);
    if (eFieldType == esriFieldTypeGeometry)
    {
      IGeometryDefPtr ipGeomDef;
      hr = ipField->get_GeometryDef(&ipGeomDef);
      if (FAILED(hr)) return hr;

      IGeometryDefEditPtr ipGeomDefEdit = ipGeomDef;
      if (ipGeomDefEdit == NULL) return E_FAIL;

      hr = ipGeomDefEdit->put_GeometryType(esriGeometryPoint);
      if (FAILED(hr)) return hr;
    }
  }

    // Add the extra text field
  IFieldEditPtr ipFieldEdit;
  hr = ipFieldEdit.CreateInstance(CLSID_Field);
  if (FAILED(hr)) return hr;

  hr = ipFieldEdit->put_Name(L"Column1");
  if (FAILED(hr)) return hr;
  
  hr = ipFieldEdit->put_Type(esriFieldTypeString);
  if (FAILED(hr)) return hr;

  hr = ipFieldEdit->put_Length(1);
  if (FAILED(hr)) return hr;

  IFieldsEditPtr ipFieldsEdit = ipFields;
  if (ipFieldsEdit == NULL) return E_FAIL;

  ipField = ipFieldEdit;
  if (ipField == NULL) return E_FAIL;

  hr = ipFieldsEdit->AddField(ipField);
  if (FAILED(hr)) return hr;

  *FieldSet = ipFields.Detach(); // pass ownership of object to client 

  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_OIDFieldIndex(long ClassIndex, long *OIDFieldIndex)
{
  if (! OIDFieldIndex) return E_POINTER;

  *OIDFieldIndex = 0;

  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::get_ShapeFieldIndex(long ClassIndex, long *ShapeFieldIndex)
{
  if (! ShapeFieldIndex) return E_POINTER;
  
  *ShapeFieldIndex = 1;

  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::FetchByID(long ClassIndex, long ID, VARIANT FieldMap, IPlugInCursorHelper **cursorHelper)
{
  HRESULT hr;
  if (! cursorHelper) return E_POINTER;

  // Some parameters can be ignored since,
  //   ClassIndex is only relevant to feature datasets
  //   WhereClause is not appropriate since we are not supporting SQL
  ISimplePointCursorHelperPtr ipSimplePointCursorHelper;
  hr = ipSimplePointCursorHelper.CreateInstance(CLSID_SimplePointCursorHelper);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->put_FieldMap(FieldMap);
  if (FAILED(hr)) return hr;

  IFieldsPtr ipFields;
  hr = get_Fields(0, &ipFields);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->putref_Fields(ipFields);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->put_OID(ID);
  if (FAILED(hr)) return hr;

  CComBSTR sFilePath;
  sFilePath = m_sWorkspacePath;
  sFilePath.Append(m_sDatasetName);
  hr = ipSimplePointCursorHelper->put_FilePath(sFilePath);
  if (FAILED(hr)) return hr;

  IPlugInCursorHelperPtr ipPlugInCursorHelper;
  ipPlugInCursorHelper = ipSimplePointCursorHelper;
  if (ipPlugInCursorHelper == NULL) return E_FAIL;

  *cursorHelper = ipPlugInCursorHelper.Detach(); // Pass ownership of object back to client
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::FetchAll(long ClassIndex, BSTR WhereClause, VARIANT FieldMap, IPlugInCursorHelper **cursorHelper)
{
  HRESULT hr;
  if (! cursorHelper) return E_POINTER;

  // Some parameters can be ignored since,
  //   ClassIndex is only relevant to feature datasets
  //   WhereClause is not appropriate since we are not supporting SQL
  ISimplePointCursorHelperPtr ipSimplePointCursorHelper;
  hr = ipSimplePointCursorHelper.CreateInstance(CLSID_SimplePointCursorHelper);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->put_FieldMap(FieldMap);
  if (FAILED(hr)) return hr;

  IFieldsPtr ipFields;
  hr = get_Fields(0, &ipFields);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->putref_Fields(ipFields);
  if (FAILED(hr)) return hr;

  CComBSTR sFilePath;
  sFilePath = m_sWorkspacePath;
  sFilePath.Append(m_sDatasetName);
  hr = ipSimplePointCursorHelper->put_FilePath(sFilePath);
  if (FAILED(hr)) return hr;

  IPlugInCursorHelperPtr ipPlugInCursorHelper;
  ipPlugInCursorHelper = ipSimplePointCursorHelper;
  if (ipPlugInCursorHelper == NULL) return E_FAIL;

  *cursorHelper = ipPlugInCursorHelper.Detach(); // Pass ownership of object back to client
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::FetchByEnvelope(long ClassIndex, IEnvelope *env, VARIANT_BOOL strictSearch, BSTR WhereClause, VARIANT FieldMap, IPlugInCursorHelper **cursorHelper)
{
  HRESULT hr;
  if (! cursorHelper) return E_POINTER;

  // Some parameters can be ignored since,
  //   ClassIndex is only relevant to feature datasets
  //   WhereClause is not appropriate since we are not supporting SQL
  ISimplePointCursorHelperPtr ipSimplePointCursorHelper;
  hr = ipSimplePointCursorHelper.CreateInstance(CLSID_SimplePointCursorHelper);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->put_FieldMap(FieldMap);
  if (FAILED(hr)) return hr;

  IFieldsPtr ipFields;
  hr = get_Fields(0, &ipFields);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->putref_Fields(ipFields);
  if (FAILED(hr)) return hr;

  hr = ipSimplePointCursorHelper->putref_QueryEnvelope(env);
  if (FAILED(hr)) return hr;

  CComBSTR sFilePath;
  sFilePath = m_sWorkspacePath;
  sFilePath.Append(m_sDatasetName);
  hr = ipSimplePointCursorHelper->put_FilePath(sFilePath);
  if (FAILED(hr)) return hr;

  IPlugInCursorHelperPtr ipPlugInCursorHelper;
  ipPlugInCursorHelper = ipSimplePointCursorHelper;
  if (ipPlugInCursorHelper == NULL) return E_FAIL;

  *cursorHelper = ipPlugInCursorHelper.Detach(); // Pass ownership of object back to client
  return S_OK;
}


// ISimplePointDatasetHelper methods
STDMETHODIMP CSimplePointDatasetHelper::put_WorkspacePath(BSTR newVal)
{
  m_sWorkspacePath = newVal;
  return S_OK;
}

STDMETHODIMP CSimplePointDatasetHelper::put_DatasetName(BSTR newVal)
{
  m_sDatasetName = newVal;
  return S_OK;
}