CreateShapeFromText.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. // // 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; }