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