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