Create a shapefile from a text file with XY values
CreateShapeFromText.cpp
// Copyright 2011 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.
// 

  

// CreateShapeFromText
//
// This sample demonstrates 2 ways of inserting features into a
// feature class:
//
// 1) use an insert cursor and
// 2) calling CreateFeature/Store.
//
// The primary difference between the two methods is that using insert
// cursors is much quicker because IFeature::Store, which performs all
// object behavior, is not called if it isn't necessary to do so.  If
// you are persisting complex features then IFeature::Store must be
// called.  An insert cursor will detect when complex features are
// being inserted and call IFeature::Store for you. Therefore in the
// case of complex features both methods yield approximately the same
// performance.

#include "CreateShapeFromText.h"

int main(int argc, char* argv[])
{
  char *inText = 0;
  char *outputPath = 0;
  char *shapeFileName = 0;
  int paramCount = 0;
  bool useFB = false;
  
  // Parse the command line
  for (int i = 1; i < argc; i++)
  {
    if (strcmp(argv[i], "-h") == 0)
    {
      Usage();
      AoExit(0);
    }
    if (strcmp(argv[i], "-t") == 0)
    {
      inText = argv[++i];
      paramCount++;
    }
    if (strcmp(argv[i], "-p") == 0)
    {
      outputPath = argv[++i];
      paramCount++;
    }
    if (strcmp(argv[i], "-s") == 0)
    {
      shapeFileName = argv[++i];
      paramCount++;
    }
    if (strcmp(argv[i], "-b") == 0)
    {
      useFB = true;
    }
  }
  
  // Check we got all parameters and they are valid
  if ((paramCount != 3) || (inText == 0) || (shapeFileName == 0) || (outputPath == 0))
  {
    Usage();
    AoExit(0);
  }
  
  // Check that the text file exists and open it
  std::ifstream inFile (inText, std::ios::in);
  if (!inFile)
  {
    std::cerr << "Couldn't open text file\n";
    AoExit(0);
  }
  
  // Initialize ArcEngine
  if (!InitializeApp())
  {
    ShutdownApp();
    AoExit(0);
  }
  
  // Create a new shapefile
  IFeatureClassPtr ipShape;
  HRESULT hr = CreateShapefile(outputPath, shapeFileName, &ipShape);
  if (SUCCEEDED(hr) && ipShape != 0)
  {
    // Populate the new shapefile with points from the text file
    hr = AddPoints(inFile, ipShape, useFB);
  }
  
  // Check returned HRESULT
  switch (hr)
  {
  case S_OK:
    std::cerr << "hr is S_OK" << std::endl;
    break;
  case E_FAIL:
    std::cerr << "hr is E_FAIL" << std::endl;
    break;
  case E_POINTER:
    std::cerr << "hr is E_POINTER" << std::endl;
    break;
  default:
    std::cerr << "hr is 0x" << std::hex << hr << std::dec << " " << hr << std::endl;
    break;
  }
  
  // Uninitialize ArcEngine
  ShutdownApp();
  AoExit(0);
}

// This function creates a shapefile at outPath/outName to store the
// points and sets *ppShape to the newly created feature class.  It
// creates the geometry (i.e. shape) field and defines the spatial
// reference and create 3 other fields to hold data.
HRESULT CreateShapefile(char *outPath, char *outName, IFeatureClass **ppShape)
{
  
  using std::cerr;
  using std::endl;
  
  // Get a feature workspace for output shapefile
  IWorkspaceFactoryPtr ipWorkFact(CLSID_ShapefileWorkspaceFactory);
  IWorkspacePtr ipWork;
  HRESULT hr = ipWorkFact->OpenFromFile(CComBSTR(outPath), 0, &ipWork);
  if (FAILED(hr) || ipWork == 0)
  {
    cerr << "Couldn't find the path " << outPath << endl;
    if (FAILED(hr))
      return hr;
    else
      return E_FAIL;
  }
  IFeatureWorkspacePtr ipFeatWork (ipWork);
  
  //
  // Set up fields
  //
  IFieldsPtr ipFields(CLSID_Fields);
  IFieldsEditPtr ipFieldsEdit;
  ipFieldsEdit = ipFields; //QI
  // Geometry and spatial reference
  IFieldPtr ipField(CLSID_Field); 
  IFieldEditPtr ipFieldEdit;
  ipFieldEdit = ipField; //QI
  ipFieldEdit->put_Name(CComBSTR(L"Shape"));
  ipFieldEdit->put_Type(esriFieldTypeGeometry);
  IGeometryDefPtr ipGeoDef(CLSID_GeometryDef);
  IGeometryDefEditPtr ipGeoDefEdit;
  ipGeoDefEdit = ipGeoDef; //QI
  ipGeoDefEdit->put_GeometryType(esriGeometryPoint);
  ISpatialReferenceFactory2Ptr ipSpaRefFact2(CLSID_SpatialReferenceEnvironment);
  IGeographicCoordinateSystemPtr ipGeoCoordSys;
  ipSpaRefFact2->CreateGeographicCoordinateSystem(esriSRGeoCS_NAD1983, &ipGeoCoordSys);
  ISpatialReferencePtr ipSRef;
  ipSRef = ipGeoCoordSys; //QI
  ipGeoDefEdit->putref_SpatialReference(ipSRef);
  ipFieldEdit->putref_GeometryDef(ipGeoDef);
  ipFieldsEdit->AddField(ipField);
  // X, Y, and Value fields
  //
  // NOTE: We do not have to explicitly add fields for Lat. and Long. b/c the shape field
  // holds this type of data.  It is sometimes useful to have the Lat. and Long. fields
  // explicitly in the table, however, and we do this here.
  IFieldPtr ipX(CLSID_Field);
  IFieldPtr ipY(CLSID_Field);
  IFieldPtr ipValue(CLSID_Field);
  long len = 30;
  ipFieldEdit = ipX;
  ipFieldEdit->put_Length(len);
  ipFieldEdit->put_Name(CComBSTR(L"Longitude"));
  ipFieldEdit->put_Type(esriFieldTypeDouble);
  ipFieldsEdit->AddField(ipX);
  ipFieldEdit = ipY;
  ipFieldEdit->put_Length(len);
  ipFieldEdit->put_Name(CComBSTR(L"Latitude"));
  ipFieldEdit->put_Type(esriFieldTypeDouble);
  ipFieldsEdit->AddField(ipY);
  ipFieldEdit = ipValue;
  ipFieldEdit->put_Length(len);
  ipFieldEdit->put_Name(CComBSTR(L"Value"));
  ipFieldEdit->put_Type(esriFieldTypeDouble);
  ipFieldsEdit->AddField(ipValue);
  
  // Create the shapefile
  hr = ipFeatWork->CreateFeatureClass(CComBSTR(outName), ipFields, 0, 0, esriFTSimple, CComBSTR(L"Shape"), 0, ppShape);
  if (FAILED(hr))
    cerr << "Couldn't create the shapefile\n";
  else
    cerr << "Shapefile has been created...\n";
  
  return hr;
}

// Adds the points from the text file to ipShape, either using
HRESULT AddPoints(std::ifstream &inFile, IFeatureClass *ipShape, bool useInsertCursor)
{
  using std::cerr;
  using std::endl;
  
  double x, y, v; // used for both methods
  
  // Get the column index in table for X, Y, Value fields
  IFieldsPtr ipFieldsNew;
  long indX, indY, indV; 
  HRESULT hr = S_OK;
  hr = ipShape->get_Fields(&ipFieldsNew);
  if (FAILED(hr = ipFieldsNew->FindField(CComBSTR(L"Longitude"), &indX))) return hr;
  if (FAILED(hr = ipFieldsNew->FindField(CComBSTR(L"Latitude"), &indY))) return hr;
  if (FAILED(hr = ipFieldsNew->FindField(CComBSTR(L"Value"), &indV))) return hr;
  
  if (useInsertCursor) // use an insert cursor
  {
    cerr << "Using an insert cursor\n";
    
    // Get an insert cursor and a feature buffer
    IFeatureCursorPtr ipFeatureCursor;
    IFeatureBufferPtr ipFeatureBuffer;
    
    ipShape->Insert(VARIANT_TRUE, &ipFeatureCursor);
    ipShape->CreateFeatureBuffer(&ipFeatureBuffer);
    
    // Loop through the X, Y, Value tuples in from text file and update the feature buffer
    int newFeatureCount = 0;
    while (inFile >> x >> y >> v)
    {
      cerr << "Longitude: " << x << " Latitude: " << y << " Value: " << v << endl;
      
      // Create a new point and add its geometry to the shape field
      IPointPtr ipPoint(CLSID_Point);    
      ipPoint->put_X(x);
      ipPoint->put_Y(y);
      IGeometryPtr ipGeom = ipPoint;
      if (FAILED(hr = ipFeatureBuffer->putref_Shape(ipGeom))) return hr;
      
      // NOTE: We do not have to explicitly add the data for Lat. and Long. b/c the shape field
      // holds this type of data.  It is sometimes useful to have the Lat. and Long. data
      // explicitly in the table, however, and we do this here.
      ipFeatureBuffer->put_Value(indX, CComVariant(x));
      ipFeatureBuffer->put_Value(indY, CComVariant(y));
      ipFeatureBuffer->put_Value(indV, CComVariant(v));
      
      // Insert the feature, its ID is returned
      CComVariant ret;
      hr = ipFeatureCursor->InsertFeature(ipFeatureBuffer, &ret);
      if (FAILED(hr))
      {
        cerr << "Couldn't insert feature\n";
        return hr;
      }
      
      // Flush the insert cursor every 100 features
      if (++newFeatureCount % 100 == 0)
      {
        cerr << "Flushing...";
        ipFeatureCursor->Flush();
        cerr << "done\n";
      }
    }
    
    // Flush the insert cursor at the end of the file
    ipFeatureCursor->Flush();
  }
  else // use CreateFeature/Store
  {
    
    cerr << "Using CreateFeature/Store\n";
    
    // Loop through the X, Y, Value tuples in from text file and update the feature buffer
    while (inFile >> x >> y >> v)
    {
      cerr << "Longitude: " << x << " Latitude: " << y << " Value: " << v << endl;
      
      // Create a new feature
      IFeaturePtr ipFeat;
      if (FAILED(hr = ipShape->CreateFeature(&ipFeat))) return hr;
      // Create a new point and add it to the shape field
      IPointPtr ipPoint(CLSID_Point);    
      ipPoint->put_X(x);
      ipPoint->put_Y(y);
      IGeometryPtr ipGeom = ipPoint;
      if (FAILED(hr = ipFeat->putref_Shape(ipGeom))) return hr;
      
      // NOTE: We do not have to explicitly add the data for Lat. and Long. b/c the shape field
      // holds this type of data.  It is sometimes useful to have the Lat. and Long. data
      // explicitly in the table, however, and we do this here.
      ipFeat->put_Value(indX, CComVariant(x));
      ipFeat->put_Value(indY, CComVariant(y));
      ipFeat->put_Value(indV, CComVariant(v));
      
      // Persist your changes by calling Store()
      hr = ipFeat->Store();
      if (FAILED(hr)) return hr;
    }
  }
  
  return hr;
}

// Print out the usage details
void Usage(void)
{
  std::cout << "Usage:  CreateShapeFromText" << std::endl
    << "-t [full path to text file w/ X,Y,values]" << std::endl 
    << "-p [output path]" << std::endl 
    << "-s [output shapefile name]" << std::endl 
    << "-b [optional, specifying -b means 'use a FeatureBuffer to improve performance']" << std::endl
    << "e.g." << std::endl
    << "createshape -t ./text.txt -p ./ -s out.shp -b" << std::endl;
}