Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Translator
4 : * Purpose: Implements OGRLIBKMLDriver
5 : * Author: Brian Case, rush at winkey dot org
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Brian Case
9 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *****************************************************************************/
13 :
14 : #include "libkml_headers.h"
15 : #include "ogrlibkmlfeature.h"
16 :
17 : #include "gdal.h"
18 : #include "ogr_geometry.h"
19 : #include "ogr_libkml.h"
20 : #include "ogrlibkmlfield.h"
21 : #include "ogrlibkmlfeaturestyle.h"
22 : #include "ogrlibkmlgeometry.h"
23 : #include "ogrsf_frmts.h"
24 :
25 : using kmldom::AliasPtr;
26 : using kmldom::CameraPtr;
27 : using kmldom::ElementPtr;
28 : using kmldom::FeaturePtr;
29 : using kmldom::GeometryPtr;
30 : using kmldom::GroundOverlayPtr;
31 : using kmldom::IconPtr;
32 : using kmldom::ImagePyramidPtr;
33 : using kmldom::KmlFactory;
34 : using kmldom::LinkPtr;
35 : using kmldom::LocationPtr;
36 : using kmldom::ModelPtr;
37 : using kmldom::NetworkLinkPtr;
38 : using kmldom::OrientationPtr;
39 : using kmldom::PhotoOverlayPtr;
40 : using kmldom::PlacemarkPtr;
41 : using kmldom::ResourceMapPtr;
42 : using kmldom::ScalePtr;
43 : using kmldom::ViewVolumePtr;
44 :
45 200 : static CameraPtr feat2kmlcamera(const struct fieldconfig &oFC, int iHeading,
46 : int iTilt, int iRoll, OGRFeature *poOgrFeat,
47 : KmlFactory *poKmlFactory)
48 : {
49 : const int iCameraLongitudeField =
50 200 : poOgrFeat->GetFieldIndex(oFC.camera_longitude_field);
51 : const int iCameraLatitudeField =
52 200 : poOgrFeat->GetFieldIndex(oFC.camera_latitude_field);
53 : const int iCameraAltitudeField =
54 200 : poOgrFeat->GetFieldIndex(oFC.camera_altitude_field);
55 : const int iCameraAltitudeModeField =
56 200 : poOgrFeat->GetFieldIndex(oFC.camera_altitudemode_field);
57 :
58 : const bool bNeedCamera =
59 2 : iCameraLongitudeField >= 0 &&
60 2 : poOgrFeat->IsFieldSetAndNotNull(iCameraLongitudeField) &&
61 2 : iCameraLatitudeField >= 0 &&
62 204 : poOgrFeat->IsFieldSetAndNotNull(iCameraLatitudeField) &&
63 2 : ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
64 0 : (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
65 0 : (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll)));
66 :
67 200 : if (!bNeedCamera)
68 198 : return nullptr;
69 :
70 4 : CameraPtr const camera = poKmlFactory->CreateCamera();
71 2 : camera->set_latitude(poOgrFeat->GetFieldAsDouble(iCameraLatitudeField));
72 2 : camera->set_longitude(poOgrFeat->GetFieldAsDouble(iCameraLongitudeField));
73 2 : int isGX = FALSE;
74 :
75 4 : if (iCameraAltitudeModeField >= 0 &&
76 2 : poOgrFeat->IsFieldSetAndNotNull(iCameraAltitudeModeField))
77 : {
78 2 : const int nAltitudeMode = kmlAltitudeModeFromString(
79 : poOgrFeat->GetFieldAsString(iCameraAltitudeModeField), isGX);
80 2 : camera->set_altitudemode(nAltitudeMode);
81 : }
82 0 : else if (CPLTestBool(
83 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
84 : {
85 0 : CPLError(CE_Warning, CPLE_AppDefined,
86 : "Camera should define altitudeMode != 'clampToGround'");
87 : }
88 :
89 4 : if (iCameraAltitudeField >= 0 &&
90 2 : poOgrFeat->IsFieldSetAndNotNull(iCameraAltitudeField))
91 : {
92 2 : camera->set_altitude(poOgrFeat->GetFieldAsDouble(iCameraAltitudeField));
93 : }
94 0 : else if (CPLTestBool(
95 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
96 : {
97 0 : CPLError(CE_Warning, CPLE_AppDefined,
98 : "Camera should have an altitude/Z");
99 0 : camera->set_altitude(0.0);
100 : }
101 :
102 2 : if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
103 2 : camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
104 2 : if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
105 2 : camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
106 2 : if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
107 2 : camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
108 :
109 2 : return camera;
110 : }
111 :
112 : /************************************************************************/
113 : /* OGRLIBKMLReplaceXYLevelInURL() */
114 : /************************************************************************/
115 :
116 0 : static CPLString OGRLIBKMLReplaceLevelXYInURL(const char *pszURL, int level,
117 : int x, int y)
118 : {
119 0 : CPLString osRet(pszURL);
120 0 : size_t nPos = osRet.find("$[level]");
121 0 : osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", level) +
122 0 : osRet.substr(nPos + strlen("$[level]"));
123 :
124 0 : nPos = osRet.find("$[x]");
125 0 : osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", x) +
126 0 : osRet.substr(nPos + strlen("$[x]"));
127 :
128 0 : nPos = osRet.find("$[y]");
129 0 : osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", y) +
130 0 : osRet.substr(nPos + strlen("$[y]"));
131 :
132 0 : return osRet;
133 : }
134 :
135 : /************************************************************************/
136 : /* IsPowerOf2 */
137 : /************************************************************************/
138 :
139 1 : static bool IsPowerOf2(int nVal)
140 : {
141 1 : if (nVal < 1)
142 0 : return false;
143 :
144 1 : const unsigned int nTmp = static_cast<unsigned int>(nVal);
145 :
146 1 : return (nTmp & (nTmp - 1)) == 0;
147 : }
148 :
149 : /************************************************************************/
150 : /* OGRLIBKMLGetMaxDimensions() */
151 : /************************************************************************/
152 :
153 0 : static void OGRLIBKMLGetMaxDimensions(const char *pszURL, int nTileSize,
154 : int *panMaxWidth, int *panMaxHeight)
155 : {
156 : VSIStatBufL sStat;
157 0 : int nMaxLevel = 0;
158 0 : *panMaxWidth = 0;
159 0 : *panMaxHeight = 0;
160 : while (true)
161 : {
162 0 : CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, 0);
163 0 : if (strstr(osURL, ".kmz/"))
164 0 : osURL = "/vsizip/" + osURL;
165 0 : if (VSIStatL(osURL, &sStat) == 0)
166 0 : nMaxLevel++;
167 : else
168 : {
169 0 : if (nMaxLevel == 0)
170 0 : return;
171 0 : break;
172 : }
173 0 : }
174 0 : nMaxLevel--;
175 :
176 : {
177 0 : int i = 0; // Used after for.
178 0 : for (;; i++)
179 : {
180 : CPLString osURL =
181 0 : OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, i + 1, 0);
182 0 : if (strstr(osURL, ".kmz/"))
183 0 : osURL = "/vsizip/" + osURL;
184 0 : if (VSIStatL(osURL, &sStat) != 0)
185 0 : break;
186 0 : }
187 0 : *panMaxWidth = (i + 1) * nTileSize;
188 : }
189 :
190 0 : int i = 0; // Used after for.
191 0 : for (;; i++)
192 : {
193 : CPLString osURL =
194 0 : OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, i + 1);
195 0 : if (strstr(osURL, ".kmz/"))
196 0 : osURL = "/vsizip/" + osURL;
197 0 : if (VSIStatL(osURL, &sStat) != 0)
198 0 : break;
199 0 : }
200 0 : *panMaxHeight = (i + 1) * nTileSize;
201 : }
202 :
203 : /************************************************************************/
204 : /* feat2kml() */
205 : /************************************************************************/
206 :
207 215 : FeaturePtr feat2kml(OGRLIBKMLDataSource *poOgrDS, OGRLIBKMLLayer *poOgrLayer,
208 : OGRFeature *poOgrFeat, KmlFactory *poKmlFactory,
209 : int bUseSimpleField)
210 : {
211 430 : FeaturePtr poKmlFeature = nullptr;
212 215 : const auto &oFC = poOgrLayer->GetFieldConfig();
213 :
214 : /***** geometry *****/
215 215 : OGRGeometry *poOgrGeom = poOgrFeat->GetGeometryRef();
216 215 : const int iHeading = poOgrFeat->GetFieldIndex(oFC.headingfield);
217 215 : const int iTilt = poOgrFeat->GetFieldIndex(oFC.tiltfield);
218 215 : const int iRoll = poOgrFeat->GetFieldIndex(oFC.rollfield);
219 215 : const int iModel = poOgrFeat->GetFieldIndex(oFC.modelfield);
220 215 : const int iNetworkLink = poOgrFeat->GetFieldIndex(oFC.networklinkfield);
221 215 : const int iPhotoOverlay = poOgrFeat->GetFieldIndex(oFC.photooverlayfield);
222 430 : CameraPtr camera = nullptr;
223 :
224 : // PhotoOverlay.
225 2 : if (iPhotoOverlay >= 0 && poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlay) &&
226 2 : poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
227 219 : wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
228 217 : (camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat,
229 2 : poKmlFactory)))
230 : {
231 2 : const int iLeftFovField = poOgrFeat->GetFieldIndex(oFC.leftfovfield);
232 2 : const int iRightFovField = poOgrFeat->GetFieldIndex(oFC.rightfovfield);
233 : const int iBottomFovField =
234 2 : poOgrFeat->GetFieldIndex(oFC.bottomfovfield);
235 2 : const int iTopFovField = poOgrFeat->GetFieldIndex(oFC.topfovfield);
236 2 : const int iNearField = poOgrFeat->GetFieldIndex(oFC.nearfield);
237 :
238 2 : const char *pszURL = poOgrFeat->GetFieldAsString(iPhotoOverlay);
239 : const int iImagePyramidTileSize =
240 2 : poOgrFeat->GetFieldIndex(oFC.imagepyramid_tilesize_field);
241 : const int iImagePyramidMaxWidth =
242 2 : poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxwidth_field);
243 : const int iImagePyramidMaxHeight =
244 2 : poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxheight_field);
245 : const int iImagePyramidGridOrigin =
246 2 : poOgrFeat->GetFieldIndex(oFC.imagepyramid_gridorigin_field);
247 :
248 2 : int nTileSize = 0;
249 2 : int nMaxWidth = 0;
250 2 : int nMaxHeight = 0;
251 2 : bool bIsTiledPhotoOverlay = false;
252 2 : bool bGridOriginIsUpperLeft = true;
253 : // OGC KML Abstract Test Case (ATC) 52 and 62
254 2 : if (strstr(pszURL, "$[x]") && strstr(pszURL, "$[y]") &&
255 1 : strstr(pszURL, "$[level]"))
256 : {
257 1 : bIsTiledPhotoOverlay = true;
258 1 : bool bErrorEmitted = false;
259 2 : if (iImagePyramidTileSize < 0 ||
260 1 : !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidTileSize))
261 : {
262 0 : CPLDebug("LIBKML",
263 : "Missing ImagePyramid tileSize. Computing it");
264 0 : CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, 0, 0, 0);
265 0 : if (strstr(osURL, ".kmz/"))
266 0 : osURL = "/vsizip/" + osURL;
267 0 : GDALDatasetH hDS = GDALOpen(osURL, GA_ReadOnly);
268 0 : if (hDS != nullptr)
269 : {
270 0 : nTileSize = GDALGetRasterXSize(hDS);
271 0 : if (nTileSize != GDALGetRasterYSize(hDS))
272 : {
273 0 : CPLError(CE_Failure, CPLE_AppDefined,
274 : "Non square tile : %dx%d",
275 : GDALGetRasterXSize(hDS),
276 : GDALGetRasterYSize(hDS));
277 0 : nTileSize = 0;
278 0 : bErrorEmitted = true;
279 : }
280 0 : GDALClose(hDS);
281 : }
282 : else
283 : {
284 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
285 : osURL.c_str());
286 0 : bErrorEmitted = true;
287 : }
288 : }
289 : else
290 : {
291 1 : nTileSize = poOgrFeat->GetFieldAsInteger(iImagePyramidTileSize);
292 : }
293 1 : if (!bErrorEmitted && (nTileSize <= 1 || !IsPowerOf2(nTileSize)))
294 : {
295 0 : CPLError(CE_Failure, CPLE_AppDefined,
296 : "Tile size is not a power of two: %d", nTileSize);
297 0 : nTileSize = 0;
298 : }
299 :
300 1 : if (nTileSize > 0)
301 : {
302 2 : if (iImagePyramidMaxWidth < 0 ||
303 1 : !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth) ||
304 2 : iImagePyramidMaxHeight < 0 ||
305 1 : !poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight))
306 : {
307 0 : CPLDebug("LIBKML",
308 : "Missing ImagePyramid maxWidth and/or maxHeight. "
309 : "Computing it");
310 0 : OGRLIBKMLGetMaxDimensions(pszURL, nTileSize, &nMaxWidth,
311 : &nMaxHeight);
312 : }
313 : else
314 : {
315 1 : nMaxWidth =
316 1 : poOgrFeat->GetFieldAsInteger(iImagePyramidMaxWidth);
317 1 : nMaxHeight =
318 1 : poOgrFeat->GetFieldAsInteger(iImagePyramidMaxHeight);
319 : }
320 :
321 1 : if (nMaxWidth <= 0 || nMaxHeight <= 0)
322 : {
323 0 : CPLError(
324 : CE_Failure, CPLE_AppDefined,
325 : "Cannot generate PhotoOverlay object since there are "
326 : "missing information to generate ImagePyramid element");
327 : }
328 : }
329 :
330 1 : if (iImagePyramidGridOrigin >= 0 &&
331 0 : poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin))
332 : {
333 : const char *pszGridOrigin =
334 0 : poOgrFeat->GetFieldAsString(iImagePyramidGridOrigin);
335 0 : if (EQUAL(pszGridOrigin, "UpperLeft"))
336 : {
337 0 : bGridOriginIsUpperLeft = true;
338 : }
339 0 : else if (EQUAL(pszGridOrigin, "BottomLeft"))
340 : {
341 0 : bGridOriginIsUpperLeft = false;
342 : }
343 : else
344 : {
345 0 : CPLError(
346 : CE_Failure, CPLE_AppDefined,
347 : "Unhandled value for imagepyramid_gridorigin : %s. "
348 : "Assuming UpperLeft",
349 : pszGridOrigin);
350 : }
351 1 : }
352 : }
353 : else
354 : {
355 2 : if ((iImagePyramidTileSize >= 0 &&
356 1 : poOgrFeat->IsFieldSetAndNotNull(iImagePyramidTileSize)) ||
357 1 : (iImagePyramidMaxWidth >= 0 &&
358 1 : poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxWidth)) ||
359 1 : (iImagePyramidMaxHeight >= 0 &&
360 2 : poOgrFeat->IsFieldSetAndNotNull(iImagePyramidMaxHeight)) ||
361 0 : (iImagePyramidGridOrigin >= 0 &&
362 0 : poOgrFeat->IsFieldSetAndNotNull(iImagePyramidGridOrigin)))
363 : {
364 0 : CPLError(
365 : CE_Warning, CPLE_AppDefined,
366 : "Ignoring any ImagePyramid information since the URL does "
367 : "not include $[x] and/or $[y] and/or $[level]");
368 : }
369 : }
370 :
371 : // OGC KML Abstract Test Case (ATC) 19 & 35.
372 2 : double dfNear = 0.0;
373 :
374 1 : if ((!bIsTiledPhotoOverlay ||
375 2 : (nTileSize > 0 && nMaxWidth > 0 && nMaxHeight > 0)) &&
376 2 : iLeftFovField >= 0 &&
377 2 : poOgrFeat->IsFieldSetAndNotNull(iLeftFovField) &&
378 2 : iRightFovField >= 0 &&
379 2 : poOgrFeat->IsFieldSetAndNotNull(iRightFovField) &&
380 2 : iBottomFovField >= 0 &&
381 2 : poOgrFeat->IsFieldSetAndNotNull(iBottomFovField) &&
382 2 : iTopFovField >= 0 &&
383 6 : poOgrFeat->IsFieldSetAndNotNull(iTopFovField) && iNearField >= 0 &&
384 2 : (dfNear = poOgrFeat->GetFieldAsDouble(iNearField)) > 0)
385 : {
386 : const PhotoOverlayPtr poKmlPhotoOverlay =
387 2 : poKmlFactory->CreatePhotoOverlay();
388 2 : poKmlFeature = poKmlPhotoOverlay;
389 :
390 2 : const IconPtr poKmlIcon = poKmlFactory->CreateIcon();
391 2 : poKmlPhotoOverlay->set_icon(poKmlIcon);
392 2 : poKmlIcon->set_href(pszURL);
393 :
394 : const ViewVolumePtr poKmlViewVolume =
395 2 : poKmlFactory->CreateViewVolume();
396 2 : poKmlPhotoOverlay->set_viewvolume(poKmlViewVolume);
397 :
398 2 : const double dfLeftFov = poOgrFeat->GetFieldAsDouble(iLeftFovField);
399 : const double dfRightFov =
400 2 : poOgrFeat->GetFieldAsDouble(iRightFovField);
401 : const double dfBottomFov =
402 2 : poOgrFeat->GetFieldAsDouble(iBottomFovField);
403 2 : const double dfTopFov = poOgrFeat->GetFieldAsDouble(iTopFovField);
404 :
405 2 : poKmlViewVolume->set_leftfov(dfLeftFov);
406 2 : poKmlViewVolume->set_rightfov(dfRightFov);
407 2 : poKmlViewVolume->set_bottomfov(dfBottomFov);
408 2 : poKmlViewVolume->set_topfov(dfTopFov);
409 2 : poKmlViewVolume->set_near(dfNear);
410 :
411 2 : if (bIsTiledPhotoOverlay)
412 : {
413 : const ImagePyramidPtr poKmlImagePyramid =
414 2 : poKmlFactory->CreateImagePyramid();
415 1 : poKmlPhotoOverlay->set_imagepyramid(poKmlImagePyramid);
416 :
417 1 : poKmlImagePyramid->set_tilesize(nTileSize);
418 1 : poKmlImagePyramid->set_maxwidth(nMaxWidth);
419 1 : poKmlImagePyramid->set_maxheight(nMaxHeight);
420 1 : poKmlImagePyramid->set_gridorigin(
421 : bGridOriginIsUpperLeft ? kmldom::GRIDORIGIN_UPPERLEFT
422 : : kmldom::GRIDORIGIN_LOWERLEFT);
423 : }
424 :
425 : const int iPhotoOverlayShapeField =
426 2 : poOgrFeat->GetFieldIndex(oFC.photooverlay_shape_field);
427 4 : if (iPhotoOverlayShapeField >= 0 &&
428 2 : poOgrFeat->IsFieldSetAndNotNull(iPhotoOverlayShapeField))
429 : {
430 : const char *pszShape =
431 2 : poOgrFeat->GetFieldAsString(iPhotoOverlayShapeField);
432 2 : if (EQUAL(pszShape, "rectangle"))
433 2 : poKmlPhotoOverlay->set_shape(kmldom::SHAPE_RECTANGLE);
434 0 : else if (EQUAL(pszShape, "cylinder"))
435 0 : poKmlPhotoOverlay->set_shape(kmldom::SHAPE_CYLINDER);
436 0 : else if (EQUAL(pszShape, "sphere"))
437 0 : poKmlPhotoOverlay->set_shape(kmldom::SHAPE_SPHERE);
438 : }
439 :
440 2 : ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
441 2 : if (!poKmlElement)
442 0 : return nullptr;
443 :
444 2 : poKmlPhotoOverlay->set_point(AsPoint(std::move(poKmlElement)));
445 : }
446 : }
447 :
448 : // NetworkLink.
449 218 : if (!poKmlFeature && iNetworkLink >= 0 &&
450 3 : poOgrFeat->IsFieldSetAndNotNull(iNetworkLink))
451 : {
452 : const NetworkLinkPtr poKmlNetworkLink =
453 6 : poKmlFactory->CreateNetworkLink();
454 3 : poKmlFeature = poKmlNetworkLink;
455 :
456 : const int iRefreshVisibility =
457 3 : poOgrFeat->GetFieldIndex(oFC.networklink_refreshvisibility_field);
458 :
459 6 : if (iRefreshVisibility >= 0 &&
460 3 : poOgrFeat->IsFieldSetAndNotNull(iRefreshVisibility))
461 : {
462 2 : poKmlNetworkLink->set_refreshvisibility(
463 1 : CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iRefreshVisibility)));
464 : }
465 :
466 : const int iFlyToView =
467 3 : poOgrFeat->GetFieldIndex(oFC.networklink_flytoview_field);
468 :
469 3 : if (iFlyToView >= 0 && poOgrFeat->IsFieldSetAndNotNull(iFlyToView))
470 2 : poKmlNetworkLink->set_flytoview(
471 1 : CPL_TO_BOOL(poOgrFeat->GetFieldAsInteger(iFlyToView)));
472 :
473 6 : const LinkPtr poKmlLink = poKmlFactory->CreateLink();
474 3 : poKmlLink->set_href(poOgrFeat->GetFieldAsString(iNetworkLink));
475 3 : poKmlNetworkLink->set_link(poKmlLink);
476 :
477 : const int iRefreshMode =
478 3 : poOgrFeat->GetFieldIndex(oFC.networklink_refreshMode_field);
479 : const int iRefreshInterval =
480 3 : poOgrFeat->GetFieldIndex(oFC.networklink_refreshInterval_field);
481 : const int iViewRefreshMode =
482 3 : poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshMode_field);
483 : const int iViewRefreshTime =
484 3 : poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshTime_field);
485 : const int iViewBoundScale =
486 3 : poOgrFeat->GetFieldIndex(oFC.networklink_viewBoundScale_field);
487 : const int iViewFormat =
488 3 : poOgrFeat->GetFieldIndex(oFC.networklink_viewFormat_field);
489 : const int iHttpQuery =
490 3 : poOgrFeat->GetFieldIndex(oFC.networklink_httpQuery_field);
491 :
492 3 : double dfRefreshInterval = 0.0;
493 6 : if (iRefreshInterval >= 0 &&
494 3 : poOgrFeat->IsFieldSetAndNotNull(iRefreshInterval))
495 : {
496 1 : dfRefreshInterval = poOgrFeat->GetFieldAsDouble(iRefreshInterval);
497 1 : if (dfRefreshInterval < 0)
498 0 : dfRefreshInterval = 0.0;
499 : }
500 :
501 3 : double dfViewRefreshTime = 0.0;
502 6 : if (iViewRefreshTime >= 0 &&
503 3 : poOgrFeat->IsFieldSetAndNotNull(iViewRefreshTime))
504 : {
505 1 : dfViewRefreshTime = poOgrFeat->GetFieldAsDouble(iViewRefreshTime);
506 1 : if (dfViewRefreshTime < 0)
507 0 : dfViewRefreshTime = 0.0;
508 : }
509 :
510 3 : if (dfRefreshInterval > 0) // ATC 51
511 1 : poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
512 4 : else if (iRefreshMode >= 0 &&
513 2 : poOgrFeat->IsFieldSetAndNotNull(iRefreshMode))
514 : {
515 : const char *const pszRefreshMode =
516 1 : poOgrFeat->GetFieldAsString(iRefreshMode);
517 1 : if (EQUAL(pszRefreshMode, "onChange"))
518 0 : poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONCHANGE);
519 1 : else if (EQUAL(pszRefreshMode, "onInterval"))
520 0 : poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL);
521 1 : else if (EQUAL(pszRefreshMode, "onExpire"))
522 1 : poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONEXPIRE);
523 : }
524 :
525 3 : if (dfRefreshInterval > 0) // ATC 9
526 1 : poKmlLink->set_refreshinterval(dfRefreshInterval);
527 :
528 3 : if (dfViewRefreshTime > 0) // ATC 51
529 1 : poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
530 4 : else if (iViewRefreshMode >= 0 &&
531 2 : poOgrFeat->IsFieldSetAndNotNull(iViewRefreshMode))
532 : {
533 : const char *const pszViewRefreshMode =
534 1 : poOgrFeat->GetFieldAsString(iViewRefreshMode);
535 1 : if (EQUAL(pszViewRefreshMode, "never"))
536 0 : poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_NEVER);
537 1 : else if (EQUAL(pszViewRefreshMode, "onRequest"))
538 0 : poKmlLink->set_viewrefreshmode(
539 : kmldom::VIEWREFRESHMODE_ONREQUEST);
540 1 : else if (EQUAL(pszViewRefreshMode, "onStop"))
541 0 : poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP);
542 1 : else if (EQUAL(pszViewRefreshMode, "onRegion"))
543 1 : poKmlLink->set_viewrefreshmode(
544 : kmldom::VIEWREFRESHMODE_ONREGION);
545 : }
546 :
547 3 : if (dfViewRefreshTime > 0) // ATC 9
548 1 : poKmlLink->set_viewrefreshtime(dfViewRefreshTime);
549 :
550 6 : if (iViewBoundScale >= 0 &&
551 3 : poOgrFeat->IsFieldSetAndNotNull(iViewBoundScale))
552 : {
553 : const double dfViewBoundScale =
554 1 : poOgrFeat->GetFieldAsDouble(iViewBoundScale);
555 1 : if (dfViewBoundScale > 0) // ATC 9
556 1 : poKmlLink->set_viewboundscale(dfViewBoundScale);
557 : }
558 :
559 3 : if (iViewFormat >= 0 && poOgrFeat->IsFieldSetAndNotNull(iViewFormat))
560 : {
561 : const char *const pszViewFormat =
562 1 : poOgrFeat->GetFieldAsString(iViewFormat);
563 1 : if (pszViewFormat[0] != '\0') // ATC 46
564 1 : poKmlLink->set_viewformat(pszViewFormat);
565 : }
566 :
567 3 : if (iHttpQuery >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHttpQuery))
568 : {
569 : const char *const pszHttpQuery =
570 1 : poOgrFeat->GetFieldAsString(iHttpQuery);
571 1 : if (strstr(pszHttpQuery, "[clientVersion]") != nullptr ||
572 0 : strstr(pszHttpQuery, "[kmlVersion]") != nullptr ||
573 0 : strstr(pszHttpQuery, "[clientName]") != nullptr ||
574 0 : strstr(pszHttpQuery, "[language]") != nullptr) // ATC 47
575 : {
576 1 : poKmlLink->set_httpquery(pszHttpQuery);
577 : }
578 : }
579 : }
580 :
581 : // Model.
582 422 : else if (!poKmlFeature && iModel >= 0 &&
583 2 : poOgrFeat->IsFieldSetAndNotNull(iModel) && poOgrGeom != nullptr &&
584 424 : !poOgrGeom->IsEmpty() &&
585 2 : wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint)
586 : {
587 4 : const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
588 2 : poKmlFeature = poKmlPlacemark;
589 :
590 2 : const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
591 4 : ModelPtr model = poKmlFactory->CreateModel();
592 :
593 4 : LocationPtr location = poKmlFactory->CreateLocation();
594 2 : model->set_location(location);
595 2 : location->set_latitude(poOgrPoint->getY());
596 2 : location->set_longitude(poOgrPoint->getX());
597 2 : if (poOgrPoint->getCoordinateDimension() == 3)
598 1 : location->set_altitude(poOgrPoint->getZ());
599 :
600 2 : int isGX = FALSE;
601 : const int iAltitudeMode =
602 2 : poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
603 2 : if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
604 : {
605 1 : const int nAltitudeMode = kmlAltitudeModeFromString(
606 : poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
607 1 : model->set_altitudemode(nAltitudeMode);
608 :
609 : // ATC 55
610 2 : if (nAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
611 1 : poOgrPoint->getCoordinateDimension() != 3)
612 : {
613 0 : if (CPLTestBool(
614 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
615 0 : CPLError(CE_Warning, CPLE_AppDefined,
616 : "Altitude should be defined");
617 : }
618 : }
619 :
620 2 : if ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
621 5 : (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
622 1 : (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll)))
623 : {
624 : OrientationPtr const orientation =
625 2 : poKmlFactory->CreateOrientation();
626 1 : model->set_orientation(orientation);
627 1 : if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
628 1 : orientation->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
629 : else
630 0 : orientation->set_heading(0);
631 1 : if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
632 1 : orientation->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
633 : else
634 0 : orientation->set_tilt(0);
635 1 : if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
636 1 : orientation->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
637 : else
638 0 : orientation->set_roll(0);
639 : }
640 2 : const int iScaleX = poOgrFeat->GetFieldIndex(oFC.scalexfield);
641 2 : const int iScaleY = poOgrFeat->GetFieldIndex(oFC.scaleyfield);
642 2 : const int iScaleZ = poOgrFeat->GetFieldIndex(oFC.scalezfield);
643 :
644 4 : const ScalePtr scale = poKmlFactory->CreateScale();
645 2 : model->set_scale(scale);
646 2 : if (iScaleX >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleX))
647 1 : scale->set_x(poOgrFeat->GetFieldAsDouble(iScaleX));
648 : else
649 1 : scale->set_x(1.0);
650 2 : if (iScaleY >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleY))
651 1 : scale->set_y(poOgrFeat->GetFieldAsDouble(iScaleY));
652 : else
653 1 : scale->set_y(1.0);
654 2 : if (iScaleZ >= 0 && poOgrFeat->IsFieldSetAndNotNull(iScaleZ))
655 1 : scale->set_z(poOgrFeat->GetFieldAsDouble(iScaleZ));
656 : else
657 1 : scale->set_z(1.0);
658 :
659 2 : const LinkPtr link = poKmlFactory->CreateLink();
660 2 : model->set_link(link);
661 2 : const char *const pszURL = poOgrFeat->GetFieldAsString(oFC.modelfield);
662 2 : link->set_href(pszURL);
663 :
664 : // Collada 3D file?
665 3 : if (EQUAL(CPLGetExtension(pszURL), "dae") &&
666 1 : CPLTestBool(CPLGetConfigOption("LIBKML_ADD_RESOURCE_MAP", "TRUE")))
667 : {
668 1 : VSILFILE *fp = nullptr;
669 1 : bool bIsURL = false;
670 1 : if (STARTS_WITH_CI(pszURL, "http://") ||
671 0 : STARTS_WITH_CI(pszURL, "https://"))
672 : {
673 1 : bIsURL = true;
674 1 : fp = VSIFOpenL(CPLSPrintf("/vsicurl/%s", pszURL), "rb");
675 : }
676 0 : else if (strstr(pszURL, ".kmz/") != nullptr)
677 : {
678 0 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszURL), "rb");
679 : }
680 : else
681 : {
682 0 : fp = VSIFOpenL(pszURL, "rb");
683 : }
684 1 : if (fp != nullptr)
685 : {
686 0 : ResourceMapPtr resourceMap = nullptr;
687 0 : const char *pszLine = nullptr;
688 0 : while ((pszLine = CPLReadLineL(fp)) != nullptr)
689 : {
690 0 : const char *pszInitFrom = strstr(pszLine, "<init_from>");
691 0 : if (pszInitFrom)
692 : {
693 0 : pszInitFrom += strlen("<init_from>");
694 : const char *const pszInitFromEnd =
695 0 : strstr(pszInitFrom, "</init_from>");
696 0 : if (pszInitFromEnd)
697 : {
698 0 : CPLString osImage(pszInitFrom);
699 0 : osImage.resize(pszInitFromEnd - pszInitFrom);
700 : const char *const pszExtension =
701 0 : CPLGetExtension(osImage);
702 0 : if (EQUAL(pszExtension, "jpg") ||
703 0 : EQUAL(pszExtension, "jpeg") ||
704 0 : EQUAL(pszExtension, "png") ||
705 0 : EQUAL(pszExtension, "gif"))
706 : {
707 0 : if (!resourceMap)
708 : resourceMap =
709 0 : poKmlFactory->CreateResourceMap();
710 : const AliasPtr alias =
711 0 : poKmlFactory->CreateAlias();
712 0 : if (bIsURL && CPLIsFilenameRelative(osImage))
713 : {
714 0 : if (STARTS_WITH(pszURL, "http"))
715 0 : alias->set_targethref(CPLSPrintf(
716 : "%s/%s", CPLGetPath(pszURL),
717 : osImage.c_str()));
718 : else
719 0 : alias->set_targethref(
720 : CPLFormFilename(CPLGetPath(pszURL),
721 : osImage, nullptr));
722 : }
723 : else
724 0 : alias->set_targethref(osImage);
725 0 : alias->set_sourcehref(osImage);
726 0 : resourceMap->add_alias(alias);
727 : }
728 : }
729 : }
730 : }
731 0 : if (resourceMap)
732 0 : model->set_resourcemap(resourceMap);
733 0 : VSIFCloseL(fp);
734 : }
735 : }
736 :
737 2 : poKmlPlacemark->set_geometry(AsGeometry(model));
738 : }
739 :
740 : // Camera.
741 418 : else if (!poKmlFeature && poOgrGeom != nullptr && !poOgrGeom->IsEmpty() &&
742 156 : wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint &&
743 468 : poOgrFeat->GetFieldIndex(oFC.camera_longitude_field) < 0 &&
744 50 : ((iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading)) ||
745 48 : (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt)) ||
746 0 : (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))))
747 : {
748 2 : const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
749 2 : poKmlFeature = poKmlPlacemark;
750 :
751 2 : const OGRPoint *const poOgrPoint = poOgrGeom->toPoint();
752 2 : camera = poKmlFactory->CreateCamera();
753 2 : camera->set_latitude(poOgrPoint->getY());
754 2 : camera->set_longitude(poOgrPoint->getX());
755 2 : int isGX = FALSE;
756 : const int iAltitudeMode =
757 2 : poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
758 2 : if (poOgrFeat->IsFieldSetAndNotNull(iAltitudeMode))
759 : {
760 1 : const int nAltitudeMode = kmlAltitudeModeFromString(
761 : poOgrFeat->GetFieldAsString(iAltitudeMode), isGX);
762 1 : camera->set_altitudemode(nAltitudeMode);
763 : }
764 1 : else if (CPLTestBool(
765 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
766 : {
767 1 : CPLError(CE_Warning, CPLE_AppDefined,
768 : "Camera should define altitudeMode != 'clampToGround'");
769 : }
770 :
771 2 : if (poOgrPoint->getCoordinateDimension() == 3)
772 : {
773 1 : camera->set_altitude(poOgrPoint->getZ());
774 : }
775 1 : else if (CPLTestBool(
776 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")))
777 : {
778 1 : CPLError(CE_Warning, CPLE_AppDefined,
779 : "Camera should have an altitude/Z");
780 1 : camera->set_altitude(0.0);
781 : }
782 :
783 2 : if (iHeading >= 0 && poOgrFeat->IsFieldSetAndNotNull(iHeading))
784 2 : camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading));
785 2 : if (iTilt >= 0 && poOgrFeat->IsFieldSetAndNotNull(iTilt))
786 1 : camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt));
787 2 : if (iRoll >= 0 && poOgrFeat->IsFieldSetAndNotNull(iRoll))
788 1 : camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll));
789 2 : poKmlPlacemark->set_abstractview(camera);
790 : }
791 208 : else if (!poKmlFeature)
792 : {
793 206 : const PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
794 206 : poKmlFeature = poKmlPlacemark;
795 :
796 206 : if (poOgrGeom)
797 : {
798 155 : ElementPtr poKmlElement = geom2kml(poOgrGeom, -1, poKmlFactory);
799 155 : if (!poKmlElement)
800 : {
801 13 : CPLError(CE_Failure, CPLE_AppDefined,
802 : "Cannot translate feature: %s",
803 26 : poOgrFeat->DumpReadableAsString().c_str());
804 13 : return nullptr;
805 : }
806 :
807 142 : poKmlPlacemark->set_geometry(AsGeometry(std::move(poKmlElement)));
808 : }
809 : }
810 :
811 202 : if (!camera)
812 396 : camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat,
813 198 : poKmlFactory);
814 202 : if (camera)
815 4 : poKmlFeature->set_abstractview(camera);
816 :
817 : /***** style *****/
818 202 : featurestyle2kml(poOgrDS, poOgrLayer, poOgrFeat, poKmlFactory,
819 : poKmlFeature);
820 :
821 : /***** fields *****/
822 202 : field2kml(poOgrFeat, poOgrLayer, poKmlFactory, poKmlFeature,
823 : bUseSimpleField, oFC);
824 :
825 202 : return poKmlFeature;
826 : }
827 :
828 1251 : OGRFeature *kml2feat(PlacemarkPtr poKmlPlacemark, OGRLIBKMLDataSource *poOgrDS,
829 : OGRLIBKMLLayer *poOgrLayer, OGRFeatureDefn *poOgrFeatDefn,
830 : OGRSpatialReference *poOgrSRS)
831 : {
832 1251 : OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
833 :
834 : /***** style *****/
835 1251 : kml2featurestyle(poKmlPlacemark, poOgrDS, poOgrLayer, poOgrFeat);
836 :
837 : /***** geometry *****/
838 1251 : if (poKmlPlacemark->has_geometry())
839 : {
840 : OGRGeometry *const poOgrGeom =
841 1187 : kml2geom(poKmlPlacemark->get_geometry(), poOgrSRS);
842 1187 : poOgrFeat->SetGeometryDirectly(poOgrGeom);
843 : }
844 66 : else if (poKmlPlacemark->has_abstractview() &&
845 2 : poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
846 : {
847 4 : const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
848 2 : if (camera->has_longitude() && camera->has_latitude())
849 : {
850 2 : if (camera->has_altitude())
851 2 : poOgrFeat->SetGeometryDirectly(new OGRPoint(
852 2 : camera->get_longitude(), camera->get_latitude(),
853 2 : camera->get_altitude()));
854 : else
855 0 : poOgrFeat->SetGeometryDirectly(new OGRPoint(
856 0 : camera->get_longitude(), camera->get_latitude()));
857 2 : poOgrFeat->GetGeometryRef()->assignSpatialReference(poOgrSRS);
858 : }
859 : }
860 :
861 : /***** fields *****/
862 1251 : kml2field(poOgrFeat, AsFeature(poKmlPlacemark),
863 : poOgrLayer->GetFieldConfig());
864 :
865 1251 : return poOgrFeat;
866 : }
867 :
868 42 : OGRFeature *kmlgroundoverlay2feat(GroundOverlayPtr poKmlOverlay,
869 : OGRLIBKMLDataSource * /* poOgrDS */,
870 : OGRLIBKMLLayer *poOgrLayer,
871 : OGRFeatureDefn *poOgrFeatDefn,
872 : OGRSpatialReference *poOgrSRS)
873 : {
874 42 : OGRFeature *poOgrFeat = new OGRFeature(poOgrFeatDefn);
875 :
876 : /***** geometry *****/
877 42 : if (poKmlOverlay->has_latlonbox())
878 : {
879 : OGRGeometry *const poOgrGeom =
880 42 : kml2geom_latlonbox(poKmlOverlay->get_latlonbox(), poOgrSRS);
881 42 : poOgrFeat->SetGeometryDirectly(poOgrGeom);
882 : }
883 0 : else if (poKmlOverlay->has_gx_latlonquad())
884 : {
885 : OGRGeometry *const poOgrGeom =
886 0 : kml2geom_latlonquad(poKmlOverlay->get_gx_latlonquad(), poOgrSRS);
887 0 : poOgrFeat->SetGeometryDirectly(poOgrGeom);
888 : }
889 :
890 : /***** fields *****/
891 42 : kml2field(poOgrFeat, AsFeature(poKmlOverlay), poOgrLayer->GetFieldConfig());
892 :
893 42 : return poOgrFeat;
894 : }
|