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 : #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 : * @since GDAL 1.10
234 : */
235 : /* clang-format on */
236 :
237 112 : OGRGeocodingSessionH OGRGeocodeCreateSession(char **papszOptions)
238 : {
239 : OGRGeocodingSessionH hSession = static_cast<OGRGeocodingSessionH>(
240 112 : CPLCalloc(1, sizeof(_OGRGeocodingSessionHS)));
241 :
242 112 : const char *pszCacheFilename = OGRGeocodeGetParameter(
243 : papszOptions, "CACHE_FILE", DEFAULT_CACHE_SQLITE);
244 224 : CPLString osExt = CPLGetExtensionSafe(pszCacheFilename);
245 168 : if (!(STARTS_WITH_CI(pszCacheFilename, "PG:") || EQUAL(osExt, "csv") ||
246 56 : EQUAL(osExt, "sqlite")))
247 : {
248 0 : CPLError(CE_Failure, CPLE_AppDefined,
249 : "Only .csv, .sqlite or PG: datasources are handled for now.");
250 0 : OGRGeocodeDestroySession(hSession);
251 0 : return nullptr;
252 : }
253 112 : hSession->pszCacheFilename = CPLStrdup(pszCacheFilename);
254 :
255 112 : hSession->bReadCache =
256 112 : CPLTestBool(OGRGeocodeGetParameter(papszOptions, "READ_CACHE", "TRUE"));
257 112 : hSession->bWriteCache = CPLTestBool(
258 : OGRGeocodeGetParameter(papszOptions, "WRITE_CACHE", "TRUE"));
259 :
260 : const char *pszGeocodingService =
261 112 : OGRGeocodeGetParameter(papszOptions, "SERVICE", "OSM_NOMINATIM");
262 112 : hSession->pszGeocodingService = CPLStrdup(pszGeocodingService);
263 :
264 : const char *pszEmail =
265 112 : OGRGeocodeGetParameter(papszOptions, "EMAIL", nullptr);
266 112 : hSession->pszEmail = pszEmail ? CPLStrdup(pszEmail) : nullptr;
267 :
268 : const char *pszUserName =
269 112 : OGRGeocodeGetParameter(papszOptions, "USERNAME", nullptr);
270 112 : hSession->pszUserName = pszUserName ? CPLStrdup(pszUserName) : nullptr;
271 :
272 112 : const char *pszKey = OGRGeocodeGetParameter(papszOptions, "KEY", nullptr);
273 112 : hSession->pszKey = pszKey ? CPLStrdup(pszKey) : nullptr;
274 :
275 112 : if (EQUAL(pszGeocodingService, "GEONAMES") && pszUserName == nullptr)
276 : {
277 0 : CPLError(CE_Failure, CPLE_AppDefined,
278 : "GEONAMES service requires USERNAME to be specified.");
279 0 : OGRGeocodeDestroySession(hSession);
280 0 : return nullptr;
281 : }
282 112 : else if (EQUAL(pszGeocodingService, "BING") && pszKey == nullptr)
283 : {
284 0 : CPLError(CE_Failure, CPLE_AppDefined,
285 : "BING service requires KEY to be specified.");
286 0 : OGRGeocodeDestroySession(hSession);
287 0 : return nullptr;
288 : }
289 :
290 112 : const char *pszApplication = OGRGeocodeGetParameter(
291 : papszOptions, "APPLICATION", GDALVersionInfo(""));
292 112 : hSession->pszApplication = CPLStrdup(pszApplication);
293 :
294 : const char *pszLanguage =
295 112 : OGRGeocodeGetParameter(papszOptions, "LANGUAGE", nullptr);
296 112 : hSession->pszLanguage = pszLanguage ? CPLStrdup(pszLanguage) : nullptr;
297 :
298 : const char *pszDelayBetweenQueries =
299 112 : OGRGeocodeGetParameter(papszOptions, "DELAY", "1.0");
300 : // coverity[tainted_data]
301 112 : hSession->dfDelayBetweenQueries = CPLAtofM(pszDelayBetweenQueries);
302 :
303 112 : const char *pszQueryTemplateDefault = nullptr;
304 112 : if (EQUAL(pszGeocodingService, "OSM_NOMINATIM"))
305 28 : pszQueryTemplateDefault = OSM_NOMINATIM_QUERY;
306 84 : else if (EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM"))
307 0 : pszQueryTemplateDefault = MAPQUEST_NOMINATIM_QUERY;
308 84 : else if (EQUAL(pszGeocodingService, "YAHOO"))
309 28 : pszQueryTemplateDefault = YAHOO_QUERY;
310 56 : else if (EQUAL(pszGeocodingService, "GEONAMES"))
311 28 : pszQueryTemplateDefault = GEONAMES_QUERY;
312 28 : else if (EQUAL(pszGeocodingService, "BING"))
313 28 : pszQueryTemplateDefault = BING_QUERY;
314 112 : const char *pszQueryTemplate = OGRGeocodeGetParameter(
315 : papszOptions, "QUERY_TEMPLATE", pszQueryTemplateDefault);
316 :
317 224 : if (pszQueryTemplate != nullptr &&
318 112 : !OGRGeocodeHasStringValidFormat(pszQueryTemplate))
319 : {
320 0 : CPLError(CE_Failure, CPLE_AppDefined,
321 : "QUERY_TEMPLATE value has an invalid format");
322 0 : OGRGeocodeDestroySession(hSession);
323 0 : return nullptr;
324 : }
325 :
326 112 : hSession->pszQueryTemplate =
327 112 : pszQueryTemplate ? CPLStrdup(pszQueryTemplate) : nullptr;
328 :
329 112 : const char *pszReverseQueryTemplateDefault = nullptr;
330 112 : if (EQUAL(pszGeocodingService, "OSM_NOMINATIM"))
331 28 : pszReverseQueryTemplateDefault = OSM_NOMINATIM_REVERSE_QUERY;
332 84 : else if (EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM"))
333 0 : pszReverseQueryTemplateDefault = MAPQUEST_NOMINATIM_REVERSE_QUERY;
334 84 : else if (EQUAL(pszGeocodingService, "YAHOO"))
335 28 : pszReverseQueryTemplateDefault = YAHOO_REVERSE_QUERY;
336 56 : else if (EQUAL(pszGeocodingService, "GEONAMES"))
337 28 : pszReverseQueryTemplateDefault = GEONAMES_REVERSE_QUERY;
338 28 : else if (EQUAL(pszGeocodingService, "BING"))
339 28 : pszReverseQueryTemplateDefault = BING_REVERSE_QUERY;
340 112 : const char *pszReverseQueryTemplate = OGRGeocodeGetParameter(
341 : papszOptions, "REVERSE_QUERY_TEMPLATE", pszReverseQueryTemplateDefault);
342 :
343 112 : if (pszReverseQueryTemplate != nullptr &&
344 112 : (strstr(pszReverseQueryTemplate, "{lat}") == nullptr ||
345 112 : strstr(pszReverseQueryTemplate, "{lon}") == nullptr))
346 : {
347 0 : CPLError(CE_Failure, CPLE_AppDefined,
348 : "REVERSE_QUERY_TEMPLATE value has an invalid format");
349 0 : OGRGeocodeDestroySession(hSession);
350 0 : return nullptr;
351 : }
352 :
353 112 : hSession->pszReverseQueryTemplate = (pszReverseQueryTemplate)
354 112 : ? CPLStrdup(pszReverseQueryTemplate)
355 : : nullptr;
356 :
357 112 : return hSession;
358 : }
359 :
360 : /************************************************************************/
361 : /* OGRGeocodeDestroySession() */
362 : /************************************************************************/
363 :
364 : /**
365 : * \brief Destroys a session handle for geocoding requests.
366 :
367 : * @param hSession the handle to destroy.
368 : *
369 : * @since GDAL 1.10
370 : */
371 3280 : void OGRGeocodeDestroySession(OGRGeocodingSessionH hSession)
372 : {
373 3280 : if (hSession == nullptr)
374 3168 : return;
375 112 : CPLFree(hSession->pszCacheFilename);
376 112 : CPLFree(hSession->pszGeocodingService);
377 112 : CPLFree(hSession->pszEmail);
378 112 : CPLFree(hSession->pszUserName);
379 112 : CPLFree(hSession->pszKey);
380 112 : CPLFree(hSession->pszApplication);
381 112 : CPLFree(hSession->pszLanguage);
382 112 : CPLFree(hSession->pszQueryTemplate);
383 112 : CPLFree(hSession->pszReverseQueryTemplate);
384 112 : if (hSession->poDS)
385 112 : delete hSession->poDS;
386 112 : CPLFree(hSession);
387 : }
388 :
389 : /************************************************************************/
390 : /* OGRGeocodeGetCacheLayer() */
391 : /************************************************************************/
392 :
393 146 : static OGRLayer *OGRGeocodeGetCacheLayer(OGRGeocodingSessionH hSession,
394 : bool bCreateIfNecessary,
395 : int *pnIdxBlob)
396 : {
397 146 : GDALDataset *poDS = hSession->poDS;
398 292 : CPLString osExt = CPLGetExtensionSafe(hSession->pszCacheFilename);
399 :
400 146 : if (poDS == nullptr)
401 : {
402 128 : if (GDALGetDriverCount() == 0)
403 0 : GDALAllRegister();
404 :
405 : const bool bHadValue =
406 128 : CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", nullptr) != nullptr;
407 128 : std::string oOldVal(CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", ""));
408 :
409 128 : CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS", "OFF");
410 :
411 128 : poDS = GDALDataset::Open(hSession->pszCacheFilename,
412 : GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr,
413 : nullptr, nullptr);
414 128 : if (poDS == nullptr &&
415 32 : EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE))
416 : {
417 0 : poDS = GDALDataset::Open(DEFAULT_CACHE_CSV,
418 : GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr,
419 : nullptr, nullptr);
420 0 : if (poDS != nullptr)
421 : {
422 0 : CPLFree(hSession->pszCacheFilename);
423 0 : hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV);
424 0 : CPLDebug("OGR", "Switch geocode cache file to %s",
425 : hSession->pszCacheFilename);
426 0 : osExt = "csv";
427 : }
428 : }
429 :
430 128 : if (bCreateIfNecessary && poDS == nullptr &&
431 16 : !STARTS_WITH_CI(hSession->pszCacheFilename, "PG:"))
432 : {
433 16 : auto poDriver = GetGDALDriverManager()->GetDriverByName(osExt);
434 16 : if (poDriver == nullptr &&
435 0 : EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE))
436 : {
437 0 : CPLFree(hSession->pszCacheFilename);
438 0 : hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV);
439 0 : CPLDebug("OGR", "Switch geocode cache file to %s",
440 : hSession->pszCacheFilename);
441 0 : osExt = "csv";
442 0 : poDriver = GetGDALDriverManager()->GetDriverByName(osExt);
443 : }
444 16 : if (poDriver != nullptr)
445 : {
446 16 : char **papszOptions = nullptr;
447 16 : if (EQUAL(osExt, "SQLITE"))
448 : {
449 : papszOptions =
450 8 : CSLAddNameValue(papszOptions, "METADATA", "FALSE");
451 : }
452 :
453 16 : poDS = poDriver->Create(hSession->pszCacheFilename, 0, 0, 0,
454 : GDT_Unknown, papszOptions);
455 :
456 16 : if (poDS == nullptr &&
457 0 : (EQUAL(osExt, "SQLITE") || EQUAL(osExt, "CSV")))
458 : {
459 0 : CPLFree(hSession->pszCacheFilename);
460 0 : hSession->pszCacheFilename =
461 0 : CPLStrdup(VSIMemGenerateHiddenFilename(CPLSPrintf(
462 : "%s.%s", CACHE_LAYER_NAME, osExt.c_str())));
463 0 : CPLDebug("OGR", "Switch geocode cache file to %s",
464 : hSession->pszCacheFilename);
465 0 : poDS = poDriver->Create(hSession->pszCacheFilename, 0, 0, 0,
466 : GDT_Unknown, papszOptions);
467 : }
468 :
469 16 : CSLDestroy(papszOptions);
470 : }
471 : }
472 :
473 128 : CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS",
474 0 : bHadValue ? oOldVal.c_str() : nullptr);
475 :
476 128 : if (poDS == nullptr)
477 16 : return nullptr;
478 :
479 112 : hSession->poDS = poDS;
480 : }
481 :
482 130 : CPLPushErrorHandler(CPLQuietErrorHandler);
483 130 : OGRLayer *poLayer = poDS->GetLayerByName(CACHE_LAYER_NAME);
484 130 : CPLPopErrorHandler();
485 :
486 130 : if (bCreateIfNecessary && poLayer == nullptr)
487 : {
488 16 : char **papszOptions = nullptr;
489 16 : if (EQUAL(osExt, "SQLITE"))
490 : {
491 : papszOptions =
492 8 : CSLAddNameValue(papszOptions, "COMPRESS_COLUMNS", FIELD_BLOB);
493 : }
494 : poLayer =
495 16 : poDS->CreateLayer(CACHE_LAYER_NAME, nullptr, wkbNone, papszOptions);
496 16 : CSLDestroy(papszOptions);
497 :
498 16 : if (poLayer != nullptr)
499 : {
500 32 : OGRFieldDefn oFieldDefnURL(FIELD_URL, OFTString);
501 16 : poLayer->CreateField(&oFieldDefnURL);
502 32 : OGRFieldDefn oFieldDefnBlob(FIELD_BLOB, OFTString);
503 16 : poLayer->CreateField(&oFieldDefnBlob);
504 24 : if (EQUAL(osExt, "SQLITE") ||
505 8 : STARTS_WITH_CI(hSession->pszCacheFilename, "PG:"))
506 : {
507 8 : const char *pszSQL = CPLSPrintf(
508 : "CREATE INDEX idx_%s_%s ON %s(%s)", FIELD_URL,
509 8 : poLayer->GetName(), poLayer->GetName(), FIELD_URL);
510 8 : poDS->ExecuteSQL(pszSQL, nullptr, nullptr);
511 : }
512 : }
513 : }
514 :
515 130 : int nIdxBlob = -1;
516 260 : if (poLayer == nullptr ||
517 260 : poLayer->GetLayerDefn()->GetFieldIndex(FIELD_URL) < 0 ||
518 130 : (nIdxBlob = poLayer->GetLayerDefn()->GetFieldIndex(FIELD_BLOB)) < 0)
519 : {
520 0 : return nullptr;
521 : }
522 :
523 130 : if (pnIdxBlob)
524 130 : *pnIdxBlob = nIdxBlob;
525 :
526 130 : return poLayer;
527 : }
528 :
529 : /************************************************************************/
530 : /* OGRGeocodeGetFromCache() */
531 : /************************************************************************/
532 :
533 112 : static char *OGRGeocodeGetFromCache(OGRGeocodingSessionH hSession,
534 : const char *pszURL)
535 : {
536 224 : CPLMutexHolderD(&hOGRGeocodingMutex);
537 :
538 112 : int nIdxBlob = -1;
539 112 : OGRLayer *poLayer = OGRGeocodeGetCacheLayer(hSession, FALSE, &nIdxBlob);
540 112 : if (poLayer == nullptr)
541 16 : return nullptr;
542 :
543 96 : char *pszSQLEscapedURL = CPLEscapeString(pszURL, -1, CPLES_SQL);
544 96 : poLayer->SetAttributeFilter(
545 96 : CPLSPrintf("%s='%s'", FIELD_URL, pszSQLEscapedURL));
546 96 : CPLFree(pszSQLEscapedURL);
547 :
548 96 : char *pszRet = nullptr;
549 96 : OGRFeature *poFeature = poLayer->GetNextFeature();
550 96 : if (poFeature != nullptr)
551 : {
552 78 : if (poFeature->IsFieldSetAndNotNull(nIdxBlob))
553 78 : pszRet = CPLStrdup(poFeature->GetFieldAsString(nIdxBlob));
554 78 : OGRFeature::DestroyFeature(poFeature);
555 : }
556 :
557 96 : return pszRet;
558 : }
559 :
560 : /************************************************************************/
561 : /* OGRGeocodePutIntoCache() */
562 : /************************************************************************/
563 :
564 34 : static bool OGRGeocodePutIntoCache(OGRGeocodingSessionH hSession,
565 : const char *pszURL, const char *pszContent)
566 : {
567 68 : CPLMutexHolderD(&hOGRGeocodingMutex);
568 :
569 34 : int nIdxBlob = -1;
570 34 : OGRLayer *poLayer = OGRGeocodeGetCacheLayer(hSession, TRUE, &nIdxBlob);
571 34 : if (poLayer == nullptr)
572 0 : return false;
573 :
574 34 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
575 34 : poFeature->SetField(FIELD_URL, pszURL);
576 34 : poFeature->SetField(FIELD_BLOB, pszContent);
577 34 : const bool bRet = poLayer->CreateFeature(poFeature) == OGRERR_NONE;
578 34 : delete poFeature;
579 :
580 34 : return bRet;
581 : }
582 :
583 : /************************************************************************/
584 : /* OGRGeocodeMakeRawLayer() */
585 : /************************************************************************/
586 :
587 0 : static OGRLayerH OGRGeocodeMakeRawLayer(const char *pszContent)
588 : {
589 0 : OGRMemLayer *poLayer = new OGRMemLayer("result", nullptr, wkbNone);
590 0 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
591 0 : OGRFieldDefn oFieldDefnRaw("raw", OFTString);
592 0 : poLayer->CreateField(&oFieldDefnRaw);
593 0 : OGRFeature *poFeature = new OGRFeature(poFDefn);
594 0 : poFeature->SetField("raw", pszContent);
595 0 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature));
596 0 : delete 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 : 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 : OGRFeature *poFeature = new 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(poFeature));
732 36 : delete poFeature;
733 : }
734 64 : psPlace = psPlace->psNext;
735 : }
736 40 : return OGRLayer::ToHandle(poLayer);
737 : }
738 :
739 : /************************************************************************/
740 : /* OGRGeocodeReverseBuildLayerNominatim() */
741 : /************************************************************************/
742 :
743 12 : static OGRLayerH OGRGeocodeReverseBuildLayerNominatim(
744 : CPLXMLNode *psReverseGeocode, const char *pszContent, bool bAddRawFeature)
745 : {
746 12 : CPLXMLNode *psResult = CPLGetXMLNode(psReverseGeocode, "result");
747 : CPLXMLNode *psAddressParts =
748 12 : CPLGetXMLNode(psReverseGeocode, "addressparts");
749 12 : if (psResult == nullptr || psAddressParts == nullptr)
750 : {
751 0 : return nullptr;
752 : }
753 :
754 12 : OGRMemLayer *poLayer = new OGRMemLayer("result", nullptr, wkbNone);
755 12 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
756 :
757 12 : bool bFoundLat = false;
758 12 : bool bFoundLon = false;
759 12 : double dfLat = 0.0;
760 12 : double dfLon = 0.0;
761 :
762 : // First iteration to add fields.
763 12 : CPLXMLNode *psChild = psResult->psChild;
764 96 : while (psChild != nullptr)
765 : {
766 84 : const char *pszName = psChild->pszValue;
767 84 : const char *pszVal = CPLGetXMLValue(psChild, nullptr, nullptr);
768 252 : if ((psChild->eType == CXT_Element ||
769 156 : psChild->eType == CXT_Attribute) &&
770 72 : poFDefn->GetFieldIndex(pszName) < 0)
771 : {
772 144 : OGRFieldDefn oFieldDefn(pszName, OFTString);
773 72 : if (strcmp(pszName, "lat") == 0)
774 : {
775 12 : if (pszVal != nullptr)
776 : {
777 12 : bFoundLat = true;
778 12 : dfLat = CPLAtofM(pszVal);
779 : }
780 12 : oFieldDefn.SetType(OFTReal);
781 : }
782 60 : else if (strcmp(pszName, "lon") == 0)
783 : {
784 12 : if (pszVal != nullptr)
785 : {
786 12 : bFoundLon = true;
787 12 : dfLon = CPLAtofM(pszVal);
788 : }
789 12 : oFieldDefn.SetType(OFTReal);
790 : }
791 72 : poLayer->CreateField(&oFieldDefn);
792 : }
793 84 : psChild = psChild->psNext;
794 : }
795 :
796 : {
797 24 : OGRFieldDefn oFieldDefn("display_name", OFTString);
798 12 : poLayer->CreateField(&oFieldDefn);
799 : }
800 :
801 12 : psChild = psAddressParts->psChild;
802 108 : while (psChild != nullptr)
803 : {
804 96 : const char *pszName = psChild->pszValue;
805 192 : if ((psChild->eType == CXT_Element ||
806 192 : psChild->eType == CXT_Attribute) &&
807 96 : poFDefn->GetFieldIndex(pszName) < 0)
808 : {
809 192 : OGRFieldDefn oFieldDefn(pszName, OFTString);
810 96 : poLayer->CreateField(&oFieldDefn);
811 : }
812 96 : psChild = psChild->psNext;
813 : }
814 :
815 12 : if (bAddRawFeature)
816 : {
817 4 : OGRFieldDefn oFieldDefnRaw("raw", OFTString);
818 2 : poLayer->CreateField(&oFieldDefnRaw);
819 : }
820 :
821 : // Second iteration to fill the feature.
822 12 : OGRFeature *poFeature = new OGRFeature(poFDefn);
823 12 : psChild = psResult->psChild;
824 96 : while (psChild != nullptr)
825 : {
826 84 : int nIdx = 0;
827 84 : const char *pszName = psChild->pszValue;
828 84 : const char *pszVal = CPLGetXMLValue(psChild, nullptr, nullptr);
829 252 : if ((psChild->eType == CXT_Element ||
830 156 : psChild->eType == CXT_Attribute) &&
831 72 : (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0)
832 : {
833 72 : if (pszVal != nullptr)
834 72 : poFeature->SetField(nIdx, pszVal);
835 : }
836 84 : psChild = psChild->psNext;
837 : }
838 :
839 12 : const char *pszVal = CPLGetXMLValue(psResult, nullptr, nullptr);
840 12 : if (pszVal != nullptr)
841 12 : poFeature->SetField("display_name", pszVal);
842 :
843 12 : psChild = psAddressParts->psChild;
844 108 : while (psChild != nullptr)
845 : {
846 96 : int nIdx = 0;
847 96 : const char *pszName = psChild->pszValue;
848 96 : pszVal = CPLGetXMLValue(psChild, nullptr, nullptr);
849 192 : if ((psChild->eType == CXT_Element ||
850 192 : psChild->eType == CXT_Attribute) &&
851 96 : (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0)
852 : {
853 96 : if (pszVal != nullptr)
854 96 : poFeature->SetField(nIdx, pszVal);
855 : }
856 96 : psChild = psChild->psNext;
857 : }
858 :
859 12 : if (bAddRawFeature)
860 : {
861 2 : poFeature->SetField("raw", pszContent);
862 : }
863 :
864 : // If we did not find an explicit geometry, build it from
865 : // the 'lon' and 'lat' attributes.
866 12 : if (poFeature->GetGeometryRef() == nullptr && bFoundLon && bFoundLat)
867 12 : poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
868 :
869 12 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature));
870 12 : delete poFeature;
871 :
872 12 : return OGRLayer::ToHandle(poLayer);
873 : }
874 :
875 : /************************************************************************/
876 : /* OGRGeocodeBuildLayerYahoo() */
877 : /************************************************************************/
878 :
879 26 : static OGRLayerH OGRGeocodeBuildLayerYahoo(CPLXMLNode *psResultSet,
880 : const char * /* pszContent */,
881 : bool bAddRawFeature)
882 : {
883 26 : OGRMemLayer *poLayer = new OGRMemLayer("place", nullptr, wkbPoint);
884 26 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
885 :
886 : // First iteration to add fields.
887 26 : CPLXMLNode *psPlace = psResultSet->psChild;
888 258 : while (psPlace != nullptr)
889 : {
890 232 : if (psPlace->eType == CXT_Element &&
891 154 : strcmp(psPlace->pszValue, "Result") == 0)
892 : {
893 24 : CPLXMLNode *psChild = psPlace->psChild;
894 720 : while (psChild != nullptr)
895 : {
896 696 : const char *pszName = psChild->pszValue;
897 1392 : if ((psChild->eType == CXT_Element ||
898 1392 : psChild->eType == CXT_Attribute) &&
899 696 : poFDefn->GetFieldIndex(pszName) < 0)
900 : {
901 1392 : OGRFieldDefn oFieldDefn(pszName, OFTString);
902 696 : if (strcmp(pszName, "latitude") == 0)
903 : {
904 24 : oFieldDefn.SetType(OFTReal);
905 : }
906 672 : else if (strcmp(pszName, "longitude") == 0)
907 : {
908 24 : oFieldDefn.SetType(OFTReal);
909 : }
910 696 : poLayer->CreateField(&oFieldDefn);
911 : }
912 696 : psChild = psChild->psNext;
913 : }
914 : }
915 :
916 232 : psPlace = psPlace->psNext;
917 : }
918 :
919 52 : OGRFieldDefn oFieldDefnDisplayName("display_name", OFTString);
920 26 : poLayer->CreateField(&oFieldDefnDisplayName);
921 :
922 26 : if (bAddRawFeature)
923 : {
924 8 : OGRFieldDefn oFieldDefnRaw("raw", OFTString);
925 4 : poLayer->CreateField(&oFieldDefnRaw);
926 : }
927 :
928 26 : psPlace = psResultSet->psChild;
929 258 : while (psPlace != nullptr)
930 : {
931 232 : if (psPlace->eType == CXT_Element &&
932 154 : strcmp(psPlace->pszValue, "Result") == 0)
933 : {
934 24 : bool bFoundLat = false;
935 24 : bool bFoundLon = false;
936 24 : double dfLat = 0.0;
937 24 : double dfLon = 0.0;
938 :
939 : // Second iteration to fill the feature.
940 24 : OGRFeature *poFeature = new OGRFeature(poFDefn);
941 720 : for (CPLXMLNode *psChild = psPlace->psChild; psChild != nullptr;
942 696 : psChild = psChild->psNext)
943 : {
944 696 : const char *pszName = psChild->pszValue;
945 696 : const char *pszVal = CPLGetXMLValue(psChild, nullptr, nullptr);
946 696 : if (!(psChild->eType == CXT_Element ||
947 0 : psChild->eType == CXT_Attribute))
948 : {
949 : // Do nothing.
950 0 : continue;
951 : }
952 696 : const int nIdx = poFDefn->GetFieldIndex(pszName);
953 696 : if (nIdx >= 0)
954 : {
955 696 : if (pszVal != nullptr)
956 : {
957 456 : poFeature->SetField(nIdx, pszVal);
958 456 : if (strcmp(pszName, "latitude") == 0)
959 : {
960 24 : bFoundLat = true;
961 24 : dfLat = CPLAtofM(pszVal);
962 : }
963 432 : else if (strcmp(pszName, "longitude") == 0)
964 : {
965 24 : bFoundLon = true;
966 24 : dfLon = CPLAtofM(pszVal);
967 : }
968 : }
969 : }
970 : }
971 :
972 48 : CPLString osDisplayName;
973 24 : for (int i = 1;; ++i)
974 : {
975 : const int nIdx =
976 120 : poFDefn->GetFieldIndex(CPLSPrintf("line%d", i));
977 120 : if (nIdx < 0)
978 24 : break;
979 96 : if (poFeature->IsFieldSetAndNotNull(nIdx))
980 : {
981 60 : if (!osDisplayName.empty())
982 36 : osDisplayName += ", ";
983 60 : osDisplayName += poFeature->GetFieldAsString(nIdx);
984 : }
985 96 : }
986 24 : poFeature->SetField("display_name", osDisplayName.c_str());
987 :
988 24 : if (bAddRawFeature)
989 : {
990 4 : CPLXMLNode *psOldNext = psPlace->psNext;
991 4 : psPlace->psNext = nullptr;
992 4 : char *pszXML = CPLSerializeXMLTree(psPlace);
993 4 : psPlace->psNext = psOldNext;
994 :
995 4 : poFeature->SetField("raw", pszXML);
996 4 : CPLFree(pszXML);
997 : }
998 :
999 : // Build geometry from the 'lon' and 'lat' attributes.
1000 24 : if (bFoundLon && bFoundLat)
1001 24 : poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
1002 :
1003 24 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature));
1004 24 : delete poFeature;
1005 : }
1006 232 : psPlace = psPlace->psNext;
1007 : }
1008 52 : return OGRLayer::ToHandle(poLayer);
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* OGRGeocodeBuildLayerBing() */
1013 : /************************************************************************/
1014 :
1015 26 : static OGRLayerH OGRGeocodeBuildLayerBing(CPLXMLNode *psResponse,
1016 : const char * /* pszContent */,
1017 : bool bAddRawFeature)
1018 : {
1019 : CPLXMLNode *psResources =
1020 26 : CPLGetXMLNode(psResponse, "ResourceSets.ResourceSet.Resources");
1021 26 : if (psResources == nullptr)
1022 0 : return nullptr;
1023 :
1024 26 : OGRMemLayer *poLayer = new OGRMemLayer("place", nullptr, wkbPoint);
1025 26 : OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
1026 :
1027 : // First iteration to add fields.
1028 26 : CPLXMLNode *psPlace = psResources->psChild;
1029 50 : while (psPlace != nullptr)
1030 : {
1031 24 : if (psPlace->eType == CXT_Element &&
1032 24 : strcmp(psPlace->pszValue, "Location") == 0)
1033 : {
1034 24 : CPLXMLNode *psChild = psPlace->psChild;
1035 144 : while (psChild != nullptr)
1036 : {
1037 120 : const char *pszName = psChild->pszValue;
1038 240 : if ((psChild->eType == CXT_Element ||
1039 0 : psChild->eType == CXT_Attribute) &&
1040 120 : strcmp(pszName, "BoundingBox") != 0 &&
1041 312 : strcmp(pszName, "GeocodePoint") != 0 &&
1042 72 : poFDefn->GetFieldIndex(pszName) < 0)
1043 : {
1044 72 : if (psChild->psChild != nullptr &&
1045 72 : psChild->psChild->eType == CXT_Element)
1046 : {
1047 48 : CPLXMLNode *psSubChild = psChild->psChild;
1048 216 : while (psSubChild != nullptr)
1049 : {
1050 168 : pszName = psSubChild->pszValue;
1051 336 : if ((psSubChild->eType == CXT_Element ||
1052 336 : psSubChild->eType == CXT_Attribute) &&
1053 168 : poFDefn->GetFieldIndex(pszName) < 0)
1054 : {
1055 336 : OGRFieldDefn oFieldDefn(pszName, OFTString);
1056 168 : if (strcmp(pszName, "Latitude") == 0)
1057 : {
1058 24 : oFieldDefn.SetType(OFTReal);
1059 : }
1060 144 : else if (strcmp(pszName, "Longitude") == 0)
1061 : {
1062 24 : oFieldDefn.SetType(OFTReal);
1063 : }
1064 168 : poLayer->CreateField(&oFieldDefn);
1065 : }
1066 168 : psSubChild = psSubChild->psNext;
1067 48 : }
1068 : }
1069 : else
1070 : {
1071 48 : OGRFieldDefn oFieldDefn(pszName, OFTString);
1072 24 : poLayer->CreateField(&oFieldDefn);
1073 : }
1074 : }
1075 120 : psChild = psChild->psNext;
1076 : }
1077 : }
1078 24 : psPlace = psPlace->psNext;
1079 : }
1080 :
1081 26 : if (bAddRawFeature)
1082 : {
1083 8 : OGRFieldDefn oFieldDefnRaw("raw", OFTString);
1084 4 : poLayer->CreateField(&oFieldDefnRaw);
1085 : }
1086 :
1087 : // Iteration to fill the feature.
1088 26 : psPlace = psResources->psChild;
1089 50 : while (psPlace != nullptr)
1090 : {
1091 24 : if (psPlace->eType == CXT_Element &&
1092 24 : strcmp(psPlace->pszValue, "Location") == 0)
1093 : {
1094 24 : bool bFoundLat = false;
1095 24 : bool bFoundLon = false;
1096 24 : double dfLat = 0.0;
1097 24 : double dfLon = 0.0;
1098 :
1099 24 : OGRFeature *poFeature = new OGRFeature(poFDefn);
1100 144 : for (CPLXMLNode *psChild = psPlace->psChild; psChild != nullptr;
1101 120 : psChild = psChild->psNext)
1102 : {
1103 120 : const char *pszName = psChild->pszValue;
1104 120 : const char *pszVal = CPLGetXMLValue(psChild, nullptr, nullptr);
1105 120 : if (!(psChild->eType == CXT_Element ||
1106 0 : psChild->eType == CXT_Attribute))
1107 : {
1108 : // Do nothing.
1109 0 : continue;
1110 : }
1111 120 : const int nIdx = poFDefn->GetFieldIndex(pszName);
1112 120 : if (nIdx >= 0)
1113 : {
1114 24 : if (pszVal != nullptr)
1115 24 : poFeature->SetField(nIdx, pszVal);
1116 : }
1117 96 : else if (strcmp(pszName, "BoundingBox") != 0 &&
1118 72 : strcmp(pszName, "GeocodePoint") != 0 &&
1119 48 : psChild->psChild != nullptr &&
1120 48 : psChild->psChild->eType == CXT_Element)
1121 : {
1122 48 : for (CPLXMLNode *psSubChild = psChild->psChild;
1123 216 : psSubChild != nullptr; psSubChild = psSubChild->psNext)
1124 : {
1125 168 : pszName = psSubChild->pszValue;
1126 168 : pszVal = CPLGetXMLValue(psSubChild, nullptr, nullptr);
1127 168 : if ((psSubChild->eType == CXT_Element ||
1128 0 : psSubChild->eType == CXT_Attribute))
1129 : {
1130 168 : const int nIdx2 = poFDefn->GetFieldIndex(pszName);
1131 168 : if (nIdx2 >= 0)
1132 : {
1133 168 : if (pszVal != nullptr)
1134 : {
1135 168 : poFeature->SetField(nIdx2, pszVal);
1136 168 : if (strcmp(pszName, "Latitude") == 0)
1137 : {
1138 24 : bFoundLat = true;
1139 24 : dfLat = CPLAtofM(pszVal);
1140 : }
1141 144 : else if (strcmp(pszName, "Longitude") == 0)
1142 : {
1143 24 : bFoundLon = true;
1144 24 : dfLon = CPLAtofM(pszVal);
1145 : }
1146 : }
1147 : }
1148 : }
1149 : }
1150 : }
1151 : }
1152 :
1153 24 : if (bAddRawFeature)
1154 : {
1155 4 : CPLXMLNode *psOldNext = psPlace->psNext;
1156 4 : psPlace->psNext = nullptr;
1157 4 : char *pszXML = CPLSerializeXMLTree(psPlace);
1158 4 : psPlace->psNext = psOldNext;
1159 :
1160 4 : poFeature->SetField("raw", pszXML);
1161 4 : CPLFree(pszXML);
1162 : }
1163 :
1164 : // Build geometry from the 'lon' and 'lat' attributes.
1165 24 : if (bFoundLon && bFoundLat)
1166 24 : poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat));
1167 :
1168 24 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature));
1169 24 : delete poFeature;
1170 : }
1171 24 : psPlace = psPlace->psNext;
1172 : }
1173 :
1174 26 : return OGRLayer::ToHandle(poLayer);
1175 : }
1176 :
1177 : /************************************************************************/
1178 : /* OGRGeocodeBuildLayer() */
1179 : /************************************************************************/
1180 :
1181 112 : static OGRLayerH OGRGeocodeBuildLayer(const char *pszContent,
1182 : bool bAddRawFeature)
1183 : {
1184 112 : OGRLayerH hLayer = nullptr;
1185 112 : CPLXMLNode *psRoot = CPLParseXMLString(pszContent);
1186 112 : if (psRoot != nullptr)
1187 : {
1188 104 : CPLXMLNode *psSearchResults = nullptr;
1189 104 : CPLXMLNode *psReverseGeocode = nullptr;
1190 104 : CPLXMLNode *psGeonames = nullptr;
1191 104 : CPLXMLNode *psResultSet = nullptr;
1192 104 : CPLXMLNode *psResponse = nullptr;
1193 104 : if ((psSearchResults = CPLSearchXMLNode(psRoot, "=searchresults")) !=
1194 : nullptr)
1195 14 : hLayer = OGRGeocodeBuildLayerNominatim(psSearchResults, pszContent,
1196 : bAddRawFeature);
1197 90 : else if ((psReverseGeocode =
1198 90 : CPLSearchXMLNode(psRoot, "=reversegeocode")) != nullptr)
1199 12 : hLayer = OGRGeocodeReverseBuildLayerNominatim(
1200 : psReverseGeocode, pszContent, bAddRawFeature);
1201 78 : else if ((psGeonames = CPLSearchXMLNode(psRoot, "=geonames")) !=
1202 : nullptr)
1203 26 : hLayer = OGRGeocodeBuildLayerNominatim(psGeonames, pszContent,
1204 : bAddRawFeature);
1205 52 : else if ((psResultSet = CPLSearchXMLNode(psRoot, "=ResultSet")) !=
1206 : nullptr)
1207 26 : hLayer = OGRGeocodeBuildLayerYahoo(psResultSet, pszContent,
1208 : bAddRawFeature);
1209 26 : else if ((psResponse = CPLSearchXMLNode(psRoot, "=Response")) !=
1210 : nullptr)
1211 26 : hLayer = OGRGeocodeBuildLayerBing(psResponse, pszContent,
1212 : bAddRawFeature);
1213 104 : CPLDestroyXMLNode(psRoot);
1214 : }
1215 112 : if (hLayer == nullptr && bAddRawFeature)
1216 0 : hLayer = OGRGeocodeMakeRawLayer(pszContent);
1217 112 : return hLayer;
1218 : }
1219 :
1220 : /************************************************************************/
1221 : /* OGRGeocodeCommon() */
1222 : /************************************************************************/
1223 :
1224 112 : static OGRLayerH OGRGeocodeCommon(OGRGeocodingSessionH hSession,
1225 : const std::string &osURLIn,
1226 : char **papszOptions)
1227 : {
1228 224 : std::string osURL(osURLIn);
1229 :
1230 : // Only documented to work with OSM Nominatim.
1231 112 : if (hSession->pszLanguage != nullptr)
1232 : {
1233 0 : osURL += "&accept-language=";
1234 0 : osURL += hSession->pszLanguage;
1235 : }
1236 :
1237 : const char *pszExtraQueryParameters =
1238 112 : OGRGeocodeGetParameter(papszOptions, "EXTRA_QUERY_PARAMETERS", nullptr);
1239 112 : if (pszExtraQueryParameters != nullptr)
1240 : {
1241 0 : osURL += "&";
1242 0 : osURL += pszExtraQueryParameters;
1243 : }
1244 :
1245 112 : CPLString osURLWithEmail = osURL;
1246 112 : if (EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") &&
1247 28 : hSession->pszEmail != nullptr)
1248 : {
1249 : char *const pszEscapedEmail =
1250 28 : CPLEscapeString(hSession->pszEmail, -1, CPLES_URL);
1251 28 : osURLWithEmail = osURL + "&email=" + pszEscapedEmail;
1252 28 : CPLFree(pszEscapedEmail);
1253 : }
1254 84 : else if (EQUAL(hSession->pszGeocodingService, "GEONAMES") &&
1255 28 : hSession->pszUserName != nullptr)
1256 : {
1257 : char *const pszEscaped =
1258 28 : CPLEscapeString(hSession->pszUserName, -1, CPLES_URL);
1259 28 : osURLWithEmail = osURL + "&username=" + pszEscaped;
1260 28 : CPLFree(pszEscaped);
1261 : }
1262 56 : else if (EQUAL(hSession->pszGeocodingService, "BING") &&
1263 28 : hSession->pszKey != nullptr)
1264 : {
1265 : char *const pszEscaped =
1266 28 : CPLEscapeString(hSession->pszKey, -1, CPLES_URL);
1267 28 : osURLWithEmail = osURL + "&key=" + pszEscaped;
1268 28 : CPLFree(pszEscaped);
1269 : }
1270 :
1271 : const bool bAddRawFeature =
1272 112 : CPLTestBool(OGRGeocodeGetParameter(papszOptions, "RAW_FEATURE", "NO"));
1273 :
1274 112 : OGRLayerH hLayer = nullptr;
1275 :
1276 112 : char *pszCachedResult = nullptr;
1277 112 : if (hSession->bReadCache)
1278 112 : pszCachedResult = OGRGeocodeGetFromCache(hSession, osURL.c_str());
1279 112 : if (pszCachedResult == nullptr)
1280 : {
1281 34 : double *pdfLastQueryTime = nullptr;
1282 34 : if (EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM"))
1283 10 : pdfLastQueryTime = &dfLastQueryTimeStampOSMNominatim;
1284 24 : else if (EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM"))
1285 0 : pdfLastQueryTime = &dfLastQueryTimeStampMapQuestNominatim;
1286 :
1287 68 : CPLString osHeaders = "User-Agent: ";
1288 34 : osHeaders += hSession->pszApplication;
1289 34 : if (hSession->pszLanguage != nullptr)
1290 : {
1291 0 : osHeaders += "\r\nAccept-Language: ";
1292 0 : osHeaders += hSession->pszLanguage;
1293 : }
1294 : char **papszHTTPOptions =
1295 34 : CSLAddNameValue(nullptr, "HEADERS", osHeaders.c_str());
1296 :
1297 34 : CPLHTTPResult *psResult = nullptr;
1298 34 : if (pdfLastQueryTime != nullptr)
1299 : {
1300 10 : CPLMutexHolderD(&hOGRGeocodingMutex);
1301 : struct timeval tv;
1302 :
1303 10 : gettimeofday(&tv, nullptr);
1304 10 : double dfCurrentTime = tv.tv_sec + tv.tv_usec / 1e6;
1305 10 : if (dfCurrentTime <
1306 10 : *pdfLastQueryTime + hSession->dfDelayBetweenQueries)
1307 : {
1308 8 : CPLSleep(*pdfLastQueryTime + hSession->dfDelayBetweenQueries -
1309 : dfCurrentTime);
1310 : }
1311 :
1312 10 : psResult = CPLHTTPFetch(osURLWithEmail, papszHTTPOptions);
1313 :
1314 10 : gettimeofday(&tv, nullptr);
1315 10 : *pdfLastQueryTime = tv.tv_sec + tv.tv_usec / 1e6;
1316 : }
1317 : else
1318 : {
1319 24 : psResult = CPLHTTPFetch(osURLWithEmail, papszHTTPOptions);
1320 : }
1321 :
1322 34 : CSLDestroy(papszHTTPOptions);
1323 34 : papszHTTPOptions = nullptr;
1324 :
1325 34 : if (psResult == nullptr)
1326 : {
1327 0 : CPLError(CE_Failure, CPLE_AppDefined, "Query '%s' failed",
1328 : osURLWithEmail.c_str());
1329 : }
1330 : else
1331 : {
1332 34 : const char *pszResult =
1333 : reinterpret_cast<const char *>(psResult->pabyData);
1334 34 : if (pszResult != nullptr)
1335 : {
1336 34 : if (hSession->bWriteCache)
1337 : {
1338 : // coverity[tainted_data]
1339 34 : OGRGeocodePutIntoCache(hSession, osURL.c_str(), pszResult);
1340 : }
1341 34 : hLayer = OGRGeocodeBuildLayer(pszResult, bAddRawFeature);
1342 : }
1343 34 : CPLHTTPDestroyResult(psResult);
1344 : }
1345 : }
1346 : else
1347 : {
1348 78 : hLayer = OGRGeocodeBuildLayer(pszCachedResult, bAddRawFeature);
1349 78 : CPLFree(pszCachedResult);
1350 : }
1351 :
1352 224 : return hLayer;
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* OGRGeocode() */
1357 : /************************************************************************/
1358 :
1359 : /* clang-format off */
1360 : /**
1361 : * \brief Runs a geocoding request.
1362 : *
1363 : * If the result is not found in cache, a GET request will be sent to resolve
1364 : * the query.
1365 : *
1366 : * Note: most online services have Term of Uses. You are kindly requested
1367 : * to read and follow them. For the OpenStreetMap Nominatim service, this
1368 : * implementation will make sure that no more than one request is sent by
1369 : * second, but there might be other restrictions that you must follow by other
1370 : * means.
1371 : *
1372 : * In case of success, the return of this function is a OGR layer that contain
1373 : * zero, one or several features matching the query. Note that the geometry of
1374 : * the features is not necessarily a point. The returned layer must be freed
1375 : * with OGRGeocodeFreeResult().
1376 : *
1377 : * Note: this function is also available as the SQL
1378 : * <a href="ogr_sql_sqlite.html#ogr_sql_sqlite_ogr_geocode_function">ogr_geocode()</a>
1379 : * function of the SQL SQLite dialect.
1380 : *
1381 : * The list of recognized options is :
1382 : * <ul>
1383 : * <li>ADDRESSDETAILS=0 or 1: Include a breakdown of the address into elements
1384 : * Defaults to 1. (Known to work with OSM and MapQuest Nominatim)
1385 : * <li>COUNTRYCODES=code1,code2,...codeN: Limit search results to a specific
1386 : * country (or a list of countries). The codes must fellow ISO 3166-1, i.e.
1387 : * gb for United Kingdom, de for Germany, etc.. (Known to work with OSM and
1388 : * MapQuest Nominatim)
1389 : * <li>LIMIT=number: the number of records to return. Unlimited if not
1390 : * specified. (Known to work with OSM and MapQuest Nominatim)
1391 : * <li>RAW_FEATURE=YES: to specify that a 'raw' field must be added to the
1392 : * returned feature with the raw XML content.
1393 : * <li>EXTRA_QUERY_PARAMETERS=params: additional parameters for the GET
1394 : * request.
1395 : * </ul>
1396 : *
1397 : * @param hSession the geocoding session handle.
1398 : * @param pszQuery the string to geocode.
1399 : * @param papszStructuredQuery unused for now. Must be NULL.
1400 : * @param papszOptions a list of options or NULL.
1401 : *
1402 : * @return a OGR layer with the result(s), or NULL in case of error.
1403 : * The returned layer must be freed with OGRGeocodeFreeResult().
1404 : *
1405 : * @since GDAL 1.10
1406 : */
1407 : /* clang-format on */
1408 :
1409 64 : OGRLayerH OGRGeocode(OGRGeocodingSessionH hSession, const char *pszQuery,
1410 : char **papszStructuredQuery, char **papszOptions)
1411 : {
1412 64 : VALIDATE_POINTER1(hSession, "OGRGeocode", nullptr);
1413 64 : if ((pszQuery == nullptr && papszStructuredQuery == nullptr) ||
1414 64 : (pszQuery != nullptr && papszStructuredQuery != nullptr))
1415 : {
1416 0 : CPLError(CE_Failure, CPLE_NotSupported,
1417 : "Only one of pszQuery or papszStructuredQuery must be set.");
1418 0 : return nullptr;
1419 : }
1420 :
1421 64 : if (papszStructuredQuery != nullptr)
1422 : {
1423 0 : CPLError(CE_Failure, CPLE_NotSupported,
1424 : "papszStructuredQuery not yet supported.");
1425 0 : return nullptr;
1426 : }
1427 :
1428 64 : if (hSession->pszQueryTemplate == nullptr)
1429 : {
1430 0 : CPLError(CE_Failure, CPLE_AppDefined,
1431 : "QUERY_TEMPLATE parameter not defined");
1432 0 : return nullptr;
1433 : }
1434 :
1435 64 : constexpr const char *PCT_S = "%s";
1436 64 : const char *pszPctS = strstr(hSession->pszQueryTemplate, PCT_S);
1437 64 : if (!pszPctS)
1438 : {
1439 : // should not happen given OGRGeocodeHasStringValidFormat()
1440 0 : return nullptr;
1441 : }
1442 :
1443 64 : char *pszEscapedQuery = CPLEscapeString(pszQuery, -1, CPLES_URL);
1444 :
1445 128 : std::string osURL;
1446 64 : osURL.assign(hSession->pszQueryTemplate,
1447 64 : pszPctS - hSession->pszQueryTemplate);
1448 64 : osURL += pszEscapedQuery;
1449 64 : osURL += (pszPctS + strlen(PCT_S));
1450 64 : CPLFree(pszEscapedQuery);
1451 :
1452 64 : if (EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") ||
1453 48 : EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM"))
1454 : {
1455 : const char *pszAddressDetails =
1456 16 : OGRGeocodeGetParameter(papszOptions, "ADDRESSDETAILS", "1");
1457 16 : osURL += "&addressdetails=";
1458 16 : osURL += pszAddressDetails;
1459 :
1460 : const char *pszCountryCodes =
1461 16 : OGRGeocodeGetParameter(papszOptions, "COUNTRYCODES", nullptr);
1462 16 : if (pszCountryCodes != nullptr)
1463 : {
1464 0 : osURL += "&countrycodes=";
1465 0 : osURL += pszCountryCodes;
1466 : }
1467 :
1468 : const char *pszLimit =
1469 16 : OGRGeocodeGetParameter(papszOptions, "LIMIT", nullptr);
1470 16 : if (pszLimit != nullptr && *pszLimit != '\0')
1471 : {
1472 16 : osURL += "&limit=";
1473 16 : osURL += pszLimit;
1474 : }
1475 : }
1476 :
1477 : // coverity[tainted_data]
1478 64 : return OGRGeocodeCommon(hSession, osURL, papszOptions);
1479 : }
1480 :
1481 : /************************************************************************/
1482 : /* OGRGeocodeReverseSubstitute() */
1483 : /************************************************************************/
1484 :
1485 48 : static CPLString OGRGeocodeReverseSubstitute(CPLString osURL, double dfLon,
1486 : double dfLat)
1487 : {
1488 48 : size_t iPos = osURL.find("{lon}");
1489 48 : if (iPos != std::string::npos)
1490 : {
1491 96 : const CPLString osEnd(osURL.substr(iPos + 5));
1492 48 : osURL = osURL.substr(0, iPos);
1493 48 : osURL += CPLSPrintf("%.8f", dfLon);
1494 48 : osURL += osEnd;
1495 : }
1496 :
1497 48 : iPos = osURL.find("{lat}");
1498 48 : if (iPos != std::string::npos)
1499 : {
1500 96 : const CPLString osEnd(osURL.substr(iPos + 5));
1501 48 : osURL = osURL.substr(0, iPos);
1502 48 : osURL += CPLSPrintf("%.8f", dfLat);
1503 48 : osURL += osEnd;
1504 : }
1505 :
1506 48 : return osURL;
1507 : }
1508 :
1509 : /************************************************************************/
1510 : /* OGRGeocodeReverse() */
1511 : /************************************************************************/
1512 :
1513 : /* clang-format off */
1514 : /**
1515 : * \brief Runs a reverse geocoding request.
1516 : *
1517 : * If the result is not found in cache, a GET request will be sent to resolve
1518 : * the query.
1519 : *
1520 : * Note: most online services have Term of Uses. You are kindly requested
1521 : * to read and follow them. For the OpenStreetMap Nominatim service, this
1522 : * implementation will make sure that no more than one request is sent by
1523 : * second, but there might be other restrictions that you must follow by other
1524 : * means.
1525 : *
1526 : * In case of success, the return of this function is a OGR layer that contain
1527 : * zero, one or several features matching the query. The returned layer must be
1528 : * freed with OGRGeocodeFreeResult().
1529 : *
1530 : * Note: this function is also available as the SQL
1531 : * <a href="ogr_sql_sqlite.html#ogr_sql_sqlite_ogr_geocode_function">ogr_geocode_reverse()</a>
1532 : * function of the SQL SQLite dialect.
1533 : *
1534 : * The list of recognized options is :
1535 : * <ul>
1536 : * <li>ZOOM=a_level: to query a specific zoom level. Only understood by the OSM
1537 : * Nominatim service.
1538 : * <li>RAW_FEATURE=YES: to specify that a 'raw' field must be added to the
1539 : * returned feature with the raw XML content.
1540 : * <li>EXTRA_QUERY_PARAMETERS=params: additional parameters for the GET request
1541 : * for reverse geocoding.
1542 : * </ul>
1543 : *
1544 : * @param hSession the geocoding session handle.
1545 : * @param dfLon the longitude.
1546 : * @param dfLat the latitude.
1547 : * @param papszOptions a list of options or NULL.
1548 : *
1549 : * @return a OGR layer with the result(s), or NULL in case of error.
1550 : * The returned layer must be freed with OGRGeocodeFreeResult().
1551 : *
1552 : * @since GDAL 1.10
1553 : */
1554 : /* clang-format on */
1555 :
1556 48 : OGRLayerH OGRGeocodeReverse(OGRGeocodingSessionH hSession, double dfLon,
1557 : double dfLat, char **papszOptions)
1558 : {
1559 48 : VALIDATE_POINTER1(hSession, "OGRGeocodeReverse", nullptr);
1560 :
1561 48 : if (hSession->pszReverseQueryTemplate == nullptr)
1562 : {
1563 0 : CPLError(CE_Failure, CPLE_AppDefined,
1564 : "REVERSE_QUERY_TEMPLATE parameter not defined");
1565 0 : return nullptr;
1566 : }
1567 :
1568 96 : CPLString osURL = hSession->pszReverseQueryTemplate;
1569 48 : osURL = OGRGeocodeReverseSubstitute(osURL, dfLon, dfLat);
1570 :
1571 48 : if (EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM"))
1572 : {
1573 : const char *pszZoomLevel =
1574 12 : OGRGeocodeGetParameter(papszOptions, "ZOOM", nullptr);
1575 12 : if (pszZoomLevel != nullptr)
1576 : {
1577 4 : osURL = osURL + "&zoom=" + pszZoomLevel;
1578 : }
1579 : }
1580 :
1581 : // coverity[tainted_data]
1582 48 : return OGRGeocodeCommon(hSession, osURL, papszOptions);
1583 : }
1584 :
1585 : /************************************************************************/
1586 : /* OGRGeocodeFreeResult() */
1587 : /************************************************************************/
1588 :
1589 : /**
1590 : * \brief Destroys the result of a geocoding request.
1591 : *
1592 : * @param hLayer the layer returned by OGRGeocode() or OGRGeocodeReverse()
1593 : * to destroy.
1594 : *
1595 : * @since GDAL 1.10
1596 : */
1597 104 : void OGRGeocodeFreeResult(OGRLayerH hLayer)
1598 : {
1599 104 : delete OGRLayer::FromHandle(hLayer);
1600 104 : }
|