SimplePointCursorHelper.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. // // SimplePointCursorHelper.cpp : Implementation of CSimplePointCursorHelper #include "stdafx.h" #include "SimplePointVC.h" #include "SimplePointCursorHelper.h" ///////////////////////////////////////////////////////////////////////////// // CSimplePointCursorHelper STDMETHODIMP CSimplePointCursorHelper::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_ISimplePointCursorHelper }; for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++) { if (InlineIsEqualGUID(*arr[i],riid)) return S_OK; } return S_FALSE; } HRESULT CSimplePointCursorHelper::FinalConstruct() { HRESULT hr; m_lCurLineNum = 0; m_lOID = -1; hr = m_ipWorkPoint.CreateInstance(CLSID_Point); if (FAILED(hr)) return hr; ISpatialReferencePtr ipSR; hr = ipSR.CreateInstance(CLSID_UnknownCoordinateSystem); if (FAILED(hr)) return hr; hr = m_ipWorkPoint->putref_SpatialReference(ipSR); if (FAILED(hr)) return hr; return S_OK; } void CSimplePointCursorHelper::FinalRelease() { if (m_fDataFile) m_fDataFile.close(); SafeArrayUnaccessData(m_vFieldMap.parray); } // IPlugInCursorHelper STDMETHODIMP CSimplePointCursorHelper::NextRecord() { HRESULT hr; // We will take the line number in the file to be the OID of the feature, // keep track of this in the m_lCurLineNum variable. // // E_FAIL is returned if there are no more records // If we are searching by OID, skip to the correct line if (m_lOID != -1) { for ( ; m_lCurLineNum < m_lOID && (!m_fDataFile.eof()); m_lCurLineNum++ ) { m_fDataFile.getline(m_sCurrentRow, c_iMaxRowLen); } if (m_lCurLineNum != m_lOID) // i.e. EOF before OID found { m_sCurrentRow[0] = '\0'; return E_FAIL; } else return S_OK; } // Read current row if (!m_fDataFile.eof()) { m_fDataFile.getline(m_sCurrentRow, c_iMaxRowLen); m_lCurLineNum++; // if its a blank line then we are at the end, so return failure if (m_sCurrentRow[0] == '\0') return E_FAIL; } else { m_sCurrentRow[0] = '\0'; return E_FAIL; } // If we are finding by envelope, check the current record // if its not in the envelope, make a recursive call to move on to the next record if (m_ipQueryEnv != NULL) { hr = QueryShape(m_ipWorkPoint); if (FAILED(hr)) return hr; IRelationalOperatorPtr ipRelOp = m_ipWorkPoint; if (ipRelOp == NULL) return E_FAIL; VARIANT_BOOL bWithin; hr = ipRelOp->Within(m_ipQueryEnv, &bWithin); if (FAILED(hr)) return hr; if (!bWithin) // current feature is not within the query envelope { hr = NextRecord(); if (FAILED(hr)) return hr; } } return S_OK; } STDMETHODIMP CSimplePointCursorHelper::IsFinished(VARIANT_BOOL *finished) { if (! finished) return E_POINTER; if ( (m_fDataFile.eof()) && (m_sCurrentRow[0] == '\0') ) *finished = VARIANT_TRUE; else *finished = VARIANT_FALSE; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::QueryValues(IRowBuffer *Row, long *OID) { HRESULT hr; if (! OID || ! Row) return E_POINTER; // At end of file, return -1 if (m_sCurrentRow[0] == '\0') { *OID = -1; return S_OK; } // First, parse the attribute out of the current row. // We know this data source has just one attribute, which is one char wide. char sAtt[2]; strncpy(sAtt, m_sCurrentRow + 12, 1); sAtt[1] = '\0'; // add null terminator CComVariant vAtt = sAtt; if (vAtt.vt == VT_ERROR) return E_FAIL; // Check field map has same number of elements as there are fields IFieldsPtr ipFields; hr = Row->get_Fields(&ipFields); if (FAILED(hr)) return hr; long lNumFields; hr = ipFields->get_FieldCount(&lNumFields); if (FAILED(hr)) return hr; long lUBound, lLBound; hr = SafeArrayGetLBound(m_vFieldMap.parray, 1, &lLBound); if (FAILED(hr)) return hr; hr = SafeArrayGetUBound(m_vFieldMap.parray, 1, &lUBound); if (FAILED(hr)) return hr; if ( (lUBound - lLBound) + 1 != lNumFields) { AtlReportError(CLSID_SimplePointCursorHelper, L"SimplePoint Data Source: Unexepected situation: Number of elements in Fieldmap does not match number of fields", IID_IPlugInCursorHelper, E_FAIL); return E_FAIL; } // For each field, copy its value into the row object. // (don't copy shape, object ID or where the field map indicates no values required) // Note, although we know there is only one attribute in the data source, // this loop has been coded generically in case support needs to be added for more attributes long i; esriFieldType eFieldType; for (i=0; i < lNumFields; i++) { IFieldPtr ipField; hr = ipFields->get_Field(i, &ipField); if (FAILED(hr)) return hr; hr = ipField->get_Type(&eFieldType); if (FAILED(hr)) return hr; if (eFieldType != esriFieldTypeGeometry && eFieldType != esriFieldTypeOID && m_lFieldMap[i] != -1) { hr = Row->put_Value(i, vAtt); if (FAILED(hr)) return hr; } } // Return value is taken as the OID. // Use the line number (stream will currently be pointing at next line) *OID = m_lCurLineNum; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::QueryShape(IGeometry *pGeometry) { HRESULT hr; if (! pGeometry) return E_POINTER; // If there is no current row, set the geometry to be empty if (m_sCurrentRow[0] == '\0') { hr = pGeometry->SetEmpty(); if (FAILED(hr)) return hr; return S_OK; } double x,y; char* end; char buf[6]; // Parse the X and Y values out of the current row and into the geometry x = strtod(strncpy(buf, m_sCurrentRow, 6),&end); y = strtod(strncpy(buf, m_sCurrentRow + 6, 6),&end); IPointPtr ipPoint = pGeometry; if (ipPoint == NULL) return E_FAIL; hr = ipPoint->PutCoords(x,y); if (FAILED(hr)) return hr; // Note - in our case there is no need to handle the strictSearch test for a cursor // created with FetchByEnvelope. We have already tested that the feature is within // the envelope on the NextRecord call, so there is no possibility of the test // failing here. return S_OK; } // IPlugInFastQueryValues STDMETHODIMP CSimplePointCursorHelper::FastQueryValues(tagFieldValue *Values) { HRESULT hr; USES_CONVERSION; if (! Values) return E_POINTER; // If at end of file, return S_FALSE if (m_sCurrentRow[0] == '\0') return S_FALSE; // First, parse the attribute out of the current row. // We know this data source has just one attribute, which is one char wide. char sAtt[2]; strncpy(sAtt, m_sCurrentRow + 12, 1); sAtt[1] = '\0'; // add null terminator // For this sample, there is just one attribute field to handle, // but for demonstration, more generic code follows: if (m_ipFields == NULL) return E_FAIL; IFieldPtr ipField; esriFieldType eFieldType; long lFieldCount; hr = m_ipFields->get_FieldCount(&lFieldCount); if (FAILED(hr)) return hr; for (long lFieldIndex = 0; lFieldIndex < lFieldCount; lFieldIndex++) { // if the supplied value in the field map is -1 then this means // that we don't have to populate it with data. if (m_lFieldMap[lFieldIndex] == -1) continue; // If the field map indicator is not -1, // it is the location in the FieldValue array that we should place our data. // For instance field map array is 0,1,2,3,4 with values of 1,0,-1,4,3 // the returned field order (that of the Values array) would be: // Field1,Field0,Field2=NULL,Field4,Field3 long lFieldLoc = m_lFieldMap[lFieldIndex]; hr = m_ipFields->get_Field(lFieldIndex, &ipField); if (FAILED(hr)) return hr; hr = ipField->get_Type(&eFieldType); if (FAILED(hr)) return hr; switch (eFieldType) { case esriFieldTypeSmallInteger: Values[lFieldLoc].m_value.vt = VT_I2; // following code is commented out - it just indicates the kind // of code that would go in if there were lots of different attributes in // the data source // field value = GetFieldValue(lFieldIndex); // Values[lngFieldLoc].m_value.iVal = field value; break; case esriFieldTypeInteger: Values[lFieldLoc].m_value.vt = VT_I4; // field value = GetFieldValue(lFieldIndex); // Values[lngFieldLoc].m_value.lVal = field value; break; case esriFieldTypeSingle: Values[lFieldLoc].m_value.vt = VT_R4; // field value = GetFieldValue(lFieldIndex); // Values[lngFieldLoc].m_value.fltVal = field value; break; case esriFieldTypeDouble: Values[lFieldLoc].m_value.vt = VT_R8; // field value = GetFieldValue(lFieldIndex); // Values[lngFieldLoc].m_value.dblVal = field value; break; case esriFieldTypeDate: Values[lFieldLoc].m_value.vt = VT_DATE; // field value = GetFieldValue(lFieldIndex); // Values[lngFieldLoc].m_value.date = some value; break; case esriFieldTypeString: // if the string already exists then just reallocate it otherwise create if (Values[lFieldLoc].m_value.bstrVal != NULL) { INT success = ::SysReAllocString(&(Values[lFieldLoc].m_value.bstrVal), A2COLE(sAtt) ); if (!success) return E_FAIL; } else { Values[lFieldLoc].m_value.bstrVal = ::SysAllocString(A2COLE(sAtt)); if (Values[lFieldLoc].m_value.bstrVal == NULL) return E_FAIL; } Values[lFieldLoc].m_value.vt = VT_BSTR; break; case esriFieldTypeGeometry: // we should never copy the shape field, as QueryShape deals with that Values[lFieldLoc].m_value.vt = VT_NULL; Values[lFieldLoc].m_value.punkVal = 0; break; case esriFieldTypeOID: Values[lFieldLoc].m_value.vt = VT_I4; Values[lFieldLoc].m_value.lVal = m_lCurLineNum; case esriFieldTypeBlob: break; } } return S_OK; } // ISimplePointCursorHelper methods STDMETHODIMP CSimplePointCursorHelper::put_FilePath(BSTR newVal) { HRESULT hr; USES_CONVERSION; m_sFilePath = newVal; // Open the text file for reading m_fDataFile.open(OLE2CA(m_sFilePath)); if (!m_fDataFile) { CComBSTR sError(L"Could not open data file for reading: "); sError.Append(m_sFilePath); AtlReportError(CLSID_SimplePointCursorHelper, sError, IID_IPlugInCursorHelper, E_FAIL); return E_FAIL; } // First record should be fetched on creation hr = NextRecord(); if (FAILED(hr)) return hr; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::put_FieldMap(VARIANT fieldMap) { m_vFieldMap = fieldMap; HRESULT hr = SafeArrayAccessData(m_vFieldMap.parray, (void HUGEP**)&m_lFieldMap); if (FAILED(hr)) return hr; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::put_OID(long lOID) { m_lOID = lOID; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::putref_QueryEnvelope(IEnvelope* pEnvelope) { m_ipQueryEnv = pEnvelope; return S_OK; } STDMETHODIMP CSimplePointCursorHelper::putref_Fields(IFields* pFields) { // This property exists so we can pass in the fields, to prevent FastQueryValues // having to fetch them each time. // We know that the fields will stay constant over the lifetime of the cursor. m_ipFields = pFields; return S_OK; }