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