BufferFeatures.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. // // BufferFeatures.cpp // // This sample shows how to create a buffer polygon for selected // features, as well as how to draw that buffer as a graphic or write // it to disk as a shapefile. It also illustrates using a Motif scale // widget with ArcEngine. #include "BufferFeatures.h" // Global variables // // Motif Widget g_topLevel; // application Widget g_mainWindow; // main program window Widget g_mainForm; // main prog window's form Widget g_mapWidget; // map control Widget g_tocWidget; // TOC control Widget g_toolbarWidget; // toolbar control Widget g_addLayerWidget; Widget g_fileSelectionDialog; Widget g_scaleWidget; Widget g_saveToggle; Widget g_shapefilePathWidget; Widget g_bufferWidget; XtAppContext g_appContext; Atom g_wmDeleteWindow; // Other bool g_bSaveAsShapefile = false; double g_dblPixelUnits = 0.0; // main // // Initialize ArcEngine and set up widgets int main(int argc, char* argv[]) { // Interfaces for the Controls IMapControl3Ptr ipMapControl; IToolbarControlPtr ipToolbarControl; ITOCControlPtr ipTOCControl; // Initialize the engine ::AoInitialize(NULL); { IAoInitializePtr ipAOinit(CLSID_AoInitialize); esriLicenseStatus status; ipAOinit->Initialize(esriLicenseProductCodeEngine, &status); if (status != esriLicenseCheckedOut) { std::cerr << "License not available for Engine\n"; std::cerr << "status is " << status << std::endl; ipAOinit->Shutdown(); ipAOinit = 0; ::AoUninitialize(); AoExit(0); } } XtSetLanguageProc(NULL, NULL, NULL); // Initialize the Motif toolkit and create the parent widget g_topLevel = XtVaAppInitialize(&g_appContext, "XApplication", NULL, 0, &argc, argv, NULL, NULL); XtVaSetValues(g_topLevel, XmNtitle, "Motif Buffer Features Example", NULL); XtResizeWidget(g_topLevel, 999, 800, 1); // Create g_mainWindow g_mainWindow = XtVaCreateWidget("g_mainWindow", xmMainWindowWidgetClass, g_topLevel, NULL); // Create g_mainForm - it will fill the g_mainWindow g_mainForm = XtVaCreateWidget("g_mainForm", xmFormWidgetClass, g_mainWindow, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, g_mainWindow, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, g_mainWindow, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, g_mainWindow, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, g_mainWindow, XmNfractionBase, 100, NULL); //Create my button XmString label; label = XmStringCreateLocalized(const_cast<char*>("Add Layer")); g_addLayerWidget = XtVaCreateWidget("g_addLayerWidget", xmPushButtonWidgetClass, g_mainForm, XmNlabelString, label, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNheight, 35, XmNwidth, 150, NULL); XtAddCallback(g_addLayerWidget, XmNactivateCallback, processClickAdd, NULL); XmStringFree(label); // Create scale widget XmString sTitle; sTitle = XmStringCreateLocalized(const_cast<char*>("Buffer Width (pixels)")); g_scaleWidget = XtVaCreateWidget("g_scaleWidget", xmScaleWidgetClass, g_mainForm, XmNtitleString, sTitle, XmNorientation, XmHORIZONTAL, XmNminimum, 1, XmNmaximum, 300, XmNdecimalPoints, 0, XmNshowValue, True, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, g_addLayerWidget, XmNwidth, 200, XmNheight, 50, NULL); XtAddCallback(g_scaleWidget, XmNvalueChangedCallback, processScaleChange, NULL); XmStringFree(sTitle); // Create toggle label = XmStringCreateLocalized(const_cast<char*>("Save to shapefile")); g_saveToggle = XtVaCreateWidget("g_saveToggle", xmToggleButtonWidgetClass, g_mainForm, XmNlabelString, label, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, g_scaleWidget, XmNheight, 50, XmNwidth, 100, NULL); XtAddCallback(g_saveToggle, XmNvalueChangedCallback, processToggle, NULL); XmStringFree(label); //Create my button label = XmStringCreateLocalized(const_cast<char*>("Buffer")); g_bufferWidget = XtVaCreateWidget("g_bufferWidget", xmPushButtonWidgetClass, g_mainForm, XmNlabelString, label, XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNheight, 35, XmNwidth, 150, NULL); XmStringFree(label); // Create the TextField for the shapefile's path g_shapefilePathWidget = XtVaCreateWidget("g_shapefilePathWidget", xmTextFieldWidgetClass, g_mainForm, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, g_saveToggle, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, g_bufferWidget, XmNheight, 35, XmNeditable, False, NULL); // Create a FileSelectionDialog with a *.lyr mask XmString mask, title; Arg args[2]; mask = XmStringCreateLocalized(const_cast<char*>("*.lyr")); title = XmStringCreateLocalized(const_cast<char*>("Select Layer")); XtSetArg(args[0], XmNdirMask, mask); XtSetArg(args[1], XmNdialogTitle, title); g_fileSelectionDialog = XmCreateFileSelectionDialog(g_mainForm, const_cast<char*>("Select"), args, 2); XmStringFree(mask); XmStringFree(title); // Remove the Help button Widget remove1 = XmFileSelectionBoxGetChild(g_fileSelectionDialog, XmDIALOG_HELP_BUTTON); XtUnmanageChild(remove1); // Toolbar Control setup g_toolbarWidget = XtVaCreateWidget("g_toolbarWidget", mwCtlWidgetClass, g_mainForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, g_scaleWidget, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, MwNprogID, AoPROGID_ToolbarControl, NULL); XtVaSetValues(g_toolbarWidget, XmNheight, 25, NULL); MwCtlGetInterface(g_toolbarWidget, (IUnknown**)&ipToolbarControl); // TOC Control setup g_tocWidget = XtVaCreateWidget("g_tocWidget", mwCtlWidgetClass, g_mainForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, g_toolbarWidget, XmNleftAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, MwNprogID, AoPROGID_TOCControl, NULL); XtVaSetValues(g_tocWidget, XmNwidth, 200, NULL); MwCtlGetInterface(g_tocWidget, (IUnknown**)&ipTOCControl); // Map Control setup g_mapWidget = XtVaCreateWidget("g_mapWidget", mwCtlWidgetClass, g_mainForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, g_toolbarWidget, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, g_tocWidget, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, MwNprogID, AoPROGID_MapControl, NULL); MwCtlGetInterface(g_mapWidget, (IUnknown**)&ipMapControl); // Set up callbacks that need the map widget XtAddCallback(g_bufferWidget, XmNactivateCallback, processClickBuffer, (XtPointer) g_mapWidget); XtAddCallback(g_fileSelectionDialog, XmNokCallback, processFileSelect, (XtPointer) g_mapWidget); XtAddCallback(g_fileSelectionDialog, XmNcancelCallback, processFileSelect, (XtPointer) g_mapWidget); // Manage the non-parent widgets XtManageChild(g_mainWindow); XtManageChild(g_mainForm); XtManageChild(g_addLayerWidget); XtManageChild(g_scaleWidget); XtManageChild(g_bufferWidget); XtManageChild(g_saveToggle); XtManageChild(g_shapefilePathWidget); XtManageChild(g_toolbarWidget); XtManageChild(g_tocWidget); XtManageChild(g_mapWidget); // Buddy the toolbar and TOC with the map ipToolbarControl->SetBuddyControl(ipMapControl); ipTOCControl->SetBuddyControl(ipMapControl); // Add tools to toolbar ::AddStandardToolbarItems(ipToolbarControl); // Structure used to pass data to callback routines CloseFormClientDataStruct cfcds; cfcds.pMapControl = ipMapControl; cfcds.pToolbarControl = ipToolbarControl; cfcds.pTOCControl = ipTOCControl; // Handle the window manager message that the window is about to be closed g_wmDeleteWindow = XmInternAtom(XtDisplay(g_topLevel), "WM_DELETE_WINDOW", FALSE); XmAddWMProtocolCallback(g_topLevel, g_wmDeleteWindow, processClickCloseForm, &cfcds); // Realize the container widget XtRealizeWidget(g_topLevel); XtResizeWidget(g_topLevel, 1000, 800, 1); // Turn application control over to the X Toolkit Intrinsics MwCtlAppMainLoop(g_appContext); } // Function called when WM_DELETE_WINDOW is received (i.e. when the // user closes the window) void processClickCloseForm(Widget w, XtPointer client_data, XtPointer call_data) { // To be on the safe side we will make sure the controls are // destroyed before uninitializing CloseFormClientDataStruct *cfcds = (CloseFormClientDataStruct *) client_data; IMapControl3Ptr ipMapControl = cfcds->pMapControl; ipMapControl = 0; IToolbarControlPtr ipToolbarControl = cfcds->pToolbarControl; ipToolbarControl = 0; ITOCControlPtr ipTOCControl = cfcds->pTOCControl; ipTOCControl = 0; // Uninitialize the engine { IAoInitializePtr ipAOinit(CLSID_AoInitialize); ipAOinit->Shutdown(); } ::AoUninitialize(); AoExit(0); } // Calls CreateFeatureBuffer, passing in the buffer size selected in // the slider control void processClickBuffer(Widget w, XtPointer client_data, XtPointer call_data) { IMapControl3Ptr ipMapControl; MwCtlGetInterface((Widget)client_data, (IUnknown**)&ipMapControl); if (g_dblPixelUnits > 0.0) CreateBufferedGeometryOfSelectedFeatures(ipMapControl, g_dblPixelUnits); else ::ShowMessage(g_mainForm, "Error","Please select a buffer size first", true); } // Open a FileSelectionDialog window to allow the user to select a .lyr file // to add to the map control and TOC void processClickAdd(Widget w, XtPointer client_data, XtPointer call_data) { XtManageChild(g_fileSelectionDialog); } // Add the .lyr file that the user selected to the map control and TOC void processFileSelect(Widget w, XtPointer client_data, XtPointer call_data) { IMapControl3Ptr ipMapControl; MwCtlGetInterface((Widget)client_data, (IUnknown**)&ipMapControl); XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *)call_data; // Kill the dialog if they canceled if (cbs->reason == XmCR_CANCEL) { XtUnmanageChild(g_fileSelectionDialog); return; } // Get the file name and check for errors char *fileName = 0; if (!XmStringGetLtoR(cbs->value, XmFONTLIST_DEFAULT_TAG, &fileName)) { XtUnmanageChild(g_fileSelectionDialog); return; } if (*fileName) { // Try to add the layer to the map ipMapControl->AddLayerFromFile(CComBSTR(fileName), 0); // Hide file selection dialog XtUnmanageChild(g_fileSelectionDialog); } XtFree (fileName); return; } // Sets the value of g_dblPixelUnits based on the slider widget's value void processScaleChange(Widget w, XtPointer client_data, XtPointer call_data) { XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *) call_data; g_dblPixelUnits = (double) cbs->value; } // Toggles a boolean which controls whether or not a shapefile with // the buffered features is created and sets whether or not the // textfieldwidget which will contain the path to the new shapefile is // editable. void processToggle(Widget w, XtPointer client_data, XtPointer call_data) { XmToggleButtonCallbackStruct *cbs = (XmToggleButtonCallbackStruct *) call_data; if (cbs->set) { g_bSaveAsShapefile = true; XtVaSetValues(g_shapefilePathWidget, XmNeditable, True, NULL); } else { g_bSaveAsShapefile = false; XtVaSetValues(g_shapefilePathWidget, XmNeditable, False, NULL); } } // Create a polygon containing the union of the buffer of the selected // features // // scaleValue - size of buffer in pixels void CreateBufferedGeometryOfSelectedFeatures(IMapControl3 *pMapControl, double scaleValue) { // Make sure that a layer has been added long layerCount = 0; pMapControl->get_LayerCount(&layerCount); if (layerCount < 1) { ::ShowMessage(g_mainForm, "Error","Please add a feature layer first", true); return; } // Just use 1st layer, but make sure it's a featurelayer ILayerPtr ipLayer; pMapControl->get_Layer(0, &ipLayer); IFeatureSelectionPtr ipFeatSel (ipLayer); // Implicit QI if (ipFeatSel == 0) { ::ShowMessage(g_mainForm, "Error","Please add a feature layer first", true); return; } // Get the SelectionSet and make sure there's at least 1 feature selected long numSelected = 0; ISelectionSetPtr ipSelSet; ipFeatSel->get_SelectionSet(&ipSelSet); ipSelSet->get_Count(&numSelected); if (numSelected < 1) { ::ShowMessage(g_mainForm, "Error","Please select features first", true); return; } // Convert the scale value to a real world value IActiveViewPtr ipActiveView; pMapControl->get_ActiveView(&ipActiveView); double rwUnits = 0.0; ::ConvertPixelsToMapUnits(ipActiveView, scaleValue, &rwUnits); double generalOffset = rwUnits / 20.0; // Get all features ICursorPtr ipCursor; ipSelSet->Search(0, VARIANT_FALSE, &ipCursor); IFeatureCursorPtr ipFeatureCursor (ipCursor); // Implicit QI // Just get the spatial reference once. It is used when creating a // new feature class. IFeaturePtr ipFeature; ipFeatureCursor->NextFeature(&ipFeature); ISpatialReferencePtr ipSpaRef; if (ipFeature != 0) { IGeometryPtr ipG; ipFeature->get_Shape(&ipG); ipG->get_SpatialReference(&ipSpaRef); } IPolygonPtr ipAllPolygons (CLSID_Polygon); ITopologicalOperatorPtr ipTopoOp (ipAllPolygons); // Implicit QI ipTopoOp->Simplify(); // Loop through the selected features and union their buffered geometry bool needToGeneralize; esriGeometryType geomType; IPolycurvePtr ipPolycurve; IPolygonPtr ipSinglePolygon; IGeometryPtr ipGeometry; while (ipFeature != 0) { ipFeature->get_ShapeCopy(&ipGeometry); ipGeometry->get_GeometryType(&geomType); if (geomType == esriGeometryPoint) { ipTopoOp = ipGeometry; needToGeneralize = false; } else { ipPolycurve = ipGeometry; ipPolycurve->Generalize(generalOffset); ipTopoOp = ipPolycurve; needToGeneralize = true; } ipTopoOp->Simplify(); ipTopoOp->Buffer(rwUnits,&ipGeometry); ipSinglePolygon = ipGeometry; if (needToGeneralize) ipSinglePolygon->Generalize(generalOffset); ipTopoOp = ipSinglePolygon; ipTopoOp->Simplify(); ipGeometry = ipAllPolygons; ipTopoOp->Union(ipAllPolygons, &ipGeometry); ipAllPolygons = ipGeometry; ipFeatureCursor->NextFeature(&ipFeature); } // Draw buffer to screen and persist as shapefile if specified DrawPolygonToActiveView(pMapControl, ipAllPolygons); if (g_bSaveAsShapefile) { char *text = XmTextFieldGetString(g_shapefilePathWidget); if (strcmp(text, "") != 0) { CComBSTR bsPathToWorkspace; CComBSTR bsName; HRESULT hr1 = GetParentDirFromFullPath(text, &bsPathToWorkspace, false); HRESULT hr2 = GetFileFromFullPath(text, &bsName); if (FAILED(hr1) || FAILED(hr2) || bsPathToWorkspace.Length() == 0 || bsName.Length() == 0) { ::ShowMessage(g_mainForm, "Error","Please specify the full path for the new shapefile", true); return; } HRESULT hr = CreateShapefile(rwUnits, bsPathToWorkspace, bsName, ipSpaRef, ipAllPolygons); if (FAILED(hr)) ::ShowMessage(g_mainForm, "Error","Couldn't create shapefile", true); } else ::ShowMessage(g_mainForm, "Error","Please specify the full path for the new shapefile", true); } } // Display a polygon in the ActiveView. This only draws graphics to // the screen- they will be gone when the activeview is refreshed. void DrawPolygonToActiveView(IMapControl3 *pMapControl, IPolygon *pPoly) { IFillSymbolPtr ipFillSymbol(CLSID_SimpleFillSymbol); ILineSymbolPtr ipLineSymbol; ISymbolPtr ipSymbol; IRgbColorPtr ipRGBColor(CLSID_RgbColor); IActiveViewPtr ipActiveView; IScreenDisplayPtr ipScreenDisplay; long color = 45; // Set up FillSymbol ipSymbol = ipFillSymbol; ipSymbol->put_ROP2(esriROPXOrPen); ipRGBColor->put_UseWindowsDithering(VARIANT_FALSE); ipRGBColor->put_Red(color); ipRGBColor->put_Green(color); ipRGBColor->put_Blue(color); ipFillSymbol->put_Color(ipRGBColor); // Set up FillSymbol's outline ipFillSymbol->get_Outline(&ipLineSymbol); ((ISymbolPtr)ipLineSymbol)->put_ROP2(esriROPXOrPen); color = 145; ipRGBColor->put_Red(color); ipRGBColor->put_Green(color); ipRGBColor->put_Blue(color); ipLineSymbol->put_Color(ipRGBColor); ipLineSymbol->put_Width(0.1); ipFillSymbol->put_Outline(ipLineSymbol); // Draw the polygon to the screen pMapControl->get_ActiveView(&ipActiveView); ipActiveView->get_ScreenDisplay(&ipScreenDisplay); ipScreenDisplay->StartDrawing(0, esriNoScreenCache); ipScreenDisplay->SetSymbol(ipSymbol); ipScreenDisplay->DrawPolygon(pPoly); ipScreenDisplay->FinishDrawing(); } // Creates a new shapefile containing 1 feature which is the buffer of // the selected features in the map control // // rwUnits - the width of the buffer in real world units (will be saved to feature in shapefile) // outPath - where shapefile will be saved // outName - name for new shapefile // ipSpatialReference - spatial reference of selected features // ipPolygon - polygon representing the union of the selected features // HRESULT CreateShapefile(double rwUnits, BSTR bstrOutPath, BSTR bstrOutName, ISpatialReference *ipSpatialReference, IPolygon *ipPolygon) { // Get a feature workspace for output shapefile IWorkspaceFactoryPtr ipWorkFact(CLSID_ShapefileWorkspaceFactory); IWorkspacePtr ipWork; ipWorkFact->OpenFromFile(bstrOutPath, 0, &ipWork); IFeatureWorkspacePtr ipFeatWork; ipFeatWork = ipWork; //QI // Set up fields IFieldsPtr ipFields(CLSID_Fields); IFieldsEditPtr ipFieldsEdit; ipFieldsEdit = ipFields; //QI IFieldPtr ipField(CLSID_Field); IFieldEditPtr ipFieldEdit; ipFieldEdit = ipField; //QI // Set up geometry field ipFieldEdit->put_Name(CComBSTR(L"Shape")); ipFieldEdit->put_Type(esriFieldTypeGeometry); IGeometryDefPtr ipGeoDef(CLSID_GeometryDef); IGeometryDefEditPtr ipGeoDefEdit; ipGeoDefEdit = ipGeoDef; //QI ipGeoDefEdit->put_GeometryType(esriGeometryPolygon); // Use spatial reference of the feature class you buffered ipGeoDefEdit->putref_SpatialReference(ipSpatialReference); ipFieldEdit->putref_GeometryDef(ipGeoDef); ipFieldsEdit->AddField(ipField); // Add field for scale value IFieldPtr ipScale(CLSID_Field); long len = 30; ipFieldEdit = ipScale; ipFieldEdit->put_Length(len); ipFieldEdit->put_Name(CComBSTR(L"Scale")); ipFieldEdit->put_Type(esriFieldTypeDouble); ipFieldsEdit->AddField(ipScale); // Create the shapefile IFeatureClassPtr ipShape; HRESULT hr = ipFeatWork->CreateFeatureClass(bstrOutName, ipFields, 0, 0, esriFTSimple, CComBSTR(L"Shape"), 0, &ipShape); if (FAILED(hr)) return hr; // Now insert the feature IFeatureCursorPtr ipFC; IFeatureBufferPtr ipFB; IFieldsPtr ipFieldsNew; // Get an insert cursor and a feature buffer ipShape->Insert(VARIANT_TRUE, &ipFC); ipShape->CreateFeatureBuffer(&ipFB); ipFB->get_Fields(&ipFieldsNew); // Get the column index in table for Scale field long indScale; ipFieldsNew->FindField(CComBSTR(L"Scale"), &indScale); // Update the feature buffer IGeometryPtr ipGeom = ipPolygon; ipFB->putref_Shape(ipGeom); ipFB->put_Value(indScale, CComVariant(rwUnits)); // Insert the feature, its OID is returned CComVariant ret; ipFC->InsertFeature(ipFB, &ret); // Flush the insert cursor ipFC->Flush(); return S_OK; }