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