EVOLUTION-MANAGER
Edit File: aolayer.cpp
/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements ArcObjects OGR layer. * Author: Ragi Yaser Burhum, ragi@burhum.com * ****************************************************************************** * Copyright (c) 2009, Ragi Yaser Burhum * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "ogr_ao.h" #include "cpl_conv.h" #include "cpl_string.h" #include "aoutils.h" #include <strstream> CPL_CVSID("$Id: aolayer.cpp 36332 2016-11-20 15:19:39Z rouault $"); /************************************************************************/ /* AOLayer() */ /************************************************************************/ AOLayer::AOLayer(): OGRLayer(),m_pFeatureDefn(NULL),m_pSRS(NULL), m_ipQF(CLSID_QueryFilter), m_pBuffer(NULL), m_bufferSize(0), m_suppressColumnMappingError(false),m_forceMulti(false) { } /************************************************************************/ /* ~AOLayer() */ /************************************************************************/ AOLayer::~AOLayer() { if (m_pFeatureDefn) m_pFeatureDefn->Release(); if (m_pSRS) m_pSRS->Release(); if (m_pBuffer) delete [] m_pBuffer; } /************************************************************************/ /* Initialize() */ /************************************************************************/ bool AOLayer::Initialize(ITable* pTable) { HRESULT hr; m_ipTable = pTable; CComBSTR temp; IDatasetPtr ipDataset = m_ipTable; if (FAILED(hr = ipDataset->get_Name(&temp))) return false; m_pFeatureDefn = new OGRFeatureDefn(CW2A(temp)); //Should I "new" an OGR smart pointer - sample says so, but it doesn't seem right //as long as we use the same compiler & settings in both the ogr build and this //driver, we should be OK m_pFeatureDefn->Reference(); IFeatureClassPtr ipFC = m_ipTable; VARIANT_BOOL hasOID = VARIANT_FALSE; ipFC->get_HasOID(&hasOID); if (hasOID == VARIANT_TRUE) { ipFC->get_OIDFieldName(&temp); m_strOIDFieldName = CW2A(temp); } if (FAILED(hr = ipFC->get_ShapeFieldName(&temp))) return AOErr(hr, "No shape field found!"); m_strShapeFieldName = CW2A(temp); IFieldsPtr ipFields; if (FAILED(hr = ipFC->get_Fields(&ipFields))) return AOErr(hr, "Fields not found!"); long shapeIndex = -1; if (FAILED(hr = ipFields->FindField(temp, &shapeIndex))) return AOErr(hr, "Shape field not found!"); IFieldPtr ipShapeField; if (FAILED(hr = ipFields->get_Field(shapeIndex, &ipShapeField))) return false; // Use GeometryDef to set OGR shapetype and Spatial Reference information // IGeometryDefPtr ipGeoDef; if (FAILED(hr = ipShapeField->get_GeometryDef(&ipGeoDef))) return false; OGRwkbGeometryType ogrGeoType; if (!AOToOGRGeometry(ipGeoDef, &ogrGeoType)) return false; m_pFeatureDefn->SetGeomType(ogrGeoType); if (wkbFlatten(ogrGeoType) == wkbMultiLineString || wkbFlatten(ogrGeoType) == wkbMultiPoint) m_forceMulti = true; // Mapping of Spatial Reference will be passive about errors // (it is possible we won't be able to map some ESRI-specific projections) esriGeometry::ISpatialReferencePtr ipSR = NULL; if (FAILED(hr = ipGeoDef->get_SpatialReference(&ipSR))) { AOErr(hr, "Failed Fetching ESRI spatial reference"); } else { if (!AOToOGRSpatialReference(ipSR, &m_pSRS)) { //report error, but be passive about it CPLError( CE_Warning, CPLE_AppDefined, "Failed Mapping ESRI Spatial Reference"); } } // Map fields // return AOToOGRFields(ipFields, m_pFeatureDefn, m_OGRFieldToESRIField); } /************************************************************************/ /* ResetReading() */ /************************************************************************/ void AOLayer::ResetReading() { HRESULT hr; if (FAILED(hr = m_ipTable->Search(m_ipQF, VARIANT_TRUE, &m_ipCursor))) AOErr(hr, "Error Executing Query"); } /************************************************************************/ /* GetTable() */ /************************************************************************/ HRESULT AOLayer::GetTable(ITable** ppTable) { return m_ipTable.QueryInterface(IID_ITable, ppTable); } /************************************************************************/ /* SetSpatialFilter() */ /************************************************************************/ void AOLayer::SetSpatialFilter( OGRGeometry* pOGRGeom ) { if (pOGRGeom == NULL) { SwitchToAttributeOnlyFilter(); return; } else { SwitchToSpatialFilter(); } esriGeometry::IGeometryPtr ipGeometry = NULL; if ((!OGRGeometryToAOGeometry(pOGRGeom, &ipGeometry)) || ipGeometry == NULL) { // Crap! we failed and there is no return error mechanism. // Report Error and dismiss ogr geometry to go back to at least a working // state CPLError( CE_Failure, CPLE_AppDefined, "Could not convert OGR spatial filter geometry to ArcObjecs one. Dismissing spatial filter!"); SwitchToAttributeOnlyFilter(); return; } // Set Spatial Reference on AO geometry IGeoDatasetPtr ipGeoDataset = m_ipTable; esriGeometry::ISpatialReferencePtr ipSR = NULL; ipGeoDataset->get_SpatialReference(&ipSR); ipGeometry->putref_SpatialReference(ipSR); ISpatialFilterPtr ipSF = m_ipQF; //QI should never fail because we called SwitchToSpatialFilter ipSF->putref_Geometry(ipGeometry); ResetReading(); } /************************************************************************/ /* SetSpatialFilterRect() */ /************************************************************************/ void AOLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) { SwitchToSpatialFilter(); IGeoDatasetPtr ipGD = m_ipTable; esriGeometry::IEnvelopePtr ipEnvelope(esriGeometry::CLSID_Envelope); esriGeometry::ISpatialReferencePtr ipSR; ipGD->get_SpatialReference(&ipSR); ipEnvelope->putref_SpatialReference(ipSR); ipEnvelope->PutCoords(dfMinX, dfMinY, dfMaxX, dfMaxY); ISpatialFilterPtr ipSF(m_ipQF); ipSF->putref_Geometry(ipEnvelope); ipSF->put_SpatialRel(esriSpatialRelIntersects); } /************************************************************************/ /* SwitchToAttributeOnlyFilter() */ /************************************************************************/ void AOLayer::SwitchToAttributeOnlyFilter() { ISpatialFilterPtr ipSF = m_ipQF; if (ipSF == NULL) return; //nothing to be done - it is an attribute filter //only need to preserve whereclause since it is the only thing we consume in this driver CComBSTR strWhereClause; m_ipQF->get_WhereClause(&strWhereClause); m_ipQF.CreateInstance(CLSID_QueryFilter); // will destroy old spatial filter if (strWhereClause.Length() > 0) { m_ipQF->put_WhereClause(strWhereClause); } } /************************************************************************/ /* SwitchToSpatialFilter() */ /************************************************************************/ void AOLayer::SwitchToSpatialFilter() { ISpatialFilterPtr ipSF = m_ipQF; if (!(ipSF == NULL)) return; //nothing to be done it is a spatial filter already //only need to preserve whereclause since it is the only thing we consume in this driver CComBSTR strWhereClause; m_ipQF->get_WhereClause(&strWhereClause); m_ipQF.CreateInstance(CLSID_SpatialFilter); // will destroy old query filter if (strWhereClause.Length() > 0) { m_ipQF->put_WhereClause(strWhereClause); } } /************************************************************************/ /* SetAttributeFilter() */ /************************************************************************/ OGRErr AOLayer::SetAttributeFilter( const char* pszQuery ) { if( pszQuery == NULL ) { CComBSTR whereClause(_T("")); m_ipQF->put_WhereClause(whereClause); } else { CComBSTR whereClause(pszQuery); m_ipQF->put_WhereClause(whereClause); } ResetReading(); return OGRERR_NONE; } /************************************************************************/ /* OGRFeatureFromAORow() */ /************************************************************************/ bool AOLayer::OGRFeatureFromAORow(IRow* pRow, OGRFeature** ppFeature) { HRESULT hr; OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn); ///////////////////////////////////////////////////////// // Translate OID // long oid = -1; if (FAILED(hr = pRow->get_OID(&oid))) { //this should never happen delete pOutFeature; return false; } pOutFeature->SetFID(oid); ///////////////////////////////////////////////////////// // Translate Geometry // IFeaturePtr ipFeature = pRow; esriGeometry::IGeometryPtr ipGeometry = NULL; if (FAILED(hr = ipFeature->get_Shape(&ipGeometry)) || ipGeometry == NULL) { delete pOutFeature; return AOErr(hr, "Failed retrieving shape from ArcObjects"); } OGRGeometry* pOGRGeo = NULL; if ((!AOGeometryToOGRGeometry(m_forceMulti, ipGeometry, m_pSRS, m_pBuffer, m_bufferSize, &pOGRGeo)) || pOGRGeo == NULL) { delete pOutFeature; return AOErr(hr, "Failed to translate ArcObjects Geometry to OGR Geometry"); } pOutFeature->SetGeometryDirectly(pOGRGeo); ////////////////////////////////////////////////////////// // Map fields // CComVariant val; size_t mappedFieldCount = m_OGRFieldToESRIField.size(); bool foundBadColumn = false; for (size_t i = 0; i < mappedFieldCount; ++i) { long index = m_OGRFieldToESRIField[i]; if (FAILED(hr = pRow->get_Value(index, &val))) { // this should not happen return AOErr(hr, "Failed retrieving row value"); } if (val.vt == VT_NULL) { continue; //leave as unset } // // NOTE: This switch statement needs to be kept in sync with AOToOGRGeometry // since we are only checking for types we mapped in that utility function switch (m_pFeatureDefn->GetFieldDefn(i)->GetType()) { case OFTInteger: { val.ChangeType(VT_I4); pOutFeature->SetField(i, val.intVal); } break; case OFTReal: { val.ChangeType(VT_R8); pOutFeature->SetField(i, val.dblVal); } break; case OFTString: { val.ChangeType(VT_BSTR); pOutFeature->SetField(i, CW2A(val.bstrVal)); } break; /* TODO: Need to get test dataset to implement these leave it as NULL for now case OFTBinary: { // Access as SafeArray with SafeArrayAccessData perhaps? } break; case OFTDateTime: { // Examine test data to figure out how to extract that } break; */ default: { if (!m_suppressColumnMappingError) { foundBadColumn = true; CPLError( CE_Warning, CPLE_AppDefined, "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.", oid, i, m_pFeatureDefn->GetFieldDefn(i)->GetType()); } } } } if (foundBadColumn) m_suppressColumnMappingError = true; *ppFeature = pOutFeature; return true; } /************************************************************************/ /* GetNextFeature() */ /************************************************************************/ OGRFeature* AOLayer::GetNextFeature() { while (1) //want to skip errors { if (m_ipCursor == NULL) return NULL; HRESULT hr; IRowPtr ipRow; if (FAILED(hr = m_ipCursor->NextRow(&ipRow))) { AOErr(hr, "Failed fetching features"); return NULL; } if (hr == S_FALSE || ipRow == NULL) { // It's OK, we are done fetching return NULL; } OGRFeature* pOGRFeature = NULL; if (!OGRFeatureFromAORow(ipRow, &pOGRFeature)) { long oid = -1; ipRow->get_OID(&oid); std::strstream msg; msg << "Failed translating ArcObjects row [" << oid << "] to OGR Feature"; AOErr(hr, msg.str()); //return NULL; continue; //skip feature } return pOGRFeature; } } /************************************************************************/ /* GetFeature() */ /************************************************************************/ OGRFeature *AOLayer::GetFeature( GIntBig oid ) { HRESULT hr; IRowPtr ipRow; if (FAILED(hr = m_ipTable->GetRow(oid, &ipRow))) { AOErr(hr, "Failed fetching row"); return NULL; } OGRFeature* pOGRFeature = NULL; if (!OGRFeatureFromAORow(ipRow, &pOGRFeature)) { AOErr(hr, "Failed translating ArcObjects row to OGR Feature"); return NULL; } return pOGRFeature; } /************************************************************************/ /* GetFeatureCount() */ /************************************************************************/ GIntBig AOLayer::GetFeatureCount( int bForce ) { HRESULT hr; long rowCount = -1; if (FAILED(hr = m_ipTable->RowCount(m_ipQF, &rowCount))) { AOErr(hr, "Failed calculating row count"); return rowCount; } return static_cast<int>(rowCount); } /************************************************************************/ /* GetExtent() */ /************************************************************************/ OGRErr AOLayer::GetExtent (OGREnvelope* psExtent, int bForce) { if (bForce) { return OGRLayer::GetExtent( psExtent, bForce ); } HRESULT hr; IGeoDatasetPtr ipGeoDataset = m_ipTable; esriGeometry::IEnvelopePtr ipEnv = NULL; if (FAILED(hr = ipGeoDataset->get_Extent(&ipEnv)) || ipEnv == NULL) { AOErr(hr, "Failed retrieving extent"); return OGRERR_FAILURE; } double temp; ipEnv->get_XMin(&temp); psExtent->MinX = temp; ipEnv->get_YMin(&temp); psExtent->MinY = temp; ipEnv->get_XMax(&temp); psExtent->MaxX = temp; ipEnv->get_YMax(&temp); psExtent->MaxY = temp; return OGRERR_NONE; } /************************************************************************/ /* TestCapability() */ /************************************************************************/ int AOLayer::TestCapability( const char* pszCap ) { if (EQUAL(pszCap,OLCRandomRead)) return TRUE; else if (EQUAL(pszCap,OLCFastFeatureCount)) return TRUE; else if (EQUAL(pszCap,OLCFastSpatialFilter)) return TRUE; else if (EQUAL(pszCap,OLCFastGetExtent)) return TRUE; #ifdef notdef // Have not implemented this yet else if (EQUAL(pszCap,OLCCreateField)) return FALSE; // Have not implemented this yet else if (EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCRandomWrite)) return FALSE; #endif else return FALSE; }