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 : }
|