Changeset 22051


Ignore:
Timestamp:
Mar 27, 2011 1:33:57 PM (6 years ago)
Author:
rouault
Message:

GFT: add creation, geometry and filters support

Location:
trunk/gdal/ogr/ogrsf_frmts/gft
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/gdal/ogr/ogrsf_frmts/gft/ogr_gft.h

    r22047 r22051  
    5252    int                nFeatureCount;
    5353
     54    int                iGeometryField;
     55    int                iLatitude;
     56    int                iLongitude;
     57
    5458    OGRFeature *       GetNextRawFeature();
    5559
     
    6266    std::vector<CPLString> aosRows;
    6367
     68    int                bHasTriedCreateTable;
     69    void               CreateTableIfNecessary();
     70
     71    CPLString          osTransaction;
     72    int                bInTransaction;
     73
     74    CPLString           osWHERE;
     75    CPLString           osQuery;
     76
     77    void                BuildWhere(void);
     78
    6479  public:
    6580                        OGRGFTLayer(OGRGFTDataSource* poDS,
    6681                                    const char* pszTableName,
    67                                     const char* pszTableId);
     82                                    const char* pszTableId = "");
    6883                        ~OGRGFTLayer();
    6984
    7085    virtual const char *        GetName() { return osTableName.c_str(); }
    71     virtual OGRwkbGeometryType GetGeomType() { return wkbPoint; }
     86    //virtual OGRwkbGeometryType GetGeomType() { return wkbUnknown; }
    7287
    7388    virtual void                ResetReading();
    7489    virtual OGRFeature *        GetNextFeature();
     90    virtual OGRFeature *        GetFeature( long nFID );
    7591
    7692    virtual OGRFeatureDefn *    GetLayerDefn();
     
    8298    virtual int         GetFeatureCount( int bForce = TRUE );
    8399
     100    virtual OGRErr      CreateField( OGRFieldDefn *poField,
     101                                     int bApproxOK = TRUE );
     102    OGRErr              CreateFeature( OGRFeature *poFeature );
     103
     104    virtual OGRErr      StartTransaction();
     105    virtual OGRErr      CommitTransaction();
     106    virtual OGRErr      RollbackTransaction();
     107
     108    virtual void        SetSpatialFilter( OGRGeometry * );
     109    virtual OGRErr      SetAttributeFilter( const char * );
     110
     111    const CPLString&            GetTableId() const { return osTableId; }
    84112};
    85113
     
    95123    int                 nLayers;
    96124
     125    int                 bReadWrite;
     126
     127    int                 bUseHTTPS;
    97128    CPLString           osAuth;
    98129    int                 FetchAuth(const char* pszEmail, const char* pszPassword);
     130
     131    void                DeleteLayer( const char *pszLayerName );
    99132
    100133  public:
     
    112145    virtual int                 TestCapability( const char * );
    113146
     147    virtual OGRLayer   *CreateLayer( const char *pszName,
     148                                     OGRSpatialReference *poSpatialRef = NULL,
     149                                     OGRwkbGeometryType eGType = wkbUnknown,
     150                                     char ** papszOptions = NULL );
     151    virtual OGRErr      DeleteLayer(int);
     152
    114153    const CPLString&            GetAuth() const { return osAuth; }
     154    const char*                 GetAPIURL() const;
     155    int                         IsReadWrite() const { return bReadWrite; }
     156    char**                      AddHTTPOptions(char** papszOptions = NULL);
    115157};
    116158
  • trunk/gdal/ogr/ogrsf_frmts/gft/ogrgftdatasource.cpp

    r22047 r22051  
    4646
    4747    pszName = NULL;
     48
     49    bReadWrite = FALSE;
     50    bUseHTTPS = FALSE;
    4851}
    4952
     
    5962    CPLFree( papoLayers );
    6063
     64    char** papszOptions = CSLAddString(NULL, CPLSPrintf("CLOSE_PERSISTENT=GFT:%p", this));
     65    CPLHTTPFetch( GetAPIURL(), papszOptions);
     66    CSLDestroy(papszOptions);
     67
    6168    CPLFree( pszName );
    6269}
     
    6976
    7077{
    71     return FALSE;
     78    if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) )
     79        return TRUE;
     80    else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) )
     81        return TRUE;
     82    else
     83        return FALSE;
    7284}
    7385
     
    147159        return FALSE;
    148160
     161    bReadWrite = bUpdateIn;
     162
    149163    pszName = CPLStrdup( pszFilename );
    150164
    151165    const char* pszEmail = CPLGetConfigOption("GFT_EMAIL", NULL);
    152166    const char* pszPassword = CPLGetConfigOption("GFT_PASSWORD", NULL);
    153 
    154     if (pszEmail == NULL)
    155     {
    156         CPLError(CE_Failure, CPLE_AppDefined,
    157                  "GFT needs a Google account email to be specified with GFT_EMAIL configuration option");
    158         return FALSE;
    159     }
    160 
    161     if (pszPassword == NULL)
    162     {
    163         CPLError(CE_Failure, CPLE_AppDefined,
    164                  "GFT needs a Google account password to be specified with GFT_PASSWORD configuration option");
    165         return FALSE;
    166     }
    167 
    168     if (!FetchAuth(pszEmail, pszPassword))
    169         return FALSE;
     167    const char* pszTables = strstr(pszFilename, "tables=");
     168
     169    bUseHTTPS = strstr(pszFilename, "protocol=https") != NULL;
     170
     171    if (pszEmail == NULL || pszPassword == NULL)
     172    {
     173        if (pszTables == NULL)
     174        {
     175            CPLError(CE_Failure, CPLE_AppDefined,
     176                    "Unauthenticated access requires explicit tables= parameter");
     177            return FALSE;
     178        }
     179    }
     180    else if (!FetchAuth(pszEmail, pszPassword))
     181    {
     182        CPLError(CE_Failure, CPLE_AppDefined, "Authentication failed");
     183        return FALSE;
     184    }
     185
     186    if (pszTables)
     187    {
     188        CPLString osTables(pszTables + 7);
     189        const char* pszSpace = strchr(pszTables + 7, ' ');
     190        if (pszSpace)
     191            osTables.resize(pszSpace - (pszTables + 7));
     192
     193        char** papszTables = CSLTokenizeString2(osTables, ",", 0);
     194        for(int i=0;papszTables && papszTables[i];i++)
     195        {
     196            papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
     197            papoLayers[nLayers ++] = new OGRGFTLayer(this, papszTables[i], papszTables[i]);
     198        }
     199        CSLDestroy(papszTables);
     200        return TRUE;
     201    }
    170202
    171203    /* Get list of tables */
    172     char** papszOptions = NULL;
    173     papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: GoogleLogin auth=%s", osAuth.c_str()));
    174     papszOptions = CSLAddString(papszOptions, "POSTFIELDS=sql=SHOW TABLES");
    175     CPLHTTPResult * psResult = CPLHTTPFetch( "https://www.google.com/fusiontables/api/query", papszOptions);
     204    char** papszOptions = CSLAddString(AddHTTPOptions(), "POSTFIELDS=sql=SHOW TABLES");
     205    CPLHTTPResult * psResult = CPLHTTPFetch( GetAPIURL(), papszOptions);
    176206    CSLDestroy(papszOptions);
    177207    papszOptions = NULL;
     
    199229        if (CSLCount(papszTokens) == 2)
    200230        {
     231            CPLString osTableId(papszTokens[0]);
     232            CPLString osLayerName(papszTokens[1]);
     233            for(int i=0;i<nLayers;i++)
     234            {
     235                if (strcmp(papoLayers[i]->GetName(), osLayerName) == 0)
     236                {
     237                    osLayerName += " (";
     238                    osLayerName += osTableId;
     239                    osLayerName += ")";
     240                    break;
     241                }
     242            }
    201243            papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
    202             papoLayers[nLayers ++] = new OGRGFTLayer(this, papszTokens[1], papszTokens[0]);
     244            papoLayers[nLayers ++] = new OGRGFTLayer(this, osLayerName, osTableId);
    203245        }
    204246        CSLDestroy(papszTokens);
     
    211253    return TRUE;
    212254}
     255
     256/************************************************************************/
     257/*                            GetAPIURL()                               */
     258/************************************************************************/
     259
     260const char*  OGRGFTDataSource::GetAPIURL() const
     261{
     262    if (bUseHTTPS)
     263        return "https://www.google.com/fusiontables/api/query";
     264    else
     265        return "http://www.google.com/fusiontables/api/query";
     266}
     267
     268/************************************************************************/
     269/*                           CreateLayer()                              */
     270/************************************************************************/
     271
     272OGRLayer   *OGRGFTDataSource::CreateLayer( const char *pszName,
     273                                           OGRSpatialReference *poSpatialRef,
     274                                           OGRwkbGeometryType eGType,
     275                                           char ** papszOptions )
     276{
     277    if (!bReadWrite)
     278    {
     279        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
     280        return NULL;
     281    }
     282
     283    if (osAuth.size() == 0)
     284    {
     285        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
     286        return NULL;
     287    }
     288
     289/* -------------------------------------------------------------------- */
     290/*      Do we already have this layer?  If so, should we blow it        */
     291/*      away?                                                           */
     292/* -------------------------------------------------------------------- */
     293    int iLayer;
     294
     295    for( iLayer = 0; iLayer < nLayers; iLayer++ )
     296    {
     297        if( EQUAL(pszName,papoLayers[iLayer]->GetName()) )
     298        {
     299            if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
     300                && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
     301            {
     302                DeleteLayer( pszName );
     303                break;
     304            }
     305            else
     306            {
     307                CPLError( CE_Failure, CPLE_AppDefined,
     308                          "Layer %s already exists, CreateLayer failed.\n"
     309                          "Use the layer creation option OVERWRITE=YES to "
     310                          "replace it.",
     311                          pszName );
     312                return NULL;
     313            }
     314        }
     315    }
     316
     317    OGRLayer* poLayer = new OGRGFTLayer(this, pszName);
     318    papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
     319    papoLayers[nLayers ++] = poLayer;
     320    return poLayer;
     321}
     322
     323/************************************************************************/
     324/*                            DeleteLayer()                             */
     325/************************************************************************/
     326
     327void OGRGFTDataSource::DeleteLayer( const char *pszLayerName )
     328
     329{
     330    int iLayer;
     331
     332/* -------------------------------------------------------------------- */
     333/*      Try to find layer.                                              */
     334/* -------------------------------------------------------------------- */
     335    for( iLayer = 0; iLayer < nLayers; iLayer++ )
     336    {
     337        if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
     338            break;
     339    }
     340
     341    if( iLayer == nLayers )
     342    {
     343        CPLError( CE_Failure, CPLE_AppDefined,
     344                  "Attempt to delete layer '%s', but this layer is not known to OGR.",
     345                  pszLayerName );
     346        return;
     347    }
     348
     349    DeleteLayer(iLayer);
     350}
     351
     352/************************************************************************/
     353/*                            DeleteLayer()                             */
     354/************************************************************************/
     355
     356OGRErr OGRGFTDataSource::DeleteLayer(int iLayer)
     357{
     358    if (!bReadWrite)
     359    {
     360        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
     361        return OGRERR_FAILURE;
     362    }
     363
     364    if (osAuth.size() == 0)
     365    {
     366        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
     367        return OGRERR_FAILURE;
     368    }
     369
     370    if( iLayer < 0 || iLayer >= nLayers )
     371    {
     372        CPLError( CE_Failure, CPLE_AppDefined,
     373                  "Layer %d not in legal range of 0 to %d.",
     374                  iLayer, nLayers-1 );
     375        return OGRERR_FAILURE;
     376    }
     377
     378    CPLString osTableId = ((OGRGFTLayer*)papoLayers[iLayer])->GetTableId();
     379    CPLString osLayerName = GetLayer(iLayer)->GetName();
     380
     381/* -------------------------------------------------------------------- */
     382/*      Blow away our OGR structures related to the layer.  This is     */
     383/*      pretty dangerous if anything has a reference to this layer!     */
     384/* -------------------------------------------------------------------- */
     385    CPLDebug( "GFT", "DeleteLayer(%s)", osLayerName.c_str() );
     386
     387    delete papoLayers[iLayer];
     388    memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
     389             sizeof(void *) * (nLayers - iLayer - 1) );
     390    nLayers--;
     391
     392/* -------------------------------------------------------------------- */
     393/*      Remove from the database.                                       */
     394/* -------------------------------------------------------------------- */
     395
     396    CPLString osPOST("POSTFIELDS=sql=DROP TABLE ");
     397    osPOST += osTableId;
     398
     399    char** papszOptions = CSLAddString(AddHTTPOptions(), osPOST);
     400    CPLHTTPResult* psResult = CPLHTTPFetch( GetAPIURL(), papszOptions);
     401    CSLDestroy(papszOptions);
     402    papszOptions = NULL;
     403
     404    if (psResult == NULL)
     405    {
     406        CPLError(CE_Failure, CPLE_AppDefined, "Table deletion failed");
     407        return OGRERR_FAILURE;
     408    }
     409
     410    char* pszLine = (char*) psResult->pabyData;
     411    if (pszLine == NULL ||
     412        !EQUALN(pszLine, "OK", 2) ||
     413        psResult->pszErrBuf != NULL)
     414    {
     415        CPLError(CE_Failure, CPLE_AppDefined, "Table deletion failed");
     416        CPLHTTPDestroyResult(psResult);
     417        return OGRERR_FAILURE;
     418    }
     419
     420    CPLHTTPDestroyResult(psResult);
     421
     422    return OGRERR_NONE;
     423}
     424
     425/************************************************************************/
     426/*                          AddHTTPOptions()                            */
     427/************************************************************************/
     428
     429char** OGRGFTDataSource::AddHTTPOptions(char** papszOptions)
     430{
     431    papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: GoogleLogin auth=%s", osAuth.c_str()));
     432    return CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=GFT:%p", this));
     433}
  • trunk/gdal/ogr/ogrsf_frmts/gft/ogrgftdriver.cpp

    r22047 r22051  
    3131#include "cpl_conv.h"
    3232
    33 // g++ -g -Wall -fPIC -shared -o ogr_GFT.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gft ogr/ogrsf_frmts/gft/*.c*
     33// g++ -g -Wall -fPIC -shared -o ogr_GFT.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gft ogr/ogrsf_frmts/gft/*.c* -L. -lgdal
    3434
    3535/* http://code.google.com/intl/fr/apis/fusiontables/docs/developers_reference.html */
  • trunk/gdal/ogr/ogrsf_frmts/gft/ogrgftlayer.cpp

    r22047 r22051  
    3737CPL_CVSID("$Id$");
    3838
     39#define MAX_FEATURES_FETCH  500
     40
    3941/************************************************************************/
    4042/*                            OGRGFTLayer()                             */
     
    5961    nOffset = 0;
    6062    bEOF = FALSE;
     63
     64    bHasTriedCreateTable = FALSE;
     65    bInTransaction = FALSE;
     66
     67    iLatitude = iLongitude = -1;
     68    iGeometryField = -1;
    6169}
    6270
     
    6876
    6977{
     78    CreateTableIfNecessary();
     79
    7080    if( poSRS != NULL )
    7181        poSRS->Release();
     
    221231    char** papszTokens = OGRGFTCSVSplitLine(aosRows[nNextFID - nOffset], ',');
    222232    int nTokens = CSLCount(papszTokens);
    223     if (nTokens == poFeatureDefn->GetFieldCount())
    224     {
     233    CPLString osFID;
     234    if (nTokens == poFeatureDefn->GetFieldCount() + 1)
     235    {
     236        osFID = papszTokens[0];
    225237        for(int i=0;i<poFeatureDefn->GetFieldCount();i++)
    226238        {
    227             poFeature->SetField(i, papszTokens[i]);
     239            const char* pszVal = papszTokens[i+1];
     240            if (pszVal[0])
     241            {
     242                poFeature->SetField(i, pszVal);
     243
     244                if (i == iGeometryField && i != iLatitude)
     245                {
     246                    if (pszVal[0] == '-' || (pszVal[0] >= '0' && pszVal[0] <= '9'))
     247                    {
     248                        char** papszLatLon = CSLTokenizeString2(pszVal, " ,", 0);
     249                        if (CSLCount(papszLatLon) == 2)
     250                        {
     251                            OGRPoint* poPoint = new OGRPoint(atof( papszLatLon[1]),
     252                                                            atof( papszLatLon[0]));
     253                            poPoint->assignSpatialReference(poSRS);
     254                            poFeature->SetGeometryDirectly(poPoint);
     255                        }
     256                        CSLDestroy(papszLatLon);
     257                    }
     258                    else
     259                    {
     260                        /* TODO : parse KML */
     261                    }
     262                }
     263            }
     264        }
     265
     266        if (iLatitude >= 0 && iLongitude >= 0 &&
     267            papszTokens[iLatitude+1][0] && papszTokens[iLongitude+1][0])
     268        {
     269            OGRPoint* poPoint = new OGRPoint(atof( papszTokens[iLongitude+1] ),
     270                                             atof( papszTokens[iLatitude+1] ));
     271            poPoint->assignSpatialReference(poSRS);
     272            poFeature->SetGeometryDirectly(poPoint);
    228273        }
    229274    }
     
    234279    CSLDestroy(papszTokens);
    235280
    236     poFeature->SetFID(nNextFID ++);
     281    /*int nFID = atoi(osFID);
     282    if (strcmp(CPLSPrintf("%d", nFID), osFID.c_str()) == 0)
     283        poFeature->SetFID(nFID);
     284    else*/
     285        poFeature->SetFID(nNextFID);
     286
     287    nNextFID ++;
    237288
    238289    return poFeature;
     
    246297
    247298{
     299    if( EQUAL(pszCap,OLCRandomRead) )
     300        return TRUE;
     301
     302    else if( EQUAL(pszCap,OLCSequentialWrite)
     303             /*|| EQUAL(pszCap,OLCRandomWrite)  */)
     304        return poDS->IsReadWrite();
     305
     306    else if( EQUAL(pszCap,OLCCreateField) )
     307        return poDS->IsReadWrite();
     308
     309    else if( EQUAL(pszCap, OLCTransactions) )
     310        return poDS->IsReadWrite();
     311
    248312    return FALSE;
    249313}
     
    269333    poFeatureDefn = new OGRFeatureDefn( osTableName );
    270334    poFeatureDefn->Reference();
    271     poFeatureDefn->SetGeomType( wkbPoint );
    272 
    273     char** papszOptions = NULL;
     335
    274336    const CPLString& osAuth = poDS->GetAuth();
    275     papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: GoogleLogin auth=%s", osAuth.c_str()));
    276     papszOptions = CSLAddString(papszOptions, CPLSPrintf("POSTFIELDS=sql=DESCRIBE %s", osTableId.c_str()));
    277     CPLHTTPResult * psResult = CPLHTTPFetch( "https://www.google.com/fusiontables/api/query", papszOptions);
    278     CSLDestroy(papszOptions);
    279     papszOptions = NULL;
    280 
    281     if (psResult == NULL)
    282         return FALSE;
    283 
    284     char* pszLine = (char*) psResult->pabyData;
    285     if (pszLine == NULL ||
    286         strncmp(pszLine, "column id,name,type", strlen("column id,name,type")) != 0)
    287     {
     337    if (osAuth.size())
     338    {
     339        char** papszOptions = CSLAddString(poDS->AddHTTPOptions(),
     340            CPLSPrintf("POSTFIELDS=sql=DESCRIBE %s", osTableId.c_str()));
     341        CPLHTTPResult* psResult = CPLHTTPFetch( poDS->GetAPIURL(), papszOptions);
     342        CSLDestroy(papszOptions);
     343        papszOptions = NULL;
     344
     345        if (psResult == NULL)
     346            return FALSE;
     347
     348        char* pszLine = (char*) psResult->pabyData;
     349        if (pszLine == NULL ||
     350            psResult->pszErrBuf != NULL ||
     351            strncmp(pszLine, "column id,name,type", strlen("column id,name,type")) != 0)
     352        {
     353            CPLHTTPDestroyResult(psResult);
     354            return FALSE;
     355        }
     356
     357        pszLine = OGRGFTLayerGotoNextLine(pszLine);
     358        while(pszLine != NULL && *pszLine != 0)
     359        {
     360            char* pszNextLine = OGRGFTLayerGotoNextLine(pszLine);
     361            if (pszNextLine)
     362                pszNextLine[-1] = 0;
     363
     364            char** papszTokens = CSLTokenizeString2(pszLine, ",", 0);
     365            if (CSLCount(papszTokens) == 3)
     366            {
     367                //CPLDebug("GFT", "%s %s %s", papszTokens[0], papszTokens[1], papszTokens[2]);
     368                OGRFieldType eType = OFTString;
     369                if (EQUAL(papszTokens[2], "number"))
     370                    eType = OFTReal;
     371                else if (EQUAL(papszTokens[2], "datetime"))
     372                    eType = OFTDateTime;
     373
     374                if (EQUAL(papszTokens[2], "location"))
     375                {
     376                    iGeometryField = poFeatureDefn->GetFieldCount();
     377                }
     378                OGRFieldDefn oFieldDefn(papszTokens[1], eType);
     379                poFeatureDefn->AddFieldDefn(&oFieldDefn);
     380            }
     381            CSLDestroy(papszTokens);
     382
     383            pszLine = pszNextLine;
     384        }
     385
    288386        CPLHTTPDestroyResult(psResult);
    289         return FALSE;
    290     }
    291 
    292     pszLine = OGRGFTLayerGotoNextLine(pszLine);
    293     while(pszLine != NULL && *pszLine != 0)
    294     {
     387    }
     388    else
     389    {
     390        /* http://code.google.com/intl/fr/apis/fusiontables/docs/developers_guide.html#Exploring states */
     391        /* that DESCRIBE should work on public tables without authentication, but it is not true... */
     392        CPLString osURL = CPLSPrintf("%s?sql=SELECT+*+FROM+%s+OFFSET+0+LIMIT+1&hdrs=true",
     393                                 poDS->GetAPIURL(), osTableId.c_str());
     394        char** papszOptions = poDS->AddHTTPOptions();
     395        CPLHTTPResult* psResult = CPLHTTPFetch( osURL, papszOptions );
     396        CSLDestroy(papszOptions);
     397        papszOptions = NULL;
     398
     399        if (psResult == NULL)
     400            return FALSE;
     401
     402        char* pszLine = (char*) psResult->pabyData;
     403        if (pszLine == NULL || psResult->pszErrBuf != NULL)
     404        {
     405            CPLHTTPDestroyResult(psResult);
     406            return FALSE;
     407        }
    295408        char* pszNextLine = OGRGFTLayerGotoNextLine(pszLine);
    296409        if (pszNextLine)
     
    298411
    299412        char** papszTokens = CSLTokenizeString2(pszLine, ",", 0);
    300         if (CSLCount(papszTokens) == 3)
    301         {
    302             //printf("%s %s %s\n", papszTokens[0], papszTokens[1], papszTokens[2]);
    303             OGRFieldType eType = OFTString;
    304             if (EQUAL(papszTokens[2], "number") || EQUAL(papszTokens[2], "location"))
    305                 eType = OFTReal;
    306             else if (EQUAL(papszTokens[2], "datetime"))
    307                 eType = OFTDateTime;
    308             OGRFieldDefn oFieldDefn(papszTokens[1], eType);
     413        for(int i=0;papszTokens && papszTokens[i];i++)
     414        {
     415            OGRFieldDefn oFieldDefn(papszTokens[i], OFTString);
    309416            poFeatureDefn->AddFieldDefn(&oFieldDefn);
    310417        }
    311418        CSLDestroy(papszTokens);
    312419
    313         pszLine = pszNextLine;
    314     }
    315 
    316     CPLHTTPDestroyResult(psResult);
     420        CPLHTTPDestroyResult(psResult);
     421    }
     422
     423    for(int i=0;i<poFeatureDefn->GetFieldCount();i++)
     424    {
     425        const char* pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     426        if (EQUAL(pszName, "latitude") || EQUAL(pszName, "lat"))
     427            iLatitude = i;
     428        else if (EQUAL(pszName, "longitude") || EQUAL(pszName, "lon") || EQUAL(pszName, "long"))
     429            iLongitude = i;
     430    }
     431
     432    if (iLatitude >= 0 && iLongitude >= 0)
     433    {
     434        iGeometryField = iLatitude;
     435        poFeatureDefn->SetGeomType( wkbPoint );
     436    }
    317437
    318438    return TRUE;
     
    327447    aosRows.resize(0);
    328448
    329     char** papszOptions = NULL;
    330     const CPLString& osAuth = poDS->GetAuth();
    331     papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: GoogleLogin auth=%s", osAuth.c_str()));
    332 
    333     CPLString osURL = CPLSPrintf("https://www.google.com/fusiontables/api/query?sql=SELECT+*+FROM+%s+OFFSET+%d+LIMIT+500&hdrs=false",
    334                                  osTableId.c_str(), nOffset);
    335 
     449    CPLString osGetColumns("SELECT ROWID");
     450    for(int i=0;i<poFeatureDefn->GetFieldCount();i++)
     451    {
     452        osGetColumns += ",'";
     453        osGetColumns += poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     454        osGetColumns += "'";
     455    }
     456    osGetColumns += " FROM ";
     457    osGetColumns += osTableId;
     458    osGetColumns += " ";
     459    osGetColumns += osWHERE;
     460    osGetColumns += CPLSPrintf("OFFSET %d LIMIT %d", nOffset, MAX_FEATURES_FETCH);
     461
     462    char** papszOptions = CSLAddString(poDS->AddHTTPOptions(),
     463            CPLSPrintf("POSTFIELDS=sql=%s", osGetColumns.c_str()));
    336464    CPLPushErrorHandler(CPLQuietErrorHandler);
    337     CPLHTTPResult * psResult = CPLHTTPFetch( osURL, papszOptions);
     465    CPLHTTPResult * psResult = CPLHTTPFetch( poDS->GetAPIURL(), papszOptions);
    338466    CPLPopErrorHandler();
    339467    CSLDestroy(papszOptions);
     
    346474    if (pszLine == NULL || psResult->pszErrBuf != NULL)
    347475    {
     476        CPLDebug("GFT", "Error : %s", pszLine ? pszLine : psResult->pszErrBuf);
     477        CPLHTTPDestroyResult(psResult);
     478        return FALSE;
     479    }
     480
     481    pszLine = OGRGFTLayerGotoNextLine(pszLine);
     482    if (pszLine == NULL)
     483    {
    348484        CPLHTTPDestroyResult(psResult);
    349485        return FALSE;
     
    360496        while(*pszIter)
    361497        {
    362             if (*pszIter == '"' && pszIter[1] != '"')
    363                 nDoubleQuotes ++;
     498            if (*pszIter == '"')
     499            {
     500                if (pszIter[1] != '"')
     501                    nDoubleQuotes ++;
     502                else
     503                    pszIter ++;
     504            }
    364505            pszIter ++;
    365506        }
     
    384525                while(*pszIter)
    385526                {
    386                     if (*pszIter == '"' && pszIter[1] != '"')
    387                         nDoubleQuotes ++;
     527                    if (*pszIter == '"')
     528                    {
     529                        if (pszIter[1] != '"')
     530                            nDoubleQuotes ++;
     531                        else
     532                            pszIter ++;
     533                    }
    388534                    pszIter ++;
    389535                }
     
    391537                if ((nDoubleQuotes % 2) == 0)
    392538                {
    393                     aosRows.push_back(osLine);
    394539                    break;
    395540                }
     
    398543            }
    399544
     545            aosRows.push_back(osLine);
    400546        }
    401547
     
    403549    }
    404550
     551    bEOF = aosRows.size() < MAX_FEATURES_FETCH;
     552
    405553    CPLHTTPDestroyResult(psResult);
    406554
    407555    return TRUE;
    408556}
     557/************************************************************************/
     558/*                            GetFeature()                              */
     559/************************************************************************/
     560
     561OGRFeature * OGRGFTLayer::GetFeature( long nFID )
     562{
     563    GetLayerDefn();
     564
     565    char** papszOptions = poDS->AddHTTPOptions();
     566    CPLString osURL = CPLSPrintf("%s?sql=SELECT+*+FROM+%s+OFFSET+%d+LIMIT+1&hdrs=false",
     567                                 poDS->GetAPIURL(), osTableId.c_str(), (int)nFID);
     568
     569    CPLPushErrorHandler(CPLQuietErrorHandler);
     570    CPLHTTPResult * psResult = CPLHTTPFetch( osURL, papszOptions);
     571    CPLPopErrorHandler();
     572    CSLDestroy(papszOptions);
     573    papszOptions = NULL;
     574
     575    if (psResult == NULL)
     576        return NULL;
     577
     578    char* pszLine = (char*) psResult->pabyData;
     579    if (pszLine == NULL || psResult->pszErrBuf != NULL)
     580    {
     581        CPLHTTPDestroyResult(psResult);
     582        return NULL;
     583    }
     584
     585    OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
     586
     587    char** papszTokens = OGRGFTCSVSplitLine(pszLine, ',');
     588    int nTokens = CSLCount(papszTokens);
     589    if (nTokens == poFeatureDefn->GetFieldCount())
     590    {
     591        for(int i=0;i<poFeatureDefn->GetFieldCount();i++)
     592        {
     593            if (papszTokens[i][0])
     594                poFeature->SetField(i, papszTokens[i]);
     595        }
     596    }
     597    else
     598    {
     599        CPLDebug("GFT", "Only %d columns for feature %d", nTokens, (int)nFID);
     600    }
     601    CSLDestroy(papszTokens);
     602
     603    poFeature->SetFID(nFID);
     604
     605    CPLHTTPDestroyResult(psResult);
     606
     607    return poFeature;
     608}
    409609
    410610/************************************************************************/
     
    415615{
    416616    if (poFeatureDefn == NULL)
     617    {
     618        if (osTableId.size() == 0)
     619            return NULL;
    417620        FetchDescribe();
     621    }
    418622
    419623    return poFeatureDefn;
     
    429633        return OGRLayer::GetFeatureCount(bForce);
    430634
    431     if (nFeatureCount >= 0)
    432         return nFeatureCount;
    433 
    434     char** papszOptions = NULL;
    435     const CPLString& osAuth = poDS->GetAuth();
    436     papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: GoogleLogin auth=%s", osAuth.c_str()));
    437 
    438     CPLString osURL = CPLSPrintf("https://www.google.com/fusiontables/api/query?sql=SELECT+COUNT()+FROM+%s&hdrs=false", osTableId.c_str());
    439 
     635    /*if (nFeatureCount >= 0)
     636        return nFeatureCount;*/
     637
     638    char** papszOptions = poDS->AddHTTPOptions();
     639    CPLString osURL = CPLSPrintf("%s?sql=SELECT+COUNT()+FROM+%s&hdrs=false",
     640                                 poDS->GetAPIURL(), osTableId.c_str());
    440641    CPLHTTPResult * psResult = CPLHTTPFetch( osURL, papszOptions);
    441642    CSLDestroy(papszOptions);
     
    458659    return nFeatureCount;
    459660}
     661
     662/************************************************************************/
     663/*                            CreateField()                             */
     664/************************************************************************/
     665
     666OGRErr OGRGFTLayer::CreateField( OGRFieldDefn *poField,
     667                                 int bApproxOK )
     668{
     669
     670    if (!poDS->IsReadWrite())
     671    {
     672        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
     673        return OGRERR_FAILURE;
     674    }
     675
     676    if (osTableId.size() != 0)
     677    {
     678        CPLError(CE_Failure, CPLE_NotSupported,
     679                 "Cannot add field to already created table");
     680        return OGRERR_FAILURE;
     681    }
     682
     683    if (poDS->GetAuth().size() == 0)
     684    {
     685        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
     686        return OGRERR_FAILURE;
     687    }
     688
     689    if (poFeatureDefn == NULL)
     690    {
     691        poFeatureDefn = new OGRFeatureDefn( osTableName );
     692        poFeatureDefn->Reference();
     693        poFeatureDefn->SetGeomType( wkbUnknown );
     694    }
     695
     696    poFeatureDefn->AddFieldDefn(poField);
     697
     698    return OGRERR_NONE;
     699}
     700
     701/************************************************************************/
     702/*                       CreateTableIfNecessary()                       */
     703/************************************************************************/
     704
     705void OGRGFTLayer::CreateTableIfNecessary()
     706{
     707    if (bHasTriedCreateTable || osTableId.size() != 0)
     708        return;
     709
     710    bHasTriedCreateTable = TRUE;
     711
     712    CPLString osPOST("POSTFIELDS=sql=CREATE TABLE '");
     713    osPOST += osTableName;
     714    osPOST += "' (";
     715
     716    int i;
     717
     718    for(i=0;i<poFeatureDefn->GetFieldCount();i++)
     719    {
     720        const char* pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     721        if (EQUAL(pszName, "latitude") || EQUAL(pszName, "lat"))
     722            iLatitude = i;
     723        else if (EQUAL(pszName, "longitude") || EQUAL(pszName, "lon") || EQUAL(pszName, "long"))
     724            iLongitude = i;
     725    }
     726
     727    if (iLatitude >= 0 && iLongitude >= 0)
     728        iGeometryField = iLatitude;
     729
     730    for(i=0;i<poFeatureDefn->GetFieldCount();i++)
     731    {
     732        if (i > 0)
     733            osPOST += ", ";
     734        osPOST += "'";
     735        osPOST += poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     736        osPOST += "': ";
     737
     738        if (iGeometryField == i)
     739        {
     740            osPOST += "LOCATION";
     741        }
     742        else
     743        {
     744            switch(poFeatureDefn->GetFieldDefn(i)->GetType())
     745            {
     746                case OFTInteger:
     747                case OFTReal:
     748                    osPOST += "NUMBER";
     749                    break;
     750                default:
     751                    osPOST += "STRING";
     752            }
     753        }
     754    }
     755    if (iGeometryField < 0)
     756    {
     757        if (i > 0)
     758            osPOST += ", ";
     759        osPOST += "geometry: LOCATION";
     760
     761        iGeometryField = poFeatureDefn->GetFieldCount();
     762        OGRFieldDefn oFieldDefn("geometry", OFTString);
     763        poFeatureDefn->AddFieldDefn(&oFieldDefn);
     764    }
     765    osPOST += ")";
     766
     767    char** papszOptions = CSLAddString(poDS->AddHTTPOptions(), osPOST);
     768    //CPLDebug("GFT", "%s",  osPOST.c_str());
     769    CPLHTTPResult* psResult = CPLHTTPFetch( poDS->GetAPIURL(), papszOptions);
     770    CSLDestroy(papszOptions);
     771    papszOptions = NULL;
     772
     773    if (psResult == NULL)
     774    {
     775        CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed");
     776        return;
     777    }
     778
     779    char* pszLine = (char*) psResult->pabyData;
     780    if (pszLine == NULL ||
     781        strncmp(pszLine, "tableid", 7) != 0 ||
     782        psResult->pszErrBuf != NULL)
     783    {
     784        CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed");
     785        CPLHTTPDestroyResult(psResult);
     786        return;
     787    }
     788
     789    pszLine = OGRGFTLayerGotoNextLine(pszLine);
     790    if (pszLine == NULL)
     791    {
     792        CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed");
     793        CPLHTTPDestroyResult(psResult);
     794        return;
     795    }
     796
     797    char* pszNextLine = OGRGFTLayerGotoNextLine(pszLine);
     798    if (pszNextLine)
     799        pszNextLine[-1] = 0;
     800
     801    osTableId = pszLine;
     802    CPLDebug("GFT", "Table %s --> id = %s", osTableName.c_str(), osTableId.c_str());
     803
     804    CPLHTTPDestroyResult(psResult);
     805}
     806
     807/************************************************************************/
     808/*                            EscapeQuote()                             */
     809/************************************************************************/
     810
     811static CPLString EscapeQuote(const char* pszStr)
     812{
     813    CPLString osRes;
     814    while(*pszStr)
     815    {
     816        if (*pszStr == '\'')
     817            osRes += "\'\'";
     818        else
     819            osRes += *pszStr;
     820        pszStr ++;
     821    }
     822    return osRes;
     823}
     824
     825/************************************************************************/
     826/*                           CreateFeature()                            */
     827/************************************************************************/
     828
     829OGRErr OGRGFTLayer::CreateFeature( OGRFeature *poFeature )
     830
     831{
     832    if (!poDS->IsReadWrite())
     833    {
     834        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
     835        return OGRERR_FAILURE;
     836    }
     837
     838    if (osTableId.size() == 0)
     839    {
     840        CreateTableIfNecessary();
     841        if (osTableId.size() == 0)
     842        {
     843            CPLError(CE_Failure, CPLE_NotSupported,
     844                    "Cannot add field to non-created table");
     845            return OGRERR_FAILURE;
     846        }
     847    }
     848
     849    if (poDS->GetAuth().size() == 0)
     850    {
     851        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
     852        return OGRERR_FAILURE;
     853    }
     854
     855    CPLString      osCommand;
     856
     857    osCommand += "INSERT INTO ";
     858    osCommand += osTableId;
     859    osCommand += " (";
     860
     861    int iField;
     862    int nFieldCount = poFeatureDefn->GetFieldCount();
     863    for(iField = 0; iField < nFieldCount; iField++)
     864    {
     865        if (iField > 0)
     866            osCommand += ", ";
     867        osCommand += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     868    }
     869    osCommand += ") VALUES (";
     870    for(iField = 0; iField < nFieldCount; iField++)
     871    {
     872        if (iField > 0)
     873            osCommand += ", ";
     874
     875        if (iGeometryField != iLatitude && iField == iGeometryField)
     876        {
     877            OGRGeometry* poGeom = poFeature->GetGeometryRef();
     878            if (poGeom == NULL)
     879                osCommand += "''";
     880            else
     881            {
     882                char* pszKML;
     883                if (poGeom->getSpatialReference() != NULL &&
     884                    !poGeom->getSpatialReference()->IsSame(poSRS))
     885                {
     886                    OGRGeometry* poGeom4326 = poGeom->clone();
     887                    poGeom4326->transformTo(poSRS);
     888                    pszKML = poGeom4326->exportToKML();
     889                    delete poGeom4326;
     890                }
     891                else
     892                {
     893                    pszKML = poGeom->exportToKML();
     894                }
     895                osCommand += "'";
     896                osCommand += pszKML;
     897                osCommand += "'";
     898                CPLFree(pszKML);
     899            }
     900            continue;
     901        }
     902
     903        if( !poFeature->IsFieldSet( iField ) )
     904        {
     905            osCommand += "''";
     906        }
     907        else
     908        {
     909            osCommand += "'";
     910            const char* pszVal = poFeature->GetFieldAsString(iField);
     911            if (strchr(pszVal, '\''))
     912                osCommand += EscapeQuote(pszVal);
     913            else
     914                osCommand += pszVal;
     915            osCommand += "'";
     916        }
     917    }
     918
     919    osCommand += ")";
     920
     921    CPLDebug("GFT", "%s",  osCommand.c_str());
     922
     923    if (bInTransaction)
     924    {
     925        if (osTransaction.size())
     926            osTransaction += "; ";
     927        osTransaction += osCommand;
     928        return OGRERR_NONE;
     929    }
     930
     931    CPLString osPOST("POSTFIELDS=sql=");
     932    osPOST += osCommand;
     933    char** papszOptions = CSLAddString(poDS->AddHTTPOptions(), osPOST);
     934    CPLHTTPResult* psResult = CPLHTTPFetch( poDS->GetAPIURL(), papszOptions);
     935    CSLDestroy(papszOptions);
     936    papszOptions = NULL;
     937
     938    if (psResult == NULL)
     939    {
     940        CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed");
     941        return OGRERR_FAILURE;
     942    }
     943
     944    char* pszLine = (char*) psResult->pabyData;
     945    if (pszLine == NULL ||
     946        strncmp(pszLine, "rowid", 5) != 0 ||
     947        psResult->pszErrBuf != NULL)
     948    {
     949        CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed");
     950        CPLHTTPDestroyResult(psResult);
     951        return OGRERR_FAILURE;
     952    }
     953
     954    pszLine = OGRGFTLayerGotoNextLine(pszLine);
     955    if (pszLine == NULL)
     956    {
     957        CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed");
     958        CPLHTTPDestroyResult(psResult);
     959        return OGRERR_FAILURE;
     960    }
     961
     962    char* pszNextLine = OGRGFTLayerGotoNextLine(pszLine);
     963    if (pszNextLine)
     964        pszNextLine[-1] = 0;
     965
     966    CPLDebug("GFT", "Feature id = %s",  pszLine);
     967
     968    CPLHTTPDestroyResult(psResult);
     969
     970    return OGRERR_NONE;
     971}
     972
     973/************************************************************************/
     974/*                         StartTransaction()                           */
     975/************************************************************************/
     976
     977OGRErr OGRGFTLayer::StartTransaction()
     978{
     979    if (bInTransaction)
     980    {
     981        CPLError(CE_Failure, CPLE_AppDefined, "Already in transaction");
     982        return OGRERR_FAILURE;
     983    }
     984
     985    if (!poDS->IsReadWrite())
     986    {
     987        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
     988        return OGRERR_FAILURE;
     989    }
     990
     991    if (osTableId.size() == 0)
     992    {
     993        CreateTableIfNecessary();
     994        if (osTableId.size() == 0)
     995        {
     996            CPLError(CE_Failure, CPLE_NotSupported,
     997                    "Cannot add field to non-created table");
     998            return OGRERR_FAILURE;
     999        }
     1000    }
     1001
     1002    if (poDS->GetAuth().size() == 0)
     1003    {
     1004        CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
     1005        return OGRERR_FAILURE;
     1006    }
     1007
     1008    bInTransaction = TRUE;
     1009
     1010    return OGRERR_NONE;
     1011}
     1012
     1013/************************************************************************/
     1014/*                         CommitTransaction()                          */
     1015/************************************************************************/
     1016
     1017OGRErr OGRGFTLayer::CommitTransaction()
     1018{
     1019    if (!bInTransaction)
     1020    {
     1021        CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction");
     1022        return OGRERR_FAILURE;
     1023    }
     1024
     1025    bInTransaction = FALSE;
     1026
     1027    if (osTransaction)
     1028    {
     1029        CPLString osPOST("POSTFIELDS=sql=");
     1030        osPOST += osTransaction;
     1031        char** papszOptions = CSLAddString(poDS->AddHTTPOptions(), osPOST);
     1032        CPLHTTPResult* psResult = CPLHTTPFetch( poDS->GetAPIURL(), papszOptions);
     1033        CSLDestroy(papszOptions);
     1034        papszOptions = NULL;
     1035
     1036        osTransaction.resize(0);
     1037
     1038        if (psResult == NULL)
     1039        {
     1040            CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed");
     1041            return OGRERR_FAILURE;
     1042        }
     1043
     1044        char* pszLine = (char*) psResult->pabyData;
     1045        if (pszLine == NULL ||
     1046            strncmp(pszLine, "rowid", 5) != 0 ||
     1047            psResult->pszErrBuf != NULL)
     1048        {
     1049            CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed");
     1050            CPLHTTPDestroyResult(psResult);
     1051            return OGRERR_FAILURE;
     1052        }
     1053
     1054        pszLine = OGRGFTLayerGotoNextLine(pszLine);
     1055        while(pszLine && *pszLine != 0)
     1056        {
     1057            char* pszNextLine = OGRGFTLayerGotoNextLine(pszLine);
     1058            if (pszNextLine)
     1059                pszNextLine[-1] = 0;
     1060            CPLDebug("GFT", "Feature id = %s",  pszLine);
     1061
     1062            pszLine = pszNextLine;
     1063        }
     1064
     1065        CPLHTTPDestroyResult(psResult);
     1066    }
     1067
     1068    return OGRERR_NONE;
     1069}
     1070
     1071/************************************************************************/
     1072/*                        RollbackTransaction()                         */
     1073/************************************************************************/
     1074
     1075OGRErr OGRGFTLayer::RollbackTransaction()
     1076{
     1077    if (!bInTransaction)
     1078    {
     1079        CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction");
     1080        return OGRERR_FAILURE;
     1081    }
     1082    bInTransaction = FALSE;
     1083    return OGRERR_NONE;
     1084}
     1085
     1086/************************************************************************/
     1087/*                         SetAttributeFilter()                         */
     1088/************************************************************************/
     1089
     1090OGRErr OGRGFTLayer::SetAttributeFilter( const char *pszQuery )
     1091
     1092{
     1093    if( pszQuery == NULL )
     1094        osQuery = "";
     1095    else
     1096        osQuery = pszQuery;
     1097
     1098    BuildWhere();
     1099
     1100    ResetReading();
     1101
     1102    return OGRERR_NONE;
     1103}
     1104
     1105
     1106/************************************************************************/
     1107/*                          SetSpatialFilter()                          */
     1108/************************************************************************/
     1109
     1110void OGRGFTLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
     1111
     1112{
     1113    if( InstallFilter( poGeomIn ) )
     1114    {
     1115        BuildWhere();
     1116
     1117        ResetReading();
     1118    }
     1119}
     1120
     1121/************************************************************************/
     1122/*                             BuildWhere()                             */
     1123/*                                                                      */
     1124/*      Build the WHERE statement appropriate to the current set of     */
     1125/*      criteria (spatial and attribute queries).                       */
     1126/************************************************************************/
     1127
     1128void OGRGFTLayer::BuildWhere()
     1129
     1130{
     1131    osWHERE = "";
     1132
     1133    if( m_poFilterGeom != NULL && iGeometryField >= 0)
     1134    {
     1135        OGREnvelope  sEnvelope;
     1136
     1137        m_poFilterGeom->getEnvelope( &sEnvelope );
     1138        osWHERE.Printf("WHERE ST_INTERSECTS('%s', RECTANGLE(LATLNG(%.12f, %.12f), LATLNG(%.12f, %.12f))) ",
     1139                       poFeatureDefn->GetFieldDefn(iGeometryField)->GetNameRef(),
     1140                       sEnvelope.MinY - 1e-11, sEnvelope.MinX - 1e-11,
     1141                       sEnvelope.MaxY + 1e-11, sEnvelope.MaxX + 1e-11);
     1142    }
     1143
     1144    if( strlen(osQuery) > 0 )
     1145    {
     1146        if( strlen(osWHERE) == 0 )
     1147        {
     1148            osWHERE.Printf( "WHERE %s ", osQuery.c_str()  );
     1149        }
     1150        else
     1151        {
     1152            osWHERE += "AND ";
     1153            osWHERE += osQuery;
     1154        }
     1155    }
     1156}
Note: See TracChangeset for help on using the changeset viewer.