LCOV - code coverage report
Current view: directory - ogr - ogr_geocoding.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 612 532 86.9 %
Date: 2013-03-30 Functions: 18 17 94.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogr_geocoding.cpp 25544 2013-01-24 19:54:22Z rouault $
       3                 :  *
       4                 :  * Project:  OpenGIS Simple Features Reference Implementation
       5                 :  * Purpose:  Client of geocoding service.
       6                 :  * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2012, Even Rouault
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "cpl_conv.h"
      31                 : #include "cpl_http.h"
      32                 : #include "cpl_multiproc.h"
      33                 : #include "cpl_minixml.h"
      34                 : 
      35                 : /* Emulation of gettimeofday() for Windows */
      36                 : #ifdef WIN32
      37                 : 
      38                 : #include <time.h>
      39                 : #include <windows.h>
      40                 : 
      41                 : /* Recent mingw define struct timezone */
      42                 : #if !(defined(__GNUC__) && defined(_TIMEZONE_DEFINED))
      43                 : struct timezone 
      44                 : {
      45                 :   int  tz_minuteswest; /* minutes W of Greenwich */
      46                 :   int  tz_dsttime;     /* type of DST correction */
      47                 : };
      48                 : #endif
      49                 : 
      50                 : #define MICROSEC_IN_SEC   1000000
      51                 : 
      52                 : static
      53                 : int OGR_gettimeofday(struct timeval *tv, struct timezone *tzIgnored)
      54                 : {
      55                 :     FILETIME ft;
      56                 :     GetSystemTimeAsFileTime(&ft);
      57                 : 
      58                 :     /* In 100-nanosecond intervals since January 1, 1601 (UTC). */
      59                 :     GUIntBig nVal = (((GUIntBig)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
      60                 :     nVal /= 10; /* to microseconds */
      61                 :     /* There are 11 644 473 600 seconds between 1601 and 1970 */
      62                 :     nVal -= ((GUIntBig)116444736) * 100 * MICROSEC_IN_SEC;
      63                 :     tv->tv_sec = (long)(nVal / MICROSEC_IN_SEC);
      64                 :     tv->tv_usec = (long)(nVal % MICROSEC_IN_SEC);
      65                 : 
      66                 :     return 0;
      67                 : }
      68                 : 
      69                 : #define gettimeofday OGR_gettimeofday
      70                 : 
      71                 : #else
      72                 : #include <sys/time.h>
      73                 : #endif
      74                 : 
      75                 : 
      76                 : #include "ogr_geocoding.h"
      77                 : #include "ogr_mem.h"
      78                 : #include "ogrsf_frmts.h"
      79                 : 
      80                 : CPL_CVSID("$Id: ogr_geocoding.cpp 25544 2013-01-24 19:54:22Z rouault $");
      81                 : 
      82                 : struct _OGRGeocodingSessionHS
      83                 : {
      84                 :     char*  pszCacheFilename;
      85                 :     char*  pszGeocodingService;
      86                 :     char*  pszEmail;
      87                 :     char*  pszUserName;
      88                 :     char*  pszKey;
      89                 :     char*  pszApplication;
      90                 :     char*  pszLanguage;
      91                 :     char*  pszQueryTemplate;
      92                 :     char*  pszReverseQueryTemplate;
      93                 :     int    bReadCache;
      94                 :     int    bWriteCache;
      95                 :     double dfDelayBetweenQueries;
      96                 :     OGRDataSource* poDS;
      97                 : };
      98                 : 
      99                 : static void* hMutex = NULL;
     100                 : static double dfLastQueryTimeStampOSMNominatim = 0.0;
     101                 : static double dfLastQueryTimeStampMapQuestNominatim = 0.0;
     102                 : 
     103                 : #define OSM_NOMINATIM_QUERY      "http://nominatim.openstreetmap.org/search?q=%s&format=xml&polygon_text=1"
     104                 : #define MAPQUEST_NOMINATIM_QUERY "http://open.mapquestapi.com/nominatim/v1/search.php?q=%s&format=xml"
     105                 : #define YAHOO_QUERY              "http://where.yahooapis.com/geocode?q=%s"
     106                 : #define GEONAMES_QUERY           "http://api.geonames.org/search?q=%s&style=LONG"
     107                 : #define BING_QUERY               "http://dev.virtualearth.net/REST/v1/Locations?q=%s&o=xml"
     108                 : 
     109                 : #define OSM_NOMINATIM_REVERSE_QUERY      "http://nominatim.openstreetmap.org/reverse?format=xml&lat={lat}&lon={lon}"
     110                 : #define MAPQUEST_NOMINATIM_REVERSE_QUERY "http://open.mapquestapi.com/nominatim/v1/reverse.php?format=xml&lat={lat}&lon={lon}"
     111                 : #define YAHOO_REVERSE_QUERY              "http://where.yahooapis.com/geocode?q={lat},{lon}&gflags=R"
     112                 : #define GEONAMES_REVERSE_QUERY           "http://api.geonames.org/findNearby?lat={lat}&lng={lon}&style=LONG"
     113                 : #define BING_REVERSE_QUERY               "http://dev.virtualearth.net/REST/v1/Locations/{lat},{lon}?includeEntityTypes=countryRegion&o=xml"
     114                 : 
     115                 : #define CACHE_LAYER_NAME         "ogr_geocode_cache"
     116                 : #define DEFAULT_CACHE_SQLITE     "ogr_geocode_cache.sqlite"
     117                 : #define DEFAULT_CACHE_CSV        "ogr_geocode_cache.csv"
     118                 : 
     119                 : #define FIELD_URL                "url"
     120                 : #define FIELD_BLOB               "blob"
     121                 : 
     122                 : /************************************************************************/
     123                 : /*                       OGRGeocodeGetParameter()                       */
     124                 : /************************************************************************/
     125                 : 
     126                 : static
     127            1628 : const char* OGRGeocodeGetParameter(char** papszOptions, const char* pszKey,
     128                 :                                    const char* pszDefaultValue)
     129                 : {
     130            1628 :     const char* pszRet = CSLFetchNameValue(papszOptions, pszKey);
     131            1628 :     if( pszRet != NULL )
     132              36 :         return pszRet;
     133                 : 
     134                 :     return CPLGetConfigOption(CPLSPrintf("OGR_GEOCODE_%s", pszKey),
     135            1592 :                               pszDefaultValue);
     136                 : }
     137                 : 
     138                 : /************************************************************************/
     139                 : /*                      OGRGeocodeHasStringValidFormat()                */
     140                 : /************************************************************************/
     141                 : 
     142                 : /* Checks that pszQueryTemplate has one and only one occurence of %s in it. */
     143                 : static
     144             112 : int OGRGeocodeHasStringValidFormat(const char* pszQueryTemplate)
     145                 : {
     146             112 :     const char* pszIter = pszQueryTemplate;
     147             112 :     int bValidFormat = TRUE;
     148             112 :     int bFoundPctS = FALSE;
     149            5356 :     while( *pszIter != '\0' )
     150                 :     {
     151            5132 :         if( *pszIter == '%' )
     152                 :         {
     153             112 :             if( pszIter[1] == '%' )
     154                 :             {
     155               0 :                 pszIter ++;
     156                 :             }
     157             112 :             else if( pszIter[1] == 's' )
     158                 :             {
     159             112 :                 if( bFoundPctS )
     160                 :                 {
     161               0 :                     bValidFormat = FALSE;
     162               0 :                     break;
     163                 :                 }
     164             112 :                 bFoundPctS = TRUE;
     165                 :             }
     166                 :             else
     167                 :             {
     168               0 :                 bValidFormat = FALSE;
     169               0 :                 break;
     170                 :             }
     171                 :         }
     172            5132 :         pszIter ++;
     173                 :     }
     174             112 :     if( !bFoundPctS )
     175               0 :         bValidFormat = FALSE;
     176             112 :     return bValidFormat;
     177                 : }
     178                 : 
     179                 : /************************************************************************/
     180                 : /*                       OGRGeocodeCreateSession()                      */
     181                 : /************************************************************************/
     182                 : 
     183                 : /**
     184                 :  * \brief Creates a session handle for geocoding requests.
     185                 :  *
     186                 :  * Available papszOptions values:
     187                 :  * <ul>
     188                 :  * <li> "CACHE_FILE" : Defaults to "ogr_geocode_cache.sqlite" (or otherwise
     189                 :  *                    "ogr_geocode_cache.csv" if the SQLite driver isn't
     190                 :  *                    available). Might be any CSV, SQLite or PostgreSQL
     191                 :  *                    datasource.
     192                 :  * <li> "READ_CACHE" : "TRUE" (default) or "FALSE"
     193                 :  * <li> "WRITE_CACHE" : "TRUE" (default) or "FALSE"
     194                 :  * <li> "SERVICE": <a href="http://wiki.openstreetmap.org/wiki/Nominatim">"OSM_NOMINATIM"</a>
     195                 :  *      (default), <a href="http://open.mapquestapi.com/nominatim/">"MAPQUEST_NOMINATIM"</a>,
     196                 :  *      <a href="http://developer.yahoo.com/geo/placefinder/">"YAHOO"</a>,
     197                 :  *      <a href="http://www.geonames.org/export/geonames-search.html">"GEONAMES"</a>,
     198                 :  *      <a href="http://msdn.microsoft.com/en-us/library/ff701714.aspx">"BING"</a> or
     199                 :  *       other value.
     200                 :  * <li> "EMAIL": used by OSM_NOMINATIM. Optional, but recommanded.
     201                 :  * <li> "USERNAME": used by GEONAMES. Compulsory in that case.
     202                 :  * <li> "KEY": used by BING. Compulsory in that case.
     203                 :  * <li> "APPLICATION": used to set the User-Agent MIME header. Defaults
     204                 :  *       to GDAL/OGR version string.
     205                 :  * <li> "LANGUAGE": used to set the Accept-Language MIME header. Preferred
     206                 :  *      language order for showing search results.
     207                 :  * <li> "DELAY": minimum delay, in second, between 2 consecutive queries.
     208                 :  *       Defaults to 1.0.
     209                 :  * <li> "QUERY_TEMPLATE": URL template for GET requests. Must contain one
     210                 :  *       and only one occurence of %%s in it. If not specified, for
     211                 :  *       SERVICE=OSM_NOMINATIM, MAPQUEST_NOMINATIM, YAHOO, GEONAMES or BING,
     212                 :  *       the URL template is hard-coded.
     213                 :  * <li> "REVERSE_QUERY_TEMPLATE": URL template for GET requests for reverse
     214                 :  *       geocoding. Must contain one and only one occurence of {lon} and {lat} in it.
     215                 :  *       If not specified, for SERVICE=OSM_NOMINATIM, MAPQUEST_NOMINATIM, YAHOO,
     216                 :  *       GEONAMES or BING, the URL template is hard-coded.
     217                 :  * </ul>
     218                 :  *
     219                 :  * All the above options can also be set by defining the configuration option
     220                 :  * of the same name, prefixed by OGR_GEOCODE_. For example "OGR_GEOCODE_SERVICE"
     221                 :  * for the "SERVICE" option.
     222                 :  *
     223                 :  * @param papszOptions NULL, or a NULL-terminated list of string options.
     224                 :  *
     225                 :  * @return an handle that should be freed with OGRGeocodeDestroySession(), or NULL
     226                 :  *         in case of failure.
     227                 :  *
     228                 :  * @since GDAL 1.10
     229                 :  */
     230                 : 
     231             112 : OGRGeocodingSessionH OGRGeocodeCreateSession(char** papszOptions)
     232                 : {
     233                 :     OGRGeocodingSessionH hSession =
     234             112 :         (OGRGeocodingSessionH)CPLCalloc(1, sizeof(_OGRGeocodingSessionHS));
     235                 : 
     236                 :     const char* pszCacheFilename = OGRGeocodeGetParameter(papszOptions,
     237                 :                                                           "CACHE_FILE",
     238             112 :                                                           DEFAULT_CACHE_SQLITE);
     239             112 :     CPLString osExt = CPLGetExtension(pszCacheFilename);
     240             112 :     if( !(EQUALN(pszCacheFilename, "PG:", 3) ||
     241                 :         EQUAL(osExt, "csv") || EQUAL(osExt, "sqlite")) )
     242                 :     {
     243                 :         CPLError(CE_Failure, CPLE_AppDefined,
     244               0 :                  "Only .csv, .sqlite or PG: datasources are handled for now.");
     245               0 :         OGRGeocodeDestroySession(hSession);
     246               0 :         return NULL;
     247                 :     }
     248             112 :     hSession->pszCacheFilename = CPLStrdup(pszCacheFilename);
     249                 : 
     250                 :     hSession->bReadCache = CSLTestBoolean(
     251             112 :         OGRGeocodeGetParameter(papszOptions, "READ_CACHE", "TRUE"));
     252                 :     hSession->bWriteCache = CSLTestBoolean(
     253             112 :         OGRGeocodeGetParameter(papszOptions, "WRITE_CACHE", "TRUE"));
     254                 : 
     255                 :     const char* pszGeocodingService = OGRGeocodeGetParameter(papszOptions,
     256                 :                                                              "SERVICE",
     257             112 :                                                              "OSM_NOMINATIM");
     258             112 :     hSession->pszGeocodingService = CPLStrdup(pszGeocodingService);
     259                 : 
     260             112 :     const char* pszEmail = OGRGeocodeGetParameter(papszOptions, "EMAIL", NULL);
     261             112 :     hSession->pszEmail = pszEmail ? CPLStrdup(pszEmail) : NULL;
     262                 : 
     263             112 :     const char* pszUserName = OGRGeocodeGetParameter(papszOptions, "USERNAME", NULL);
     264             112 :     hSession->pszUserName = pszUserName ? CPLStrdup(pszUserName) : NULL;
     265                 : 
     266             112 :     const char* pszKey = OGRGeocodeGetParameter(papszOptions, "KEY", NULL);
     267             112 :     hSession->pszKey = pszKey ? CPLStrdup(pszKey) : NULL;
     268                 : 
     269             112 :     if( EQUAL(pszGeocodingService, "GEONAMES") && pszUserName == NULL )
     270                 :     {
     271                 :         CPLError(CE_Failure, CPLE_AppDefined,
     272               0 :                  "GEONAMES service requires USERNAME to be specified.");
     273               0 :         OGRGeocodeDestroySession(hSession);
     274               0 :         return NULL;
     275                 :     }
     276             112 :     else if( EQUAL(pszGeocodingService, "BING") && pszKey == NULL )
     277                 :     {
     278                 :         CPLError(CE_Failure, CPLE_AppDefined,
     279               0 :                  "BING service requires KEY to be specified.");
     280               0 :         OGRGeocodeDestroySession(hSession);
     281               0 :         return NULL;
     282                 :     }
     283                 : 
     284                 :     const char* pszApplication = OGRGeocodeGetParameter(papszOptions,
     285                 :                                                         "APPLICATION",
     286             112 :                                                         GDALVersionInfo(""));
     287             112 :     hSession->pszApplication = CPLStrdup(pszApplication);
     288                 : 
     289                 :     const char* pszLanguage = OGRGeocodeGetParameter(papszOptions,
     290                 :                                                      "LANGUAGE",
     291             112 :                                                      NULL);
     292             112 :     hSession->pszLanguage = pszLanguage ? CPLStrdup(pszLanguage) : NULL;
     293                 : 
     294                 :     const char* pszDelayBetweenQueries = OGRGeocodeGetParameter(papszOptions,
     295             112 :                                                                 "DELAY", "1.0");
     296             112 :     hSession->dfDelayBetweenQueries = CPLAtofM(pszDelayBetweenQueries);
     297                 : 
     298             112 :     const char* pszQueryTemplateDefault = NULL;
     299             112 :     if( EQUAL(pszGeocodingService, "OSM_NOMINATIM") )
     300              28 :         pszQueryTemplateDefault = OSM_NOMINATIM_QUERY;
     301              84 :     else if( EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM") )
     302               0 :         pszQueryTemplateDefault = MAPQUEST_NOMINATIM_QUERY;
     303              84 :     else if( EQUAL(pszGeocodingService, "YAHOO") )
     304              28 :         pszQueryTemplateDefault = YAHOO_QUERY;
     305              56 :     else if( EQUAL(pszGeocodingService, "GEONAMES") )
     306              28 :         pszQueryTemplateDefault = GEONAMES_QUERY;
     307              28 :     else if( EQUAL(pszGeocodingService, "BING") )
     308              28 :         pszQueryTemplateDefault = BING_QUERY;
     309                 :     const char* pszQueryTemplate = OGRGeocodeGetParameter(papszOptions,
     310                 :                                                           "QUERY_TEMPLATE",
     311             112 :                                                           pszQueryTemplateDefault);
     312                 : 
     313             112 :     if( pszQueryTemplate != NULL &&
     314                 :         !OGRGeocodeHasStringValidFormat(pszQueryTemplate) )
     315                 :     {
     316                 :         CPLError(CE_Failure, CPLE_AppDefined,
     317               0 :                  "QUERY_TEMPLATE value has an invalid format");
     318               0 :         OGRGeocodeDestroySession(hSession);
     319               0 :         return NULL;
     320                 :     }
     321                 : 
     322                 :     hSession->pszQueryTemplate =
     323             112 :         pszQueryTemplate ? CPLStrdup(pszQueryTemplate) : NULL;
     324                 : 
     325             112 :     const char* pszReverseQueryTemplateDefault = NULL;
     326             112 :     if( EQUAL(pszGeocodingService, "OSM_NOMINATIM") )
     327              28 :         pszReverseQueryTemplateDefault = OSM_NOMINATIM_REVERSE_QUERY;
     328              84 :     else if( EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM") )
     329               0 :         pszReverseQueryTemplateDefault = MAPQUEST_NOMINATIM_REVERSE_QUERY;
     330              84 :     else if( EQUAL(pszGeocodingService, "YAHOO") )
     331              28 :         pszReverseQueryTemplateDefault = YAHOO_REVERSE_QUERY;
     332              56 :     else if( EQUAL(pszGeocodingService, "GEONAMES") )
     333              28 :         pszReverseQueryTemplateDefault = GEONAMES_REVERSE_QUERY;
     334              28 :     else if( EQUAL(pszGeocodingService, "BING") )
     335              28 :         pszReverseQueryTemplateDefault = BING_REVERSE_QUERY;
     336                 :     const char* pszReverseQueryTemplate = OGRGeocodeGetParameter(papszOptions,
     337                 :                                                           "REVERSE_QUERY_TEMPLATE",
     338             112 :                                                           pszReverseQueryTemplateDefault);
     339                 : 
     340             112 :     if( pszReverseQueryTemplate != NULL &&
     341                 :         (strstr(pszReverseQueryTemplate, "{lat}") == NULL ||
     342                 :          strstr(pszReverseQueryTemplate, "{lon}") == NULL) )
     343                 :     {
     344                 :         CPLError(CE_Failure, CPLE_AppDefined,
     345               0 :                  "REVERSE_QUERY_TEMPLATE value has an invalid format");
     346               0 :         OGRGeocodeDestroySession(hSession);
     347               0 :         return NULL;
     348                 :     }
     349                 : 
     350                 :     hSession->pszReverseQueryTemplate =
     351             112 :         (pszReverseQueryTemplate) ? CPLStrdup(pszReverseQueryTemplate) : NULL;
     352                 : 
     353             112 :     return hSession;
     354                 : }
     355                 : 
     356                 : /************************************************************************/
     357                 : /*                       OGRGeocodeDestroySession()                     */
     358                 : /************************************************************************/
     359                 : 
     360                 : /**
     361                 :  * \brief Destroys a session handle for geocoding requests.
     362                 : 
     363                 :  * @param hSession the handle to destroy.
     364                 :  *
     365                 :  * @since GDAL 1.10
     366                 :  */
     367             751 : void OGRGeocodeDestroySession(OGRGeocodingSessionH hSession)
     368                 : {
     369             751 :     if( hSession == NULL )
     370             639 :         return;
     371             112 :     CPLFree(hSession->pszCacheFilename);
     372             112 :     CPLFree(hSession->pszGeocodingService);
     373             112 :     CPLFree(hSession->pszEmail);
     374             112 :     CPLFree(hSession->pszUserName);
     375             112 :     CPLFree(hSession->pszKey);
     376             112 :     CPLFree(hSession->pszApplication);
     377             112 :     CPLFree(hSession->pszLanguage);
     378             112 :     CPLFree(hSession->pszQueryTemplate);
     379             112 :     CPLFree(hSession->pszReverseQueryTemplate);
     380             112 :     if( hSession->poDS )
     381             112 :         OGRReleaseDataSource((OGRDataSourceH) hSession->poDS);
     382             112 :     CPLFree(hSession);
     383                 : }
     384                 : 
     385                 : /************************************************************************/
     386                 : /*                        OGRGeocodeGetCacheLayer()                     */
     387                 : /************************************************************************/
     388                 : 
     389             146 : static OGRLayer* OGRGeocodeGetCacheLayer(OGRGeocodingSessionH hSession,
     390                 :                                          int bCreateIfNecessary,
     391                 :                                          int* pnIdxBlob)
     392                 : {
     393             146 :     OGRDataSource* poDS = hSession->poDS;
     394             146 :     CPLString osExt = CPLGetExtension(hSession->pszCacheFilename);
     395                 : 
     396             146 :     if( poDS == NULL )
     397                 :     {
     398             128 :         if( OGRGetDriverCount() == 0 )
     399               0 :             OGRRegisterAll();
     400                 : 
     401                 :         char* pszOldVal = CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL) ?
     402             128 :             CPLStrdup(CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL)) : NULL;
     403             128 :         CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS", "OFF");
     404                 : 
     405             128 :         poDS = (OGRDataSource*) OGROpen(hSession->pszCacheFilename, TRUE, NULL);
     406             128 :         if( poDS == NULL &&
     407                 :             EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE) )
     408                 :         {
     409               0 :             poDS = (OGRDataSource*) OGROpen(DEFAULT_CACHE_CSV, TRUE, NULL);
     410               0 :             if( poDS != NULL )
     411                 :             {
     412               0 :                 CPLFree(hSession->pszCacheFilename);
     413               0 :                 hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV);
     414                 :                 CPLDebug("OGR", "Switch geocode cache file to %s",
     415               0 :                          hSession->pszCacheFilename);
     416               0 :                 osExt = "csv";
     417                 :             }
     418                 :         }
     419                 : 
     420             128 :         if( bCreateIfNecessary && poDS == NULL &&
     421                 :             !EQUALN(hSession->pszCacheFilename, "PG:", 3) )
     422                 :         {
     423              16 :             OGRSFDriverH hDriver = OGRGetDriverByName(osExt);
     424              16 :             if( hDriver == NULL &&
     425                 :                 EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE) )
     426                 :             {
     427               0 :                 CPLFree(hSession->pszCacheFilename);
     428               0 :                 hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV);
     429                 :                 CPLDebug("OGR", "Switch geocode cache file to %s",
     430               0 :                          hSession->pszCacheFilename);
     431               0 :                 osExt = "csv";
     432               0 :                 hDriver = OGRGetDriverByName(osExt);
     433                 :             }
     434              16 :             if( hDriver != NULL )
     435                 :             {
     436              16 :                 char** papszOptions = NULL;
     437              16 :                 if( EQUAL(osExt, "SQLITE") )
     438                 :                 {
     439                 :                     papszOptions = CSLAddNameValue(papszOptions,
     440               8 :                                                    "METADATA", "FALSE");
     441                 :                 }
     442                 : 
     443                 :                 poDS = (OGRDataSource*) OGR_Dr_CreateDataSource(
     444              16 :                             hDriver, hSession->pszCacheFilename, papszOptions);
     445                 : 
     446              16 :                 if( poDS == NULL &&
     447                 :                     (EQUAL(osExt, "SQLITE") || EQUAL(osExt, "CSV")))
     448                 :                 {
     449               0 :                     CPLFree(hSession->pszCacheFilename);
     450                 :                     hSession->pszCacheFilename = CPLStrdup(
     451                 :                         CPLSPrintf("/vsimem/%s.%s",
     452               0 :                                    CACHE_LAYER_NAME, osExt.c_str()));
     453                 :                     CPLDebug("OGR", "Switch geocode cache file to %s",
     454               0 :                          hSession->pszCacheFilename);
     455                 :                     poDS = (OGRDataSource*) OGR_Dr_CreateDataSource(
     456               0 :                             hDriver, hSession->pszCacheFilename, papszOptions);
     457                 :                 }
     458                 : 
     459              16 :                 CSLDestroy(papszOptions);
     460                 :             }
     461                 :         }
     462                 : 
     463             128 :         CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS", pszOldVal);
     464                 : 
     465             128 :         if( poDS == NULL )
     466              16 :             return NULL;
     467                 : 
     468             112 :         hSession->poDS = poDS;
     469                 :     }
     470                 : 
     471             130 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     472             130 :     OGRLayer* poLayer = poDS->GetLayerByName(CACHE_LAYER_NAME);
     473             130 :     CPLPopErrorHandler();
     474                 : 
     475             130 :     if( bCreateIfNecessary && poLayer == NULL )
     476                 :     {
     477              16 :         char** papszOptions = NULL;
     478              16 :         if( EQUAL(osExt, "SQLITE") )
     479                 :         {
     480                 :             papszOptions = CSLAddNameValue(papszOptions, "COMPRESS_COLUMNS",
     481               8 :                                            FIELD_BLOB);
     482                 :         }
     483                 :         poLayer = poDS->CreateLayer(
     484              16 :                         CACHE_LAYER_NAME, NULL, wkbNone, papszOptions);
     485              16 :         CSLDestroy(papszOptions);
     486                 : 
     487              16 :         if( poLayer != NULL )
     488                 :         {
     489              16 :             OGRFieldDefn oFieldDefnURL(FIELD_URL, OFTString);
     490              16 :             poLayer->CreateField(&oFieldDefnURL);
     491              16 :             OGRFieldDefn oFieldDefnBlob(FIELD_BLOB, OFTString);
     492              16 :             poLayer->CreateField(&oFieldDefnBlob);
     493              16 :             if( EQUAL(osExt, "SQLITE") ||
     494                 :                 EQUALN(hSession->pszCacheFilename, "PG:", 3) )
     495                 :             {
     496                 :                 const char* pszSQL =
     497                 :                     CPLSPrintf( "CREATE INDEX idx_%s_%s ON %s(%s)",
     498               8 :                                 FIELD_URL, poLayer->GetName(),
     499              16 :                                 poLayer->GetName(), FIELD_URL );
     500               8 :                 poDS->ExecuteSQL(pszSQL, NULL, NULL);
     501              16 :             }
     502                 :         }
     503                 :     }
     504                 : 
     505             130 :     int nIdxBlob = -1;
     506             390 :     if( poLayer == NULL ||
     507             130 :         poLayer->GetLayerDefn()->GetFieldIndex(FIELD_URL) < 0 ||
     508             130 :         (nIdxBlob = poLayer->GetLayerDefn()->GetFieldIndex(FIELD_BLOB)) < 0 )
     509                 :     {
     510               0 :         return NULL;
     511                 :     }
     512                 : 
     513             130 :     if( pnIdxBlob )
     514             130 :         *pnIdxBlob = nIdxBlob;
     515                 : 
     516             130 :     return poLayer;
     517                 : }
     518                 : 
     519                 : /************************************************************************/
     520                 : /*                        OGRGeocodeGetFromCache()                      */
     521                 : /************************************************************************/
     522                 : 
     523             112 : static char* OGRGeocodeGetFromCache(OGRGeocodingSessionH hSession,
     524                 :                                     const char* pszURL)
     525                 : {
     526             112 :     CPLMutexHolderD(&hMutex);
     527                 : 
     528             112 :     int nIdxBlob = -1;
     529             112 :     OGRLayer* poLayer = OGRGeocodeGetCacheLayer(hSession, FALSE, &nIdxBlob);
     530             112 :     if( poLayer == NULL )
     531              16 :         return NULL;
     532                 : 
     533              96 :     char* pszSQLEscapedURL = CPLEscapeString(pszURL, -1, CPLES_SQL);
     534              96 :     poLayer->SetAttributeFilter(CPLSPrintf("%s='%s'", FIELD_URL, pszSQLEscapedURL));
     535              96 :     CPLFree(pszSQLEscapedURL);
     536                 : 
     537              96 :     char* pszRet = NULL;
     538              96 :     OGRFeature* poFeature = poLayer->GetNextFeature();
     539              96 :     if( poFeature != NULL )
     540                 :     {
     541              78 :         if( poFeature->IsFieldSet(nIdxBlob) )
     542              78 :             pszRet = CPLStrdup(poFeature->GetFieldAsString(nIdxBlob));
     543              78 :         OGRFeature::DestroyFeature(poFeature);
     544                 :     }
     545                 : 
     546              96 :     return pszRet;
     547                 : }
     548                 : 
     549                 : /************************************************************************/
     550                 : /*                        OGRGeocodePutIntoCache()                      */
     551                 : /************************************************************************/
     552                 : 
     553              34 : static int OGRGeocodePutIntoCache(OGRGeocodingSessionH hSession,
     554                 :                                   const char* pszURL,
     555                 :                                   const char* pszContent)
     556                 : {
     557              34 :     CPLMutexHolderD(&hMutex);
     558                 : 
     559              34 :     int nIdxBlob = -1;
     560              34 :     OGRLayer* poLayer = OGRGeocodeGetCacheLayer(hSession, TRUE, &nIdxBlob);
     561              34 :     if( poLayer == NULL )
     562               0 :         return FALSE;
     563                 : 
     564              34 :     OGRFeature* poFeature = new OGRFeature(poLayer->GetLayerDefn());
     565              34 :     poFeature->SetField(FIELD_URL, pszURL);
     566              34 :     poFeature->SetField(FIELD_BLOB, pszContent);
     567              34 :     int bRet = poLayer->CreateFeature(poFeature) == OGRERR_NONE;
     568              34 :     delete poFeature;
     569                 : 
     570              34 :     return bRet;
     571                 : }
     572                 : 
     573                 : /************************************************************************/
     574                 : /*                        OGRGeocodeMakeRawLayer()                      */
     575                 : /************************************************************************/
     576                 : 
     577               0 : static OGRLayerH OGRGeocodeMakeRawLayer(const char* pszContent)
     578                 : {
     579               0 :     OGRMemLayer* poLayer = new OGRMemLayer( "result", NULL, wkbNone );
     580               0 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
     581               0 :     OGRFieldDefn oFieldDefnRaw("raw", OFTString);
     582               0 :     poLayer->CreateField(&oFieldDefnRaw);
     583               0 :     OGRFeature* poFeature = new OGRFeature(poFDefn);
     584               0 :     poFeature->SetField("raw", pszContent);
     585               0 :     poLayer->CreateFeature(poFeature);
     586               0 :     delete poFeature;
     587               0 :     return (OGRLayerH) poLayer;
     588                 : }
     589                 : 
     590                 : /************************************************************************/
     591                 : /*                  OGRGeocodeBuildLayerNominatim()                     */
     592                 : /************************************************************************/
     593                 : 
     594              40 : static OGRLayerH OGRGeocodeBuildLayerNominatim(CPLXMLNode* psSearchResults,
     595                 :                                                const char* pszContent,
     596                 :                                                int bAddRawFeature)
     597                 : {
     598              40 :     OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbUnknown );
     599              40 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
     600                 : 
     601              40 :     CPLXMLNode* psPlace = psSearchResults->psChild;
     602             144 :     while( psPlace != NULL )
     603                 :     {
     604              64 :         if( psPlace->eType == CXT_Element &&
     605                 :             (strcmp(psPlace->pszValue, "place") == 0 || /* Nominatim */
     606                 :              strcmp(psPlace->pszValue, "geoname") == 0 /* Geonames */) )
     607                 :         {
     608              36 :             int bFoundLat = FALSE, bFoundLon = FALSE;
     609              36 :             double dfLat = 0.0, dfLon = 0.0;
     610                 : 
     611                 :             /* First iteration to add fields */
     612              36 :             CPLXMLNode* psChild = psPlace->psChild;
     613             384 :             while( psChild != NULL )
     614                 :             {
     615             312 :                 const char* pszName = psChild->pszValue;
     616             312 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     617             312 :                 if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     618                 :                     poFDefn->GetFieldIndex(pszName) < 0 &&
     619                 :                     strcmp(pszName, "geotext") != 0 )
     620                 :                 {
     621             312 :                     OGRFieldDefn oFieldDefn(pszName, OFTString);
     622             312 :                     if( strcmp(pszName, "place_rank") == 0 )
     623                 :                     {
     624               0 :                         oFieldDefn.SetType(OFTInteger);
     625                 :                     }
     626             312 :                     else if( strcmp(pszName, "lat") == 0 )
     627                 :                     {
     628              36 :                         if( pszVal != NULL )
     629                 :                         {
     630              36 :                             bFoundLat = TRUE;
     631              36 :                             dfLat = CPLAtofM(pszVal);
     632                 :                         }
     633              36 :                         oFieldDefn.SetType(OFTReal);
     634                 :                     }
     635             276 :                     else if( strcmp(pszName, "lon") == 0 ||  /* Nominatim */
     636                 :                              strcmp(pszName, "lng") == 0 /* Geonames */ )
     637                 :                     {
     638              36 :                         if( pszVal != NULL )
     639                 :                         {
     640              36 :                             bFoundLon = TRUE;
     641              36 :                             dfLon = CPLAtofM(pszVal);
     642                 :                         }
     643              36 :                         oFieldDefn.SetType(OFTReal);
     644                 :                     }
     645             312 :                     poLayer->CreateField(&oFieldDefn);
     646                 :                 }
     647             312 :                 psChild = psChild->psNext;
     648                 :             }
     649                 : 
     650              36 :             if( bAddRawFeature )
     651                 :             {
     652               6 :                 OGRFieldDefn oFieldDefnRaw("raw", OFTString);
     653               6 :                 poLayer->CreateField(&oFieldDefnRaw);
     654                 :             }
     655                 : 
     656                 :             /* Second iteration to fill the feature */
     657              36 :             OGRFeature* poFeature = new OGRFeature(poFDefn);
     658              36 :             psChild = psPlace->psChild;
     659             384 :             while( psChild != NULL )
     660                 :             {
     661                 :                 int nIdx;
     662             312 :                 const char* pszName = psChild->pszValue;
     663             312 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     664             312 :                 if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) )
     665                 :                 {
     666                 :                     // do nothing
     667                 :                 }
     668             312 :                 else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
     669                 :                 {
     670             312 :                     if( pszVal != NULL )
     671             312 :                         poFeature->SetField(nIdx, pszVal);
     672                 :                 }
     673               0 :                 else if( strcmp(pszName, "geotext") == 0 )
     674                 :                 {
     675               0 :                     char* pszWKT = (char*) pszVal;
     676               0 :                     if( pszWKT != NULL )
     677                 :                     {
     678               0 :                         OGRGeometry* poGeometry = NULL;
     679                 :                         OGRGeometryFactory::createFromWkt(&pszWKT, NULL,
     680               0 :                                                           &poGeometry);
     681               0 :                         if( poGeometry )
     682               0 :                             poFeature->SetGeometryDirectly(poGeometry);
     683                 :                     }
     684                 :                 }
     685             312 :                 psChild = psChild->psNext;
     686                 :             }
     687                 : 
     688              36 :             if( bAddRawFeature )
     689                 :             {
     690               6 :                 CPLXMLNode* psOldNext = psPlace->psNext;
     691               6 :                 psPlace->psNext = NULL;
     692               6 :                 char* pszXML = CPLSerializeXMLTree(psPlace);
     693               6 :                 psPlace->psNext = psOldNext;
     694                 : 
     695               6 :                 poFeature->SetField("raw", pszXML);
     696               6 :                 CPLFree(pszXML);
     697                 :             }
     698                 : 
     699                 :             /* If we didn't found an explicit geometry, build it from */
     700                 :             /* the 'lon' and 'lat' attributes. */
     701              36 :             if( poFeature->GetGeometryRef() == NULL && bFoundLon && bFoundLat )
     702              36 :                 poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
     703                 : 
     704              36 :             poLayer->CreateFeature(poFeature);
     705              36 :             delete poFeature;
     706                 :         }
     707              64 :         psPlace = psPlace->psNext;
     708                 :     }
     709              40 :     return (OGRLayerH) poLayer;
     710                 : }
     711                 : 
     712                 : /************************************************************************/
     713                 : /*               OGRGeocodeReverseBuildLayerNominatim()                 */
     714                 : /************************************************************************/
     715                 : 
     716              12 : static OGRLayerH OGRGeocodeReverseBuildLayerNominatim(CPLXMLNode* psReverseGeocode,
     717                 :                                                       const char* pszContent,
     718                 :                                                       int bAddRawFeature)
     719                 : {
     720              12 :     CPLXMLNode* psResult = CPLGetXMLNode(psReverseGeocode, "result");
     721              12 :     CPLXMLNode* psAddressParts = CPLGetXMLNode(psReverseGeocode, "addressparts");
     722              12 :     if( psResult == NULL || psAddressParts == NULL )
     723                 :     {
     724               0 :         return NULL;
     725                 :     }
     726                 : 
     727              12 :     OGRMemLayer* poLayer = new OGRMemLayer( "result", NULL, wkbNone );
     728              12 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
     729                 : 
     730              12 :     int bFoundLat = FALSE, bFoundLon = FALSE;
     731              12 :     double dfLat = 0.0, dfLon = 0.0;
     732                 : 
     733                 :     /* First iteration to add fields */
     734              12 :     CPLXMLNode* psChild = psResult->psChild;
     735             108 :     while( psChild != NULL )
     736                 :     {
     737              84 :         const char* pszName = psChild->pszValue;
     738              84 :         const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     739              84 :         if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     740                 :             poFDefn->GetFieldIndex(pszName) < 0 )
     741                 :         {
     742              72 :             OGRFieldDefn oFieldDefn(pszName, OFTString);
     743              72 :             if( strcmp(pszName, "lat") == 0 )
     744                 :             {
     745              12 :                 if( pszVal != NULL )
     746                 :                 {
     747              12 :                     bFoundLat = TRUE;
     748              12 :                     dfLat = CPLAtofM(pszVal);
     749                 :                 }
     750              12 :                 oFieldDefn.SetType(OFTReal);
     751                 :             }
     752              60 :             else if( strcmp(pszName, "lon") == 0 )
     753                 :             {
     754              12 :                 if( pszVal != NULL )
     755                 :                 {
     756              12 :                     bFoundLon = TRUE;
     757              12 :                     dfLon = CPLAtofM(pszVal);
     758                 :                 }
     759              12 :                 oFieldDefn.SetType(OFTReal);
     760                 :             }
     761              72 :             poLayer->CreateField(&oFieldDefn);
     762                 :         }
     763              84 :         psChild = psChild->psNext;
     764                 :     }
     765                 : 
     766              12 :     OGRFieldDefn oFieldDefn("display_name", OFTString);
     767              12 :     poLayer->CreateField(&oFieldDefn);
     768                 : 
     769              12 :     psChild = psAddressParts->psChild;
     770             120 :     while( psChild != NULL )
     771                 :     {
     772              96 :         const char* pszName = psChild->pszValue;
     773              96 :         if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     774                 :             poFDefn->GetFieldIndex(pszName) < 0 )
     775                 :         {
     776              96 :             OGRFieldDefn oFieldDefn(pszName, OFTString);
     777              96 :             poLayer->CreateField(&oFieldDefn);
     778                 :         }
     779              96 :         psChild = psChild->psNext;
     780                 :     }
     781                 : 
     782              12 :     if( bAddRawFeature )
     783                 :     {
     784               2 :         OGRFieldDefn oFieldDefnRaw("raw", OFTString);
     785               2 :         poLayer->CreateField(&oFieldDefnRaw);
     786                 :     }
     787                 : 
     788                 :     /* Second iteration to fill the feature */
     789              12 :     OGRFeature* poFeature = new OGRFeature(poFDefn);
     790              12 :     psChild = psResult->psChild;
     791             108 :     while( psChild != NULL )
     792                 :     {
     793                 :         int nIdx;
     794              84 :         const char* pszName = psChild->pszValue;
     795              84 :         const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     796              84 :         if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     797                 :             (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
     798                 :         {
     799              72 :             if( pszVal != NULL )
     800              72 :                 poFeature->SetField(nIdx, pszVal);
     801                 :         }
     802              84 :         psChild = psChild->psNext;
     803                 :     }
     804                 : 
     805              12 :     const char* pszVal = CPLGetXMLValue(psResult, NULL, NULL);
     806              12 :     if( pszVal != NULL )
     807              12 :         poFeature->SetField("display_name", pszVal);
     808                 : 
     809              12 :     psChild = psAddressParts->psChild;
     810             120 :     while( psChild != NULL )
     811                 :     {
     812                 :         int nIdx;
     813              96 :         const char* pszName = psChild->pszValue;
     814              96 :         const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     815              96 :         if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     816                 :             (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
     817                 :         {
     818              96 :             if( pszVal != NULL )
     819              96 :                 poFeature->SetField(nIdx, pszVal);
     820                 :         }
     821              96 :         psChild = psChild->psNext;
     822                 :     }
     823                 : 
     824              12 :     if( bAddRawFeature )
     825                 :     {
     826               2 :         poFeature->SetField("raw", pszContent);
     827                 :     }
     828                 : 
     829                 :     /* If we didn't found an explicit geometry, build it from */
     830                 :     /* the 'lon' and 'lat' attributes. */
     831              12 :     if( poFeature->GetGeometryRef() == NULL && bFoundLon && bFoundLat )
     832              12 :         poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
     833                 : 
     834              12 :     poLayer->CreateFeature(poFeature);
     835              12 :     delete poFeature;
     836                 : 
     837              12 :     return (OGRLayerH) poLayer;
     838                 : }
     839                 : 
     840                 : /************************************************************************/
     841                 : /*                   OGRGeocodeBuildLayerYahoo()                        */
     842                 : /************************************************************************/
     843                 : 
     844              26 : static OGRLayerH OGRGeocodeBuildLayerYahoo(CPLXMLNode* psResultSet,
     845                 :                                            const char* pszContent,
     846                 :                                            int bAddRawFeature)
     847                 : {
     848              26 :     OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbPoint );
     849              26 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
     850                 : 
     851              26 :     CPLXMLNode* psPlace = psResultSet->psChild;
     852             284 :     while( psPlace != NULL )
     853                 :     {
     854             232 :         if( psPlace->eType == CXT_Element &&
     855                 :             strcmp(psPlace->pszValue, "Result") == 0 )
     856                 :         {
     857              24 :             int bFoundLat = FALSE, bFoundLon = FALSE;
     858              24 :             double dfLat = 0.0, dfLon = 0.0;
     859                 : 
     860                 :             /* First iteration to add fields */
     861              24 :             CPLXMLNode* psChild = psPlace->psChild;
     862             744 :             while( psChild != NULL )
     863                 :             {
     864             696 :                 const char* pszName = psChild->pszValue;
     865             696 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     866             696 :                 if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     867                 :                     poFDefn->GetFieldIndex(pszName) < 0 )
     868                 :                 {
     869             696 :                     OGRFieldDefn oFieldDefn(pszName, OFTString);
     870             696 :                     if( strcmp(pszName, "latitude") == 0 )
     871                 :                     {
     872              24 :                         if( pszVal != NULL )
     873                 :                         {
     874              24 :                             bFoundLat = TRUE;
     875              24 :                             dfLat = CPLAtofM(pszVal);
     876                 :                         }
     877              24 :                         oFieldDefn.SetType(OFTReal);
     878                 :                     }
     879             672 :                     else if( strcmp(pszName, "longitude") == 0 )
     880                 :                     {
     881              24 :                         if( pszVal != NULL )
     882                 :                         {
     883              24 :                             bFoundLon = TRUE;
     884              24 :                             dfLon = CPLAtofM(pszVal);
     885                 :                         }
     886              24 :                         oFieldDefn.SetType(OFTReal);
     887                 :                     }
     888             696 :                     poLayer->CreateField(&oFieldDefn);
     889                 :                 }
     890             696 :                 psChild = psChild->psNext;
     891                 :             }
     892                 : 
     893              24 :             OGRFieldDefn oFieldDefnDisplayName("display_name", OFTString);
     894              24 :             poLayer->CreateField(&oFieldDefnDisplayName);
     895                 : 
     896              24 :             if( bAddRawFeature )
     897                 :             {
     898               4 :                 OGRFieldDefn oFieldDefnRaw("raw", OFTString);
     899               4 :                 poLayer->CreateField(&oFieldDefnRaw);
     900                 :             }
     901                 : 
     902                 :             /* Second iteration to fill the feature */
     903              24 :             OGRFeature* poFeature = new OGRFeature(poFDefn);
     904              24 :             psChild = psPlace->psChild;
     905             744 :             while( psChild != NULL )
     906                 :             {
     907                 :                 int nIdx;
     908             696 :                 const char* pszName = psChild->pszValue;
     909             696 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     910             696 :                 if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) )
     911                 :                 {
     912                 :                     // do nothing
     913                 :                 }
     914             696 :                 else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
     915                 :                 {
     916             696 :                     if( pszVal != NULL )
     917             456 :                         poFeature->SetField(nIdx, pszVal);
     918                 :                 }
     919             696 :                 psChild = psChild->psNext;
     920                 :             }
     921                 : 
     922              24 :             CPLString osDisplayName;
     923             120 :             for(int i=1;;i++)
     924                 :             {
     925             120 :                 int nIdx = poFDefn->GetFieldIndex(CPLSPrintf("line%d", i));
     926             120 :                 if( nIdx < 0 )
     927                 :                     break;
     928              96 :                 if( poFeature->IsFieldSet(nIdx) )
     929                 :                 {
     930              60 :                     if( osDisplayName.size() )
     931              36 :                         osDisplayName += ", ";
     932              60 :                     osDisplayName += poFeature->GetFieldAsString(nIdx);
     933                 :                 }
     934                 :             }
     935              48 :             poFeature->SetField("display_name", osDisplayName.c_str());
     936                 : 
     937              24 :             if( bAddRawFeature )
     938                 :             {
     939               4 :                 CPLXMLNode* psOldNext = psPlace->psNext;
     940               4 :                 psPlace->psNext = NULL;
     941               4 :                 char* pszXML = CPLSerializeXMLTree(psPlace);
     942               4 :                 psPlace->psNext = psOldNext;
     943                 : 
     944               4 :                 poFeature->SetField("raw", pszXML);
     945               4 :                 CPLFree(pszXML);
     946                 :             }
     947                 : 
     948                 :             /* Build geometry from the 'lon' and 'lat' attributes. */
     949              24 :             if( bFoundLon && bFoundLat )
     950              24 :                 poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
     951                 : 
     952              24 :             poLayer->CreateFeature(poFeature);
     953              24 :             delete poFeature;
     954                 :         }
     955             232 :         psPlace = psPlace->psNext;
     956                 :     }
     957              26 :     return (OGRLayerH) poLayer;
     958                 : }
     959                 : 
     960                 : /************************************************************************/
     961                 : /*                   OGRGeocodeBuildLayerBing()                         */
     962                 : /************************************************************************/
     963                 : 
     964              26 : static OGRLayerH OGRGeocodeBuildLayerBing (CPLXMLNode* psResponse,
     965                 :                                            const char* pszContent,
     966                 :                                            int bAddRawFeature)
     967                 : {
     968              26 :     CPLXMLNode* psResources = CPLGetXMLNode(psResponse, "ResourceSets.ResourceSet.Resources");
     969              26 :     if( psResources == NULL )
     970               0 :         return NULL;
     971                 : 
     972              26 :     OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbPoint );
     973              26 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
     974                 : 
     975              26 :     CPLXMLNode* psPlace = psResources->psChild;
     976              76 :     while( psPlace != NULL )
     977                 :     {
     978              24 :         if( psPlace->eType == CXT_Element &&
     979                 :             strcmp(psPlace->pszValue, "Location") == 0 )
     980                 :         {
     981              24 :             int bFoundLat = FALSE, bFoundLon = FALSE;
     982              24 :             double dfLat = 0.0, dfLon = 0.0;
     983                 : 
     984                 :             /* First iteration to add fields */
     985              24 :             CPLXMLNode* psChild = psPlace->psChild;
     986             168 :             while( psChild != NULL )
     987                 :             {
     988             120 :                 const char* pszName = psChild->pszValue;
     989             120 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
     990             120 :                 if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) &&
     991                 :                     strcmp(pszName, "BoundingBox") != 0 &&
     992                 :                     strcmp(pszName, "GeocodePoint") != 0 &&
     993                 :                     poFDefn->GetFieldIndex(pszName) < 0 )
     994                 :                 {
     995             120 :                     if( psChild->psChild != NULL &&
     996                 :                         psChild->psChild->eType == CXT_Element )
     997                 :                     {
     998              48 :                         CPLXMLNode* psSubChild = psChild->psChild;
     999             264 :                         while( psSubChild != NULL )
    1000                 :                         {
    1001             168 :                             pszName = psSubChild->pszValue;
    1002             168 :                             pszVal = CPLGetXMLValue(psSubChild, NULL, NULL);
    1003             168 :                             if( (psSubChild->eType == CXT_Element ||
    1004                 :                                  psSubChild->eType == CXT_Attribute) &&
    1005                 :                                 poFDefn->GetFieldIndex(pszName) < 0 )
    1006                 :                             {
    1007             168 :                                 OGRFieldDefn oFieldDefn(pszName, OFTString);
    1008             168 :                                 if( strcmp(pszName, "Latitude") == 0 )
    1009                 :                                 {
    1010              24 :                                     if( pszVal != NULL )
    1011                 :                                     {
    1012              24 :                                         bFoundLat = TRUE;
    1013              24 :                                         dfLat = CPLAtofM(pszVal);
    1014                 :                                     }
    1015              24 :                                     oFieldDefn.SetType(OFTReal);
    1016                 :                                 }
    1017             144 :                                 else if( strcmp(pszName, "Longitude") == 0 )
    1018                 :                                 {
    1019              24 :                                     if( pszVal != NULL )
    1020                 :                                     {
    1021              24 :                                         bFoundLon = TRUE;
    1022              24 :                                         dfLon = CPLAtofM(pszVal);
    1023                 :                                     }
    1024              24 :                                     oFieldDefn.SetType(OFTReal);
    1025                 :                                 }
    1026             168 :                                 poLayer->CreateField(&oFieldDefn);
    1027                 :                             }
    1028             168 :                             psSubChild = psSubChild->psNext;
    1029                 :                         }
    1030                 :                     }
    1031                 :                     else
    1032                 :                     {
    1033              24 :                         OGRFieldDefn oFieldDefn(pszName, OFTString);
    1034              24 :                         poLayer->CreateField(&oFieldDefn);
    1035                 :                     }
    1036                 :                 }
    1037             120 :                 psChild = psChild->psNext;
    1038                 :             }
    1039                 : 
    1040              24 :             if( bAddRawFeature )
    1041                 :             {
    1042               4 :                 OGRFieldDefn oFieldDefnRaw("raw", OFTString);
    1043               4 :                 poLayer->CreateField(&oFieldDefnRaw);
    1044                 :             }
    1045                 : 
    1046                 :             /* Second iteration to fill the feature */
    1047              24 :             OGRFeature* poFeature = new OGRFeature(poFDefn);
    1048              24 :             psChild = psPlace->psChild;
    1049             168 :             while( psChild != NULL )
    1050                 :             {
    1051                 :                 int nIdx;
    1052             120 :                 const char* pszName = psChild->pszValue;
    1053             120 :                 const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL);
    1054             120 :                 if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) )
    1055                 :                 {
    1056                 :                     // do nothing
    1057                 :                 }
    1058             120 :                 else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
    1059                 :                 {
    1060              24 :                     if( pszVal != NULL )
    1061              24 :                         poFeature->SetField(nIdx, pszVal);
    1062                 :                 }
    1063              96 :                 else if( strcmp(pszName, "BoundingBox") != 0 &&
    1064                 :                          strcmp(pszName, "GeocodePoint") != 0 &&
    1065                 :                          psChild->psChild != NULL &&
    1066                 :                          psChild->psChild->eType == CXT_Element )
    1067                 :                 {
    1068              48 :                     CPLXMLNode* psSubChild = psChild->psChild;
    1069             264 :                     while( psSubChild != NULL )
    1070                 :                     {
    1071             168 :                         pszName = psSubChild->pszValue;
    1072             168 :                         pszVal = CPLGetXMLValue(psSubChild, NULL, NULL);
    1073             168 :                         if( (psSubChild->eType == CXT_Element ||
    1074                 :                              psSubChild->eType == CXT_Attribute) &&
    1075                 :                             (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 )
    1076                 :                         {
    1077             168 :                             if( pszVal != NULL )
    1078             168 :                                 poFeature->SetField(nIdx, pszVal);
    1079                 :                         }
    1080             168 :                         psSubChild = psSubChild->psNext;
    1081                 :                     }
    1082                 :                 }
    1083             120 :                 psChild = psChild->psNext;
    1084                 :             }
    1085                 : 
    1086              24 :             if( bAddRawFeature )
    1087                 :             {
    1088               4 :                 CPLXMLNode* psOldNext = psPlace->psNext;
    1089               4 :                 psPlace->psNext = NULL;
    1090               4 :                 char* pszXML = CPLSerializeXMLTree(psPlace);
    1091               4 :                 psPlace->psNext = psOldNext;
    1092                 : 
    1093               4 :                 poFeature->SetField("raw", pszXML);
    1094               4 :                 CPLFree(pszXML);
    1095                 :             }
    1096                 : 
    1097                 :             /* Build geometry from the 'lon' and 'lat' attributes. */
    1098              24 :             if( bFoundLon && bFoundLat )
    1099              24 :                 poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
    1100                 : 
    1101              24 :             poLayer->CreateFeature(poFeature);
    1102              24 :             delete poFeature;
    1103                 :         }
    1104              24 :         psPlace = psPlace->psNext;
    1105                 :     }
    1106              26 :     return (OGRLayerH) poLayer;
    1107                 : }
    1108                 : 
    1109                 : /************************************************************************/
    1110                 : /*                         OGRGeocodeBuildLayer()                       */
    1111                 : /************************************************************************/
    1112                 : 
    1113             112 : static OGRLayerH OGRGeocodeBuildLayer(const char* pszContent,
    1114                 :                                       int bAddRawFeature)
    1115                 : {
    1116             112 :     OGRLayerH hLayer = NULL;
    1117             112 :     CPLXMLNode* psRoot = CPLParseXMLString( pszContent );
    1118             112 :     if( psRoot != NULL )
    1119                 :     {
    1120                 :         CPLXMLNode* psSearchResults;
    1121                 :         CPLXMLNode* psReverseGeocode;
    1122                 :         CPLXMLNode* psGeonames;
    1123                 :         CPLXMLNode* psResultSet;
    1124                 :         CPLXMLNode* psResponse;
    1125             104 :         if( (psSearchResults =
    1126                 :                         CPLSearchXMLNode(psRoot, "=searchresults")) != NULL )
    1127                 :             hLayer = OGRGeocodeBuildLayerNominatim(psSearchResults,
    1128                 :                                                    pszContent,
    1129              14 :                                                    bAddRawFeature);
    1130              90 :         else if( (psReverseGeocode =
    1131                 :                     CPLSearchXMLNode(psRoot, "=reversegeocode")) != NULL )
    1132                 :             hLayer = OGRGeocodeReverseBuildLayerNominatim(psReverseGeocode,
    1133                 :                                                           pszContent,
    1134              12 :                                                           bAddRawFeature);
    1135              78 :         else if( (psGeonames =
    1136                 :                         CPLSearchXMLNode(psRoot, "=geonames")) != NULL )
    1137                 :             hLayer = OGRGeocodeBuildLayerNominatim(psGeonames,
    1138                 :                                                    pszContent,
    1139              26 :                                                    bAddRawFeature);
    1140              52 :         else if( (psResultSet =
    1141                 :                         CPLSearchXMLNode(psRoot, "=ResultSet")) != NULL )
    1142                 :             hLayer = OGRGeocodeBuildLayerYahoo(psResultSet,
    1143                 :                                                pszContent,
    1144              26 :                                                bAddRawFeature);
    1145              26 :         else if( (psResponse =
    1146                 :                     CPLSearchXMLNode(psRoot, "=Response")) != NULL )
    1147                 :             hLayer = OGRGeocodeBuildLayerBing (psResponse,
    1148                 :                                                pszContent,
    1149              26 :                                                bAddRawFeature);
    1150             104 :         CPLDestroyXMLNode( psRoot );
    1151                 :     }
    1152             112 :     if( hLayer == NULL && bAddRawFeature )
    1153               0 :         hLayer = OGRGeocodeMakeRawLayer(pszContent);
    1154             112 :     return hLayer;
    1155                 : }
    1156                 : 
    1157                 : /************************************************************************/
    1158                 : /*                         OGRGeocodeCommon()                           */
    1159                 : /************************************************************************/
    1160                 : 
    1161             112 : static OGRLayerH OGRGeocodeCommon(OGRGeocodingSessionH hSession,
    1162                 :                                   CPLString osURL,
    1163                 :                                   char** papszOptions)
    1164                 : {
    1165                 :     /* Only documented to work with OSM Nominatim. */
    1166             112 :     if( hSession->pszLanguage != NULL )
    1167                 :     {
    1168               0 :         osURL += "&accept-language=";
    1169               0 :         osURL += hSession->pszLanguage;
    1170                 :     }
    1171                 : 
    1172                 :     const char* pszExtraQueryParameters = OGRGeocodeGetParameter(
    1173             112 :                                 papszOptions, "EXTRA_QUERY_PARAMETERS", NULL);
    1174             112 :     if( pszExtraQueryParameters != NULL )
    1175                 :     {
    1176               0 :         osURL += "&";
    1177               0 :         osURL += pszExtraQueryParameters;
    1178                 :     }
    1179                 : 
    1180             112 :     CPLString osURLWithEmail = osURL;
    1181             140 :     if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") &&
    1182                 :         hSession->pszEmail != NULL )
    1183                 :     {
    1184                 :         char* pszEscapedEmail = CPLEscapeString(hSession->pszEmail,
    1185              28 :                                                 -1, CPLES_URL);
    1186              28 :         osURLWithEmail = osURL + "&email=" + pszEscapedEmail;
    1187              28 :         CPLFree(pszEscapedEmail);
    1188                 :     }
    1189             112 :     else if( EQUAL(hSession->pszGeocodingService, "GEONAMES") &&
    1190                 :              hSession->pszUserName != NULL )
    1191                 :     {
    1192                 :         char* pszEscaped = CPLEscapeString(hSession->pszUserName,
    1193              28 :                                                 -1, CPLES_URL);
    1194              28 :         osURLWithEmail = osURL + "&username=" + pszEscaped;
    1195              28 :         CPLFree(pszEscaped);
    1196                 :     }
    1197              56 :     else if( EQUAL(hSession->pszGeocodingService, "BING") &&
    1198                 :              hSession->pszKey != NULL )
    1199                 :     {
    1200                 :         char* pszEscaped = CPLEscapeString(hSession->pszKey,
    1201              28 :                                                 -1, CPLES_URL);
    1202              28 :         osURLWithEmail = osURL + "&key=" + pszEscaped;
    1203              28 :         CPLFree(pszEscaped);
    1204                 :     }
    1205                 : 
    1206                 :     int bAddRawFeature =
    1207             112 :         CSLTestBoolean(OGRGeocodeGetParameter(papszOptions, "RAW_FEATURE", "NO"));
    1208                 : 
    1209             112 :     OGRLayerH hLayer = NULL;
    1210                 : 
    1211             112 :     char* pszCachedResult = NULL;
    1212             112 :     if( hSession->bReadCache )
    1213             112 :         pszCachedResult = OGRGeocodeGetFromCache(hSession, osURL);
    1214             112 :     if( pszCachedResult == NULL )
    1215                 :     {
    1216                 :         CPLHTTPResult* psResult;
    1217                 : 
    1218              34 :         double* pdfLastQueryTime = NULL;
    1219              34 :         if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") )
    1220              10 :             pdfLastQueryTime = &dfLastQueryTimeStampOSMNominatim;
    1221              24 :         else if( EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM") )
    1222               0 :             pdfLastQueryTime = &dfLastQueryTimeStampMapQuestNominatim;
    1223                 : 
    1224              34 :         char** papszHTTPOptions = NULL;
    1225              34 :         CPLString osHeaders;
    1226              34 :         osHeaders = "User-Agent: ";
    1227              34 :         osHeaders += hSession->pszApplication;
    1228              34 :         if( hSession->pszLanguage != NULL )
    1229                 :         {
    1230               0 :             osHeaders += "\r\nAccept-Language: ";
    1231               0 :             osHeaders += hSession->pszLanguage;
    1232                 :         }
    1233                 :         papszHTTPOptions = CSLAddNameValue(papszHTTPOptions, "HEADERS",
    1234              34 :                                            osHeaders.c_str());
    1235                 : 
    1236              34 :         if( pdfLastQueryTime != NULL )
    1237                 :         {
    1238              10 :             CPLMutexHolderD(&hMutex);
    1239                 :             struct timeval tv;
    1240                 : 
    1241              10 :             gettimeofday(&tv, NULL);
    1242              10 :             double dfCurrentTime = tv.tv_sec + tv.tv_usec / 1e6;
    1243              10 :             if( dfCurrentTime < *pdfLastQueryTime +
    1244                 :                                     hSession->dfDelayBetweenQueries )
    1245                 :             {
    1246                 :                 CPLSleep(*pdfLastQueryTime + hSession->dfDelayBetweenQueries -
    1247               9 :                          dfCurrentTime);
    1248                 :             }
    1249                 : 
    1250              10 :             psResult = CPLHTTPFetch( osURLWithEmail,  papszHTTPOptions );
    1251                 : 
    1252              10 :             gettimeofday(&tv, NULL);
    1253              10 :             *pdfLastQueryTime = tv.tv_sec + tv.tv_usec / 1e6;
    1254                 :         }
    1255                 :         else
    1256              24 :             psResult = CPLHTTPFetch( osURLWithEmail,  papszHTTPOptions );
    1257                 : 
    1258              34 :         CSLDestroy(papszHTTPOptions);
    1259              34 :         papszHTTPOptions = NULL;
    1260                 : 
    1261              34 :         if( psResult == NULL )
    1262                 :         {
    1263                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1264               0 :                      "Query '%s' failed", osURLWithEmail.c_str());
    1265                 :         }
    1266                 :         else
    1267                 :         {
    1268              34 :             const char* pszResult = (const char*) psResult->pabyData;
    1269              34 :             if( pszResult != NULL )
    1270                 :             {
    1271              34 :                 if( hSession->bWriteCache )
    1272              34 :                     OGRGeocodePutIntoCache(hSession, osURL, pszResult);
    1273              34 :                 hLayer = OGRGeocodeBuildLayer(pszResult, bAddRawFeature);
    1274                 :             }
    1275              34 :             CPLHTTPDestroyResult(psResult);
    1276              34 :         }
    1277                 :     }
    1278                 :     else
    1279                 :     {
    1280              78 :         hLayer = OGRGeocodeBuildLayer(pszCachedResult, bAddRawFeature);
    1281              78 :         CPLFree(pszCachedResult);
    1282                 :     }
    1283                 : 
    1284             112 :     return hLayer;
    1285                 : }
    1286                 : 
    1287                 : /************************************************************************/
    1288                 : /*                              OGRGeocode()                            */
    1289                 : /************************************************************************/
    1290                 : 
    1291                 : /**
    1292                 :  * \brief Runs a geocoding request.
    1293                 :  *
    1294                 :  * If the result is not found in cache, a GET request will be sent to resolve
    1295                 :  * the query.
    1296                 :  *
    1297                 :  * Note: most online services have Term of Uses. You are kindly requested
    1298                 :  * to read and follow them. For the OpenStreetMap Nominatim service, this
    1299                 :  * implementation will make sure that no more than one request is sent by
    1300                 :  * second, but there might be other restrictions that you must follow by other
    1301                 :  * means.
    1302                 :  *
    1303                 :  * In case of success, the return of this function is a OGR layer that contain
    1304                 :  * zero, one or several features matching the query. Note that the geometry of the
    1305                 :  * features is not necessarily a point.  The returned layer must be freed with
    1306                 :  * OGRGeocodeFreeResult().
    1307                 :  *
    1308                 :  * Note: this function is also available as the SQL
    1309                 :  * <a href="ogr_sql_sqlite.html#ogr_sql_sqlite_ogr_geocode_function">ogr_geocode()</a>
    1310                 :  * function of the SQL SQLite dialect.
    1311                 :  *
    1312                 :  * The list of recognized options is :
    1313                 :  * <ul>
    1314                 :  * <li>ADDRESSDETAILS=0 or 1: Include a breakdown of the address into elements
    1315                 :  *     Defaults to 1. (Known to work with OSM and MapQuest Nominatim)
    1316                 :  * <li>COUNTRYCODES=code1,code2,...codeN: Limit search results to a specific
    1317                 :  *     country (or a list of countries). The codes must fellow ISO 3166-1, i.e.
    1318                 :  *     gb for United Kingdom, de for Germany, etc.. (Known to work with OSM and MapQuest Nominatim)
    1319                 :  * <li>LIMIT=number: the number of records to return. Unlimited if not specified.
    1320                 :  *     (Known to work with OSM and MapQuest Nominatim)
    1321                 :  * <li>RAW_FEATURE=YES: to specify that a 'raw' field must be added to the returned
    1322                 :  *     feature with the raw XML content.
    1323                 :  * <li> EXTRA_QUERY_PARAMETERS=params: additionnal parameters for the GET request.
    1324                 :  * </ul>
    1325                 :  *
    1326                 :  * @param hSession the geocoding session handle.
    1327                 :  * @param pszQuery the string to geocode.
    1328                 :  * @param papszStructuredQuery unused for now. Must be NULL.
    1329                 :  * @param papszOptions a list of options or NULL.
    1330                 :  *
    1331                 :  * @return a OGR layer with the result(s), or NULL in case of error.
    1332                 :  *         The returned layer must be freed with OGRGeocodeFreeResult().
    1333                 :  *
    1334                 :  * @since GDAL 1.10
    1335                 :  */
    1336              64 : OGRLayerH OGRGeocode(OGRGeocodingSessionH hSession,
    1337                 :                      const char* pszQuery,
    1338                 :                      char** papszStructuredQuery,
    1339                 :                      char** papszOptions)
    1340                 : {
    1341              64 :     VALIDATE_POINTER1( hSession, "OGRGeocode", NULL );
    1342              64 :     if( (pszQuery == NULL && papszStructuredQuery == NULL) ||
    1343                 :         (pszQuery != NULL && papszStructuredQuery != NULL) )
    1344                 :     {
    1345                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1346               0 :                  "Only one of pszQuery or papszStructuredQuery must be set.");
    1347               0 :         return NULL;
    1348                 :     }
    1349                 : 
    1350              64 :     if( papszStructuredQuery != NULL )
    1351                 :     {
    1352                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1353               0 :                  "papszStructuredQuery not yet supported.");
    1354               0 :         return NULL;
    1355                 :     }
    1356                 : 
    1357              64 :     if( hSession->pszQueryTemplate == NULL )
    1358                 :     {
    1359                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1360               0 :                  "QUERY_TEMPLATE parameter not defined");
    1361               0 :         return NULL;
    1362                 :     }
    1363                 : 
    1364              64 :     char* pszEscapedQuery = CPLEscapeString(pszQuery, -1, CPLES_URL);
    1365              64 :     CPLString osURL = CPLSPrintf(hSession->pszQueryTemplate, pszEscapedQuery);
    1366              64 :     CPLFree(pszEscapedQuery);
    1367                 : 
    1368              64 :     if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") ||
    1369                 :         EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM") )
    1370                 :     {
    1371              16 :         const char* pszAddressDetails = OGRGeocodeGetParameter(papszOptions, "ADDRESSDETAILS", "1");
    1372              16 :         osURL += "&addressdetails=";
    1373              16 :         osURL += pszAddressDetails;
    1374                 : 
    1375              16 :         const char* pszCountryCodes = OGRGeocodeGetParameter(papszOptions, "COUNTRYCODES", NULL);
    1376              16 :         if( pszCountryCodes != NULL )
    1377                 :         {
    1378               0 :             osURL += "&countrycodes=";
    1379               0 :             osURL += pszCountryCodes;
    1380                 :         }
    1381                 : 
    1382              16 :         const char* pszLimit = OGRGeocodeGetParameter(papszOptions, "LIMIT", NULL);
    1383              16 :         if( pszLimit != NULL && *pszLimit != '\0' )
    1384                 :         {
    1385              16 :             osURL += "&limit=";
    1386              16 :             osURL += pszLimit;
    1387                 :         }
    1388                 :     }
    1389                 : 
    1390              64 :     return OGRGeocodeCommon(hSession, osURL, papszOptions);
    1391                 : }
    1392                 : 
    1393                 : /************************************************************************/
    1394                 : /*                      OGRGeocodeReverseSubstitute()                   */
    1395                 : /************************************************************************/
    1396                 : 
    1397              48 : static CPLString OGRGeocodeReverseSubstitute(CPLString osURL,
    1398                 :                                              double dfLon, double dfLat)
    1399                 : {
    1400              48 :     size_t iPos = osURL.find("{lon}");
    1401              48 :     if( iPos != std::string::npos )
    1402                 :     {
    1403              48 :         CPLString osEnd(osURL.substr(iPos + 5));
    1404              48 :         osURL = osURL.substr(0,iPos);
    1405              48 :         osURL += CPLSPrintf("%.8f", dfLon);
    1406              48 :         osURL += osEnd;
    1407                 :     }
    1408                 : 
    1409              48 :     iPos = osURL.find("{lat}");
    1410              48 :     if( iPos != std::string::npos )
    1411                 :     {
    1412              48 :         CPLString osEnd(osURL.substr(iPos + 5));
    1413              48 :         osURL = osURL.substr(0,iPos);
    1414              48 :         osURL += CPLSPrintf("%.8f", dfLat);
    1415              48 :         osURL += osEnd;
    1416                 :     }
    1417                 : 
    1418              48 :     return osURL;
    1419                 : }
    1420                 : 
    1421                 : /************************************************************************/
    1422                 : /*                         OGRGeocodeReverse()                          */
    1423                 : /************************************************************************/
    1424                 : 
    1425                 : /**
    1426                 :  * \brief Runs a reverse geocoding request.
    1427                 :  *
    1428                 :  * If the result is not found in cache, a GET request will be sent to resolve
    1429                 :  * the query.
    1430                 :  *
    1431                 :  * Note: most online services have Term of Uses. You are kindly requested
    1432                 :  * to read and follow them. For the OpenStreetMap Nominatim service, this
    1433                 :  * implementation will make sure that no more than one request is sent by
    1434                 :  * second, but there might be other restrictions that you must follow by other
    1435                 :  * means.
    1436                 :  *
    1437                 :  * In case of success, the return of this function is a OGR layer that contain
    1438                 :  * zero, one or several features matching the query. The returned layer must be freed with
    1439                 :  * OGRGeocodeFreeResult().
    1440                 :  *
    1441                 :  * Note: this function is also available as the SQL
    1442                 :  * <a href="ogr_sql_sqlite.html#ogr_sql_sqlite_ogr_geocode_function">ogr_geocode_reverse()</a>
    1443                 :  * function of the SQL SQLite dialect.
    1444                 :  *
    1445                 :  * The list of recognized options is :
    1446                 :  * <ul>
    1447                 :  * <li>ZOOM=a_level: to query a specific zoom level. Only understood by the OSM Nominatim service.
    1448                 :  * <li>RAW_FEATURE=YES: to specify that a 'raw' field must be added to the returned
    1449                 :  *     feature with the raw XML content.
    1450                 :  * <li>EXTRA_QUERY_PARAMETERS=params: additionnal parameters for the GET request
    1451                 :  *     for reverse geocoding.
    1452                 :  * </ul>
    1453                 :  *
    1454                 :  * @param hSession the geocoding session handle.
    1455                 :  * @param dfLon the longitude.
    1456                 :  * @param dfLat the latitude.
    1457                 :  * @param papszOptions a list of options or NULL.
    1458                 :  *
    1459                 :  * @return a OGR layer with the result(s), or NULL in case of error.
    1460                 :  *         The returned layer must be freed with OGRGeocodeFreeResult().
    1461                 :  *
    1462                 :  * @since GDAL 1.10
    1463                 :  */
    1464              48 : OGRLayerH OGRGeocodeReverse(OGRGeocodingSessionH hSession,
    1465                 :                             double dfLon, double dfLat,
    1466                 :                             char** papszOptions)
    1467                 : {
    1468              48 :     VALIDATE_POINTER1( hSession, "OGRGeocodeReverse", NULL );
    1469                 : 
    1470              48 :     if( hSession->pszReverseQueryTemplate == NULL )
    1471                 :     {
    1472                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1473               0 :                  "REVERSE_QUERY_TEMPLATE parameter not defined");
    1474               0 :         return NULL;
    1475                 :     }
    1476                 : 
    1477              48 :     CPLString osURL = hSession->pszReverseQueryTemplate;
    1478              48 :     osURL = OGRGeocodeReverseSubstitute(osURL, dfLon, dfLat);
    1479                 : 
    1480              48 :     if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") )
    1481                 :     {
    1482              12 :         const char* pszZoomLevel = OGRGeocodeGetParameter(papszOptions, "ZOOM", NULL);
    1483              12 :         if( pszZoomLevel != NULL )
    1484                 :         {
    1485               4 :             osURL = osURL + "&zoom=" + pszZoomLevel;
    1486                 :         }
    1487                 :     }
    1488                 : 
    1489              48 :     return OGRGeocodeCommon(hSession, osURL, papszOptions);
    1490                 : }
    1491                 : 
    1492                 : /************************************************************************/
    1493                 : /*                        OGRGeocodeFreeResult()                        */
    1494                 : /************************************************************************/
    1495                 : 
    1496                 : /**
    1497                 :  * \brief Destroys the result of a geocoding request.
    1498                 :  *
    1499                 :  * @param hLayer the layer returned by OGRGeocode() or OGRGeocodeReverse()
    1500                 :  *               to destroy.
    1501                 :  *
    1502                 :  * @since GDAL 1.10
    1503                 :  */
    1504             104 : void OGRGeocodeFreeResult(OGRLayerH hLayer)
    1505                 : {
    1506             104 :     delete (OGRLayer*) hLayer;
    1507             104 : }

Generated by: LCOV version 1.7