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) 2011-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 :
32 : #include <set>
33 : #include <string>
34 :
35 : #include "ogr_featurestyle.h"
36 : #include "ogrlibkmlstyle.h"
37 : #include "ogr_libkml.h"
38 :
39 : using kmlbase::Color32;
40 : using kmldom::BalloonStylePtr;
41 : using kmldom::ContainerPtr;
42 : using kmldom::DocumentPtr;
43 : using kmldom::ElementPtr;
44 : using kmldom::FeaturePtr;
45 : using kmldom::HotSpotPtr;
46 : using kmldom::IconStyleIconPtr;
47 : using kmldom::IconStylePtr;
48 : using kmldom::ItemIconPtr;
49 : using kmldom::KmlFactory;
50 : using kmldom::KmlPtr;
51 : using kmldom::LabelStylePtr;
52 : using kmldom::LineStylePtr;
53 : using kmldom::ListStylePtr;
54 : using kmldom::ObjectPtr;
55 : using kmldom::PairPtr;
56 : using kmldom::PolyStylePtr;
57 : using kmldom::StyleMapPtr;
58 : using kmldom::StylePtr;
59 : using kmldom::StyleSelectorPtr;
60 : using kmldom::STYLESTATE_HIGHLIGHT;
61 : using kmldom::STYLESTATE_NORMAL;
62 :
63 : /******************************************************************************
64 : Generic function to parse a stylestring and add to a kml style.
65 :
66 : Args:
67 : pszStyleString the stylestring to parse
68 : poKmlStyle the kml style to add to (or NULL)
69 : poKmlFactory the kml dom factory
70 :
71 : Returns:
72 : the kml style
73 :
74 : ******************************************************************************/
75 :
76 9 : StylePtr addstylestring2kml(const char *pszStyleString, StylePtr poKmlStyle,
77 : KmlFactory *poKmlFactory, FeaturePtr poKmlFeature)
78 : {
79 : /***** just bail now if stylestring is empty *****/
80 9 : if (!pszStyleString || !*pszStyleString)
81 : {
82 0 : return poKmlStyle;
83 : }
84 :
85 18 : LineStylePtr poKmlLineStyle = nullptr;
86 18 : PolyStylePtr poKmlPolyStyle = nullptr;
87 18 : IconStylePtr poKmlIconStyle = nullptr;
88 18 : LabelStylePtr poKmlLabelStyle = nullptr;
89 :
90 : /***** create and init a style mamager with the style string *****/
91 9 : OGRStyleMgr *const poOgrSM = new OGRStyleMgr;
92 :
93 9 : poOgrSM->InitStyleString(pszStyleString);
94 :
95 : /***** loop though the style parts *****/
96 26 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
97 : {
98 17 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
99 :
100 17 : if (!poOgrST)
101 : {
102 0 : continue;
103 : }
104 :
105 17 : switch (poOgrST->GetType())
106 : {
107 4 : case OGRSTCPen:
108 : {
109 4 : poKmlLineStyle = poKmlFactory->CreateLineStyle();
110 :
111 : OGRStylePen *poStylePen =
112 4 : cpl::down_cast<OGRStylePen *>(poOgrST);
113 :
114 : /***** pen color *****/
115 4 : GBool nullcheck = FALSE;
116 4 : const char *const pszColor = poStylePen->Color(nullcheck);
117 :
118 4 : int nR = 0;
119 4 : int nG = 0;
120 4 : int nB = 0;
121 4 : int nA = 0;
122 8 : if (!nullcheck &&
123 4 : poStylePen->GetRGBFromString(pszColor, nR, nG, nB, nA))
124 : {
125 4 : poKmlLineStyle->set_color(Color32(
126 : static_cast<GByte>(nA), static_cast<GByte>(nB),
127 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
128 : }
129 4 : poStylePen->SetUnit(OGRSTUPixel);
130 4 : double dfWidth = poStylePen->Width(nullcheck);
131 :
132 4 : if (nullcheck)
133 0 : dfWidth = 1.0;
134 :
135 4 : poKmlLineStyle->set_width(dfWidth);
136 :
137 4 : break;
138 : }
139 4 : case OGRSTCBrush:
140 : {
141 : OGRStyleBrush *const poStyleBrush =
142 4 : cpl::down_cast<OGRStyleBrush *>(poOgrST);
143 :
144 : /***** brush color *****/
145 4 : GBool nullcheck = FALSE;
146 4 : const char *pszColor = poStyleBrush->ForeColor(nullcheck);
147 :
148 4 : int nR = 0;
149 4 : int nG = 0;
150 4 : int nB = 0;
151 4 : int nA = 0;
152 8 : if (!nullcheck &&
153 4 : poStyleBrush->GetRGBFromString(pszColor, nR, nG, nB, nA))
154 : {
155 4 : poKmlPolyStyle = poKmlFactory->CreatePolyStyle();
156 4 : poKmlPolyStyle->set_color(Color32(
157 : static_cast<GByte>(nA), static_cast<GByte>(nB),
158 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
159 : }
160 4 : break;
161 : }
162 6 : case OGRSTCSymbol:
163 : {
164 : OGRStyleSymbol *const poStyleSymbol =
165 6 : cpl::down_cast<OGRStyleSymbol *>(poOgrST);
166 :
167 : /***** id (kml icon) *****/
168 6 : GBool nullcheck = FALSE;
169 6 : const char *pszId = poStyleSymbol->Id(nullcheck);
170 :
171 6 : if (!nullcheck)
172 : {
173 6 : if (!poKmlIconStyle)
174 6 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
175 :
176 : /***** split it at the ,'s *****/
177 6 : char **papszTokens = CSLTokenizeString2(
178 : pszId, ",",
179 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
180 : CSLT_STRIPENDSPACES);
181 :
182 6 : if (papszTokens)
183 : {
184 : // Just take the first one.
185 : // TODO: Come up with a better idea.
186 6 : if (papszTokens[0])
187 : {
188 : IconStyleIconPtr poKmlIcon =
189 12 : poKmlFactory->CreateIconStyleIcon();
190 6 : poKmlIcon->set_href(papszTokens[0]);
191 6 : poKmlIconStyle->set_icon(poKmlIcon);
192 : }
193 :
194 6 : CSLDestroy(papszTokens);
195 : }
196 : }
197 :
198 : /***** heading *****/
199 6 : double heading = poStyleSymbol->Angle(nullcheck);
200 :
201 6 : if (!nullcheck)
202 : {
203 3 : if (!poKmlIconStyle)
204 0 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
205 3 : poKmlIconStyle->set_heading(heading);
206 : }
207 :
208 : /***** scale *****/
209 6 : double dfScale = poStyleSymbol->Size(nullcheck);
210 :
211 6 : if (!nullcheck)
212 : {
213 3 : if (!poKmlIconStyle)
214 0 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
215 :
216 3 : poKmlIconStyle->set_scale(dfScale);
217 : }
218 :
219 : /***** color *****/
220 6 : const char *const pszcolor = poStyleSymbol->Color(nullcheck);
221 :
222 6 : int nR = 0;
223 6 : int nG = 0;
224 6 : int nB = 0;
225 6 : int nA = 0;
226 11 : if (!nullcheck &&
227 5 : poOgrST->GetRGBFromString(pszcolor, nR, nG, nB, nA))
228 : {
229 5 : poKmlIconStyle->set_color(Color32(
230 : static_cast<GByte>(nA), static_cast<GByte>(nB),
231 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
232 : }
233 :
234 : /***** hotspot *****/
235 :
236 6 : double dfDx = poStyleSymbol->SpacingX(nullcheck);
237 6 : GBool nullcheck2 = FALSE;
238 6 : double dfDy = poStyleSymbol->SpacingY(nullcheck2);
239 :
240 6 : if (!nullcheck && !nullcheck2)
241 : {
242 2 : if (!poKmlIconStyle)
243 0 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
244 :
245 4 : HotSpotPtr poKmlHotSpot = poKmlFactory->CreateHotSpot();
246 2 : if (poKmlHotSpot)
247 : {
248 2 : poKmlHotSpot->set_x(dfDx);
249 2 : poKmlHotSpot->set_y(dfDy);
250 :
251 2 : poKmlIconStyle->set_hotspot(poKmlHotSpot);
252 : }
253 : }
254 :
255 6 : break;
256 : }
257 3 : case OGRSTCLabel:
258 : {
259 : GBool nullcheck;
260 : GBool nullcheck2;
261 :
262 : OGRStyleLabel *poStyleLabel =
263 3 : cpl::down_cast<OGRStyleLabel *>(poOgrST);
264 :
265 : /***** color *****/
266 3 : const char *pszcolor = poStyleLabel->ForeColor(nullcheck);
267 :
268 3 : int nR = 0;
269 3 : int nG = 0;
270 3 : int nB = 0;
271 3 : int nA = 0;
272 6 : if (!nullcheck &&
273 3 : poStyleLabel->GetRGBFromString(pszcolor, nR, nG, nB, nA))
274 : {
275 3 : if (!poKmlLabelStyle)
276 3 : poKmlLabelStyle = poKmlFactory->CreateLabelStyle();
277 3 : poKmlLabelStyle->set_color(Color32(
278 : static_cast<GByte>(nA), static_cast<GByte>(nB),
279 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
280 : }
281 :
282 : /***** scale *****/
283 3 : double dfScale = poStyleLabel->Stretch(nullcheck);
284 :
285 3 : if (!nullcheck)
286 : {
287 3 : dfScale /= 100.0;
288 3 : if (!poKmlLabelStyle)
289 0 : poKmlLabelStyle = poKmlFactory->CreateLabelStyle();
290 3 : poKmlLabelStyle->set_scale(dfScale);
291 : }
292 :
293 : /***** heading *****/
294 3 : const double heading = poStyleLabel->Angle(nullcheck);
295 :
296 3 : if (!nullcheck)
297 : {
298 0 : if (!poKmlIconStyle)
299 : {
300 0 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
301 : const IconStyleIconPtr poKmlIcon =
302 0 : poKmlFactory->CreateIconStyleIcon();
303 0 : poKmlIconStyle->set_icon(poKmlIcon);
304 : }
305 :
306 0 : poKmlIconStyle->set_heading(heading);
307 : }
308 :
309 : /***** hotspot *****/
310 3 : const double dfDx = poStyleLabel->SpacingX(nullcheck);
311 3 : const double dfDy = poStyleLabel->SpacingY(nullcheck2);
312 :
313 3 : if (!nullcheck && !nullcheck2)
314 : {
315 0 : if (!poKmlIconStyle)
316 : {
317 0 : poKmlIconStyle = poKmlFactory->CreateIconStyle();
318 : const IconStyleIconPtr poKmlIcon =
319 0 : poKmlFactory->CreateIconStyleIcon();
320 0 : poKmlIconStyle->set_icon(poKmlIcon);
321 : }
322 :
323 0 : HotSpotPtr poKmlHotSpot = poKmlFactory->CreateHotSpot();
324 0 : if (poKmlHotSpot)
325 : {
326 0 : poKmlHotSpot->set_x(dfDx);
327 0 : poKmlHotSpot->set_y(dfDy);
328 :
329 0 : poKmlIconStyle->set_hotspot(poKmlHotSpot);
330 : }
331 : }
332 :
333 : /***** label text *****/
334 3 : const char *const pszText = poStyleLabel->TextString(nullcheck);
335 :
336 3 : if (!nullcheck && poKmlFeature)
337 : {
338 0 : poKmlFeature->set_name(pszText);
339 : }
340 :
341 3 : break;
342 : }
343 0 : case OGRSTCNone:
344 : default:
345 : {
346 0 : break;
347 : }
348 : }
349 :
350 17 : delete poOgrST;
351 : }
352 :
353 9 : if (poKmlLineStyle || poKmlPolyStyle || poKmlIconStyle || poKmlLabelStyle)
354 : {
355 9 : if (!poKmlStyle)
356 1 : poKmlStyle = poKmlFactory->CreateStyle();
357 :
358 9 : if (poKmlLineStyle)
359 4 : poKmlStyle->set_linestyle(poKmlLineStyle);
360 :
361 9 : if (poKmlPolyStyle)
362 4 : poKmlStyle->set_polystyle(poKmlPolyStyle);
363 :
364 9 : if (poKmlIconStyle)
365 6 : poKmlStyle->set_iconstyle(poKmlIconStyle);
366 :
367 9 : if (poKmlLabelStyle)
368 3 : poKmlStyle->set_labelstyle(poKmlLabelStyle);
369 : }
370 :
371 9 : delete poOgrSM;
372 :
373 9 : return poKmlStyle;
374 : }
375 :
376 : /******************************************************************************
377 : kml2pen
378 : ******************************************************************************/
379 :
380 103 : static OGRStylePen *kml2pen(LineStylePtr poKmlLineStyle,
381 : OGRStylePen *poOgrStylePen)
382 : {
383 103 : if (!poOgrStylePen)
384 103 : poOgrStylePen = new OGRStylePen();
385 :
386 : /***** <LineStyle> should always have a width in pixels *****/
387 103 : poOgrStylePen->SetUnit(OGRSTUPixel);
388 :
389 : /***** width *****/
390 103 : if (poKmlLineStyle->has_width())
391 83 : poOgrStylePen->SetWidth(poKmlLineStyle->get_width());
392 :
393 : /***** color *****/
394 103 : if (poKmlLineStyle->has_color())
395 : {
396 53 : Color32 poKmlColor = poKmlLineStyle->get_color();
397 53 : char szColor[10] = {};
398 53 : snprintf(szColor, sizeof(szColor), "#%02X%02X%02X%02X",
399 : poKmlColor.get_red(), poKmlColor.get_green(),
400 : poKmlColor.get_blue(), poKmlColor.get_alpha());
401 53 : poOgrStylePen->SetColor(szColor);
402 : }
403 :
404 103 : return poOgrStylePen;
405 : }
406 :
407 : /******************************************************************************
408 : kml2brush
409 : ******************************************************************************/
410 :
411 83 : static OGRStyleBrush *kml2brush(PolyStylePtr poKmlPolyStyle,
412 : OGRStyleBrush *poOgrStyleBrush)
413 : {
414 83 : if (!poOgrStyleBrush)
415 83 : poOgrStyleBrush = new OGRStyleBrush();
416 :
417 : /***** color *****/
418 83 : if (poKmlPolyStyle->has_color())
419 : {
420 83 : Color32 poKmlColor = poKmlPolyStyle->get_color();
421 83 : char szColor[10] = {};
422 83 : snprintf(szColor, sizeof(szColor), "#%02X%02X%02X%02X",
423 : poKmlColor.get_red(), poKmlColor.get_green(),
424 : poKmlColor.get_blue(), poKmlColor.get_alpha());
425 83 : poOgrStyleBrush->SetForeColor(szColor);
426 : }
427 :
428 83 : return poOgrStyleBrush;
429 : }
430 :
431 : /******************************************************************************
432 : kml2symbol
433 : ******************************************************************************/
434 :
435 44 : static OGRStyleSymbol *kml2symbol(IconStylePtr poKmlIconStyle,
436 : OGRStyleSymbol *poOgrStyleSymbol)
437 : {
438 44 : if (!poOgrStyleSymbol)
439 44 : poOgrStyleSymbol = new OGRStyleSymbol();
440 :
441 : /***** id (kml icon) *****/
442 44 : if (poKmlIconStyle->has_icon())
443 : {
444 88 : IconStyleIconPtr poKmlIcon = poKmlIconStyle->get_icon();
445 :
446 44 : if (poKmlIcon->has_href())
447 : {
448 88 : std::string oIcon = "\"";
449 44 : oIcon.append(poKmlIcon->get_href().c_str());
450 44 : oIcon.append("\"");
451 44 : poOgrStyleSymbol->SetId(oIcon.c_str());
452 : }
453 : }
454 :
455 : /***** heading *****/
456 44 : if (poKmlIconStyle->has_heading())
457 3 : poOgrStyleSymbol->SetAngle(poKmlIconStyle->get_heading());
458 :
459 : /***** scale *****/
460 44 : if (poKmlIconStyle->has_scale())
461 3 : poOgrStyleSymbol->SetSize(poKmlIconStyle->get_scale());
462 :
463 : /***** color *****/
464 44 : if (poKmlIconStyle->has_color())
465 : {
466 3 : Color32 poKmlColor = poKmlIconStyle->get_color();
467 3 : char szColor[10] = {};
468 3 : snprintf(szColor, sizeof(szColor), "#%02X%02X%02X%02X",
469 : poKmlColor.get_red(), poKmlColor.get_green(),
470 : poKmlColor.get_blue(), poKmlColor.get_alpha());
471 3 : poOgrStyleSymbol->SetColor(szColor);
472 : }
473 :
474 : /***** hotspot *****/
475 44 : if (poKmlIconStyle->has_hotspot())
476 : {
477 4 : const HotSpotPtr poKmlHotSpot = poKmlIconStyle->get_hotspot();
478 :
479 2 : if (poKmlHotSpot->has_x())
480 2 : poOgrStyleSymbol->SetSpacingX(poKmlHotSpot->get_x());
481 2 : if (poKmlHotSpot->has_y())
482 2 : poOgrStyleSymbol->SetSpacingY(poKmlHotSpot->get_y());
483 : }
484 :
485 44 : return poOgrStyleSymbol;
486 : }
487 :
488 : /******************************************************************************
489 : kml2label
490 : ******************************************************************************/
491 :
492 3 : static OGRStyleLabel *kml2label(LabelStylePtr poKmlLabelStyle,
493 : OGRStyleLabel *poOgrStyleLabel)
494 : {
495 3 : if (!poOgrStyleLabel)
496 3 : poOgrStyleLabel = new OGRStyleLabel();
497 :
498 : /***** color *****/
499 3 : if (poKmlLabelStyle->has_color())
500 : {
501 3 : Color32 poKmlColor = poKmlLabelStyle->get_color();
502 3 : char szColor[10] = {};
503 3 : snprintf(szColor, sizeof(szColor), "#%02X%02X%02X%02X",
504 : poKmlColor.get_red(), poKmlColor.get_green(),
505 : poKmlColor.get_blue(), poKmlColor.get_alpha());
506 3 : poOgrStyleLabel->SetForColor(szColor);
507 : }
508 :
509 3 : if (poKmlLabelStyle->has_scale())
510 : {
511 3 : double dfScale = poKmlLabelStyle->get_scale();
512 3 : dfScale *= 100.0;
513 :
514 3 : poOgrStyleLabel->SetStretch(dfScale);
515 : }
516 :
517 3 : return poOgrStyleLabel;
518 : }
519 :
520 : /******************************************************************************
521 : Function to add a kml style to a style table.
522 : ******************************************************************************/
523 :
524 145 : static void kml2styletable(OGRStyleTable *poOgrStyleTable, StylePtr poKmlStyle)
525 : {
526 : /***** No reason to add it if it don't have an id. *****/
527 145 : if (!poKmlStyle->has_id())
528 : {
529 0 : CPLError(CE_Warning, CPLE_AppDefined, "ERROR parsing kml Style: No id");
530 0 : return;
531 : }
532 :
533 145 : OGRStyleMgr *poOgrSM = new OGRStyleMgr(poOgrStyleTable);
534 :
535 145 : poOgrSM->InitStyleString(nullptr);
536 :
537 : /***** read the style *****/
538 145 : kml2stylestring(poKmlStyle, poOgrSM);
539 :
540 : /***** add the style to the style table *****/
541 290 : const std::string oName = poKmlStyle->get_id();
542 :
543 145 : poOgrSM->AddStyle(CPLString().Printf("%s", oName.c_str()), nullptr);
544 :
545 : /***** Cleanup the style manager. *****/
546 145 : delete poOgrSM;
547 : }
548 :
549 : /******************************************************************************
550 : Function to follow the kml stylemap if one exists.
551 : ******************************************************************************/
552 :
553 : StyleSelectorPtr
554 1 : StyleFromStyleSelector(const StyleSelectorPtr &poKmlStyleSelector,
555 : OGRStyleTable *poStyleTable)
556 : {
557 : /***** Is it a style? *****/
558 1 : if (poKmlStyleSelector->IsA(kmldom::Type_Style))
559 1 : return poKmlStyleSelector;
560 :
561 : /***** Is it a style map? *****/
562 :
563 0 : else if (poKmlStyleSelector->IsA(kmldom::Type_StyleMap))
564 0 : return StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyleSelector),
565 0 : poStyleTable);
566 :
567 : /***** Not a style or a style map. *****/
568 0 : return nullptr;
569 : }
570 :
571 : /******************************************************************************
572 : kml2stylemgr
573 : ******************************************************************************/
574 :
575 146 : void kml2stylestring(StylePtr poKmlStyle, OGRStyleMgr *poOgrSM)
576 :
577 : {
578 146 : OGRStyleMgr *const poOgrNewSM = new OGRStyleMgr(nullptr);
579 :
580 : /***** linestyle / pen *****/
581 146 : if (poKmlStyle->has_linestyle())
582 : {
583 103 : poOgrNewSM->InitStyleString(nullptr);
584 :
585 206 : LineStylePtr poKmlLineStyle = poKmlStyle->get_linestyle();
586 :
587 103 : OGRStyleTool *poOgrTmpST = nullptr;
588 103 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
589 : {
590 0 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
591 :
592 0 : if (!poOgrST)
593 0 : continue;
594 :
595 0 : if (poOgrST->GetType() == OGRSTCPen && poOgrTmpST == nullptr)
596 : {
597 0 : poOgrTmpST = poOgrST;
598 : }
599 : else
600 : {
601 0 : poOgrNewSM->AddPart(poOgrST);
602 0 : delete poOgrST;
603 : }
604 : }
605 :
606 : OGRStylePen *poOgrStylePen =
607 103 : kml2pen(std::move(poKmlLineStyle), (OGRStylePen *)poOgrTmpST);
608 :
609 103 : poOgrNewSM->AddPart(poOgrStylePen);
610 :
611 103 : delete poOgrStylePen;
612 103 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
613 : }
614 :
615 : /***** polystyle / brush *****/
616 146 : if (poKmlStyle->has_polystyle())
617 : {
618 83 : poOgrNewSM->InitStyleString(nullptr);
619 :
620 166 : PolyStylePtr poKmlPolyStyle = poKmlStyle->get_polystyle();
621 :
622 83 : OGRStyleTool *poOgrTmpST = nullptr;
623 166 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
624 : {
625 83 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
626 :
627 83 : if (!poOgrST)
628 0 : continue;
629 :
630 83 : if (poOgrST->GetType() == OGRSTCBrush && poOgrTmpST == nullptr)
631 : {
632 0 : poOgrTmpST = poOgrST;
633 : }
634 : else
635 : {
636 83 : poOgrNewSM->AddPart(poOgrST);
637 83 : delete poOgrST;
638 : }
639 : }
640 :
641 : OGRStyleBrush *poOgrStyleBrush =
642 83 : kml2brush(std::move(poKmlPolyStyle), (OGRStyleBrush *)poOgrTmpST);
643 :
644 83 : poOgrNewSM->AddPart(poOgrStyleBrush);
645 :
646 83 : delete poOgrStyleBrush;
647 83 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
648 : }
649 :
650 : /***** iconstyle / symbol *****/
651 146 : if (poKmlStyle->has_iconstyle())
652 : {
653 44 : poOgrNewSM->InitStyleString(nullptr);
654 :
655 88 : IconStylePtr poKmlIconStyle = poKmlStyle->get_iconstyle();
656 :
657 44 : OGRStyleTool *poOgrTmpST = nullptr;
658 56 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
659 : {
660 12 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
661 :
662 12 : if (!poOgrST)
663 0 : continue;
664 :
665 12 : if (poOgrST->GetType() == OGRSTCSymbol && poOgrTmpST == nullptr)
666 : {
667 0 : poOgrTmpST = poOgrST;
668 : }
669 : else
670 : {
671 12 : poOgrNewSM->AddPart(poOgrST);
672 12 : delete poOgrST;
673 : }
674 : }
675 :
676 : OGRStyleSymbol *poOgrStyleSymbol =
677 44 : kml2symbol(std::move(poKmlIconStyle), (OGRStyleSymbol *)poOgrTmpST);
678 :
679 44 : poOgrNewSM->AddPart(poOgrStyleSymbol);
680 :
681 44 : delete poOgrStyleSymbol;
682 44 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
683 : }
684 :
685 : /***** labelstyle / label *****/
686 146 : if (poKmlStyle->has_labelstyle())
687 : {
688 3 : poOgrNewSM->InitStyleString(nullptr);
689 :
690 6 : LabelStylePtr poKmlLabelStyle = poKmlStyle->get_labelstyle();
691 :
692 3 : OGRStyleTool *poOgrTmpST = nullptr;
693 8 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
694 : {
695 5 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
696 :
697 5 : if (!poOgrST)
698 0 : continue;
699 :
700 5 : if (poOgrST->GetType() == OGRSTCLabel && poOgrTmpST == nullptr)
701 : {
702 0 : poOgrTmpST = poOgrST;
703 : }
704 : else
705 : {
706 5 : poOgrNewSM->AddPart(poOgrST);
707 5 : delete poOgrST;
708 : }
709 : }
710 :
711 : OGRStyleLabel *poOgrStyleLabel =
712 3 : kml2label(std::move(poKmlLabelStyle), (OGRStyleLabel *)poOgrTmpST);
713 :
714 3 : poOgrNewSM->AddPart(poOgrStyleLabel);
715 :
716 3 : delete poOgrStyleLabel;
717 3 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
718 : }
719 :
720 146 : delete poOgrNewSM;
721 146 : }
722 :
723 : /******************************************************************************
724 : Function to get the container from the kmlroot.
725 :
726 : Args: poKmlRoot the root element
727 :
728 : Returns: root if its a container, if its a kml the container it
729 : contains, or NULL
730 :
731 : ******************************************************************************/
732 :
733 0 : static ContainerPtr MyGetContainerFromRoot(KmlFactory *poKmlFactory,
734 : ElementPtr poKmlRoot)
735 : {
736 0 : ContainerPtr poKmlContainer = nullptr;
737 :
738 0 : if (poKmlRoot)
739 : {
740 : /***** skip over the <kml> we want the container *****/
741 0 : if (poKmlRoot->IsA(kmldom::Type_kml))
742 : {
743 0 : KmlPtr poKmlKml = AsKml(poKmlRoot);
744 :
745 0 : if (poKmlKml && poKmlKml->has_feature())
746 : {
747 0 : FeaturePtr poKmlFeat = poKmlKml->get_feature();
748 :
749 0 : if (poKmlFeat->IsA(kmldom::Type_Container))
750 : {
751 0 : poKmlContainer = AsContainer(poKmlFeat);
752 : }
753 0 : else if (poKmlFeat->IsA(kmldom::Type_Placemark))
754 : {
755 0 : poKmlContainer = poKmlFactory->CreateDocument();
756 0 : poKmlContainer->add_feature(
757 0 : kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
758 : }
759 : }
760 : }
761 0 : else if (poKmlRoot->IsA(kmldom::Type_Container))
762 : {
763 0 : poKmlContainer = AsContainer(std::move(poKmlRoot));
764 : }
765 : }
766 :
767 0 : return poKmlContainer;
768 : }
769 :
770 11 : static StyleSelectorPtr StyleFromStyleURL(const StyleMapPtr &stylemap,
771 : const string &styleurl,
772 : OGRStyleTable *poStyleTable)
773 : {
774 : // TODO:: Parse the styleURL.
775 11 : char *pszUrl = CPLStrdup(styleurl.c_str());
776 11 : char *pszStyleMapId = CPLStrdup(stylemap->get_id().c_str());
777 :
778 : /***** Is it an internal style ref that starts with a #? *****/
779 11 : if (*pszUrl == '#' && poStyleTable)
780 : {
781 : /***** Search the style table for the style we *****/
782 : /***** want and copy it back into the table. *****/
783 11 : const char *pszTest = poStyleTable->Find(pszUrl + 1);
784 11 : if (pszTest)
785 : {
786 11 : poStyleTable->AddStyle(pszStyleMapId, pszTest);
787 11 : }
788 : }
789 :
790 : /***** We have a real URL and need to go out and fetch it *****/
791 : /***** FIXME this could be a relative path in a kmz *****/
792 0 : else if (strchr(pszUrl, '#'))
793 : {
794 : const char *pszFetch =
795 0 : CPLGetConfigOption("LIBKML_EXTERNAL_STYLE", "no");
796 0 : if (CPLTestBool(pszFetch))
797 : {
798 : /***** Lets go out and fetch the style from the external URL *****/
799 0 : char *pszUrlTmp = CPLStrdup(pszUrl);
800 0 : char *pszPound = strchr(pszUrlTmp, '#');
801 0 : char *pszRemoteStyleName = nullptr;
802 : // Chop off the stuff (style id) after the URL
803 0 : if (pszPound != nullptr)
804 : {
805 0 : *pszPound = '\0';
806 0 : pszRemoteStyleName = pszPound + 1;
807 : }
808 :
809 : /***** try it as a url then a file *****/
810 0 : VSILFILE *fp = nullptr;
811 0 : if ((fp =
812 0 : VSIFOpenL(CPLFormFilename("/vsicurl/", pszUrlTmp, nullptr),
813 0 : "r")) != nullptr ||
814 0 : (fp = VSIFOpenL(pszUrlTmp, "r")) != nullptr)
815 : {
816 0 : char szbuf[1025] = {};
817 0 : std::string oStyle = "";
818 :
819 : /***** loop, read and copy to a string *****/
820 0 : do
821 : {
822 : const size_t nRead =
823 0 : VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
824 0 : if (nRead == 0)
825 0 : break;
826 :
827 : /***** copy buf to the string *****/
828 0 : szbuf[nRead] = '\0';
829 0 : oStyle.append(szbuf);
830 0 : } while (!VSIFEofL(fp));
831 :
832 0 : VSIFCloseL(fp);
833 :
834 : /***** parse the kml into the dom *****/
835 0 : std::string oKmlErrors;
836 0 : ElementPtr poKmlRoot = kmldom::Parse(oStyle, &oKmlErrors);
837 :
838 0 : if (!poKmlRoot)
839 : {
840 0 : CPLError(CE_Warning, CPLE_OpenFailed,
841 : "ERROR parsing style kml %s :%s", pszUrlTmp,
842 : oKmlErrors.c_str());
843 0 : CPLFree(pszUrlTmp);
844 0 : CPLFree(pszUrl);
845 0 : CPLFree(pszStyleMapId);
846 :
847 0 : return nullptr;
848 : }
849 :
850 : /***** get the root container *****/
851 : kmldom::KmlFactory *poKmlFactory =
852 0 : kmldom::KmlFactory::GetFactory();
853 0 : ContainerPtr poKmlContainer;
854 0 : if (!(poKmlContainer = MyGetContainerFromRoot(
855 0 : poKmlFactory, std::move(poKmlRoot))))
856 : {
857 0 : CPLFree(pszUrlTmp);
858 0 : CPLFree(pszUrl);
859 0 : CPLFree(pszStyleMapId);
860 :
861 0 : return nullptr;
862 : }
863 :
864 : /**** parse the styles into the table *****/
865 0 : ParseStyles(AsDocument(poKmlContainer), &poStyleTable);
866 :
867 : /***** look for the style we need to map to in the table *****/
868 0 : const char *pszTest = poStyleTable->Find(pszRemoteStyleName);
869 :
870 : /***** if found copy it to the table as a new style *****/
871 0 : if (pszTest)
872 0 : poStyleTable->AddStyle(pszStyleMapId, pszTest);
873 : }
874 0 : CPLFree(pszUrlTmp);
875 : }
876 : }
877 :
878 : /***** FIXME Add support here for relative links inside KML. *****/
879 11 : CPLFree(pszUrl);
880 11 : CPLFree(pszStyleMapId);
881 :
882 11 : return nullptr;
883 : }
884 :
885 12 : StyleSelectorPtr StyleFromStyleMap(const StyleMapPtr &poKmlStyleMap,
886 : OGRStyleTable *poStyleTable)
887 : {
888 : /***** check the config option to see if the *****/
889 : /***** user wants normal or highlighted mapping *****/
890 : const char *pszStyleMapKey =
891 12 : CPLGetConfigOption("LIBKML_STYLEMAP_KEY", "normal");
892 24 : const int nStyleMapKey = EQUAL(pszStyleMapKey, "highlight")
893 12 : ? STYLESTATE_HIGHLIGHT
894 : : STYLESTATE_NORMAL;
895 :
896 : /***** Loop through the stylemap pairs and look for the "normal" one *****/
897 13 : for (size_t i = 0; i < poKmlStyleMap->get_pair_array_size(); ++i)
898 : {
899 13 : PairPtr myPair = poKmlStyleMap->get_pair_array_at(i);
900 :
901 : /***** is it the right one of the pair? *****/
902 13 : if (myPair->get_key() == nStyleMapKey)
903 : {
904 12 : if (myPair->has_styleselector())
905 : return StyleFromStyleSelector(myPair->get_styleselector(),
906 1 : poStyleTable);
907 11 : else if (myPair->has_styleurl())
908 : return StyleFromStyleURL(poKmlStyleMap, myPair->get_styleurl(),
909 11 : poStyleTable);
910 : }
911 : }
912 :
913 0 : return nullptr;
914 : }
915 :
916 : /******************************************************************************
917 : Function to parse a style table out of a document.
918 : ******************************************************************************/
919 :
920 113 : void ParseStyles(DocumentPtr poKmlDocument, OGRStyleTable **poStyleTable)
921 : {
922 : /***** if document is null just bail now *****/
923 113 : if (!poKmlDocument)
924 2 : return;
925 :
926 : /***** loop over the Styles *****/
927 111 : const size_t nKmlStyles = poKmlDocument->get_styleselector_array_size();
928 :
929 : /***** Lets first build the style table. *****/
930 : /***** to begin this is just proper styles. *****/
931 267 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
932 : {
933 : StyleSelectorPtr poKmlStyle =
934 156 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
935 :
936 : /***** Everything that is not a style you skip *****/
937 156 : if (!poKmlStyle->IsA(kmldom::Type_Style))
938 12 : continue;
939 :
940 : /***** We need to check to see if this is the first style. if it *****/
941 : /***** is we will not have a style table and need to create one *****/
942 :
943 144 : if (!*poStyleTable)
944 22 : *poStyleTable = new OGRStyleTable();
945 :
946 144 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
947 : }
948 :
949 : /***** Now we have to loop back around and get the style maps. We *****/
950 : /***** have to do this a second time since the stylemap might matter *****/
951 : /***** and we are just looping reference styles that are farther *****/
952 : /***** down in the file. Order through the XML as it is parsed. *****/
953 :
954 267 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
955 : {
956 : StyleSelectorPtr poKmlStyle =
957 156 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
958 :
959 : /***** Everything that is not a stylemap you skip *****/
960 156 : if (!poKmlStyle->IsA(kmldom::Type_StyleMap))
961 144 : continue;
962 :
963 : /***** We need to check to see if this is the first style. if it *****/
964 : /***** is we will not have a style table and need to create one *****/
965 12 : if (!*poStyleTable)
966 0 : *poStyleTable = new OGRStyleTable();
967 :
968 : /***** copy the style the style map points to since *****/
969 :
970 12 : char *pszStyleMapId = CPLStrdup(poKmlStyle->get_id().c_str());
971 : poKmlStyle =
972 12 : StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyle), *poStyleTable);
973 12 : if (!poKmlStyle)
974 : {
975 11 : CPLFree(pszStyleMapId);
976 11 : continue;
977 : }
978 1 : char *pszStyleId = CPLStrdup(poKmlStyle->get_id().c_str());
979 :
980 1 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
981 :
982 : // Change the name of the new style in the style table
983 :
984 1 : const char *pszTest = (*poStyleTable)->Find(pszStyleId);
985 : // If we found the style we want in the style table we...
986 1 : if (pszTest)
987 : {
988 1 : (*poStyleTable)->AddStyle(pszStyleMapId, pszTest);
989 1 : (*poStyleTable)->RemoveStyle(pszStyleId);
990 : }
991 1 : CPLFree(pszStyleId);
992 1 : CPLFree(pszStyleMapId);
993 : }
994 : }
995 :
996 : /******************************************************************************
997 : Function to add a style table to a kml container.
998 : ******************************************************************************/
999 :
1000 3 : void styletable2kml(OGRStyleTable *poOgrStyleTable, KmlFactory *poKmlFactory,
1001 : ContainerPtr poKmlContainer, char **papszOptions)
1002 : {
1003 : /***** just return if the styletable is null *****/
1004 3 : if (!poOgrStyleTable)
1005 0 : return;
1006 :
1007 6 : std::set<CPLString> aoSetNormalStyles;
1008 6 : std::set<CPLString> aoSetHighlightStyles;
1009 3 : poOgrStyleTable->ResetStyleStringReading();
1010 :
1011 : // Collect styles that end with _normal or _highlight.
1012 11 : while (poOgrStyleTable->GetNextStyle() != nullptr)
1013 : {
1014 8 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
1015 :
1016 8 : if (strlen(pszStyleName) > strlen("_normal") &&
1017 4 : EQUAL(pszStyleName + strlen(pszStyleName) - strlen("_normal"),
1018 : "_normal"))
1019 : {
1020 2 : CPLString osName(pszStyleName);
1021 1 : osName.resize(strlen(pszStyleName) - strlen("_normal"));
1022 2 : aoSetNormalStyles.insert(osName);
1023 : }
1024 7 : else if (strlen(pszStyleName) > strlen("_highlight") &&
1025 3 : EQUAL(pszStyleName + strlen(pszStyleName) -
1026 : strlen("_highlight"),
1027 : "_highlight"))
1028 : {
1029 2 : CPLString osName(pszStyleName);
1030 1 : osName.resize(strlen(pszStyleName) - strlen("_highlight"));
1031 1 : aoSetHighlightStyles.insert(osName);
1032 : }
1033 : }
1034 :
1035 : /***** parse the style table *****/
1036 3 : poOgrStyleTable->ResetStyleStringReading();
1037 :
1038 3 : const char *pszStyleString = nullptr;
1039 11 : while ((pszStyleString = poOgrStyleTable->GetNextStyle()) != nullptr)
1040 : {
1041 8 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
1042 :
1043 16 : if (aoSetNormalStyles.find(pszStyleName) != aoSetNormalStyles.end() &&
1044 8 : aoSetHighlightStyles.find(pszStyleName) !=
1045 8 : aoSetHighlightStyles.end())
1046 : {
1047 0 : continue;
1048 : }
1049 :
1050 : /***** add the style header to the kml *****/
1051 16 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1052 :
1053 8 : poKmlStyle->set_id(pszStyleName);
1054 :
1055 : /***** parse the style string *****/
1056 8 : addstylestring2kml(pszStyleString, poKmlStyle, poKmlFactory, nullptr);
1057 :
1058 : /***** add balloon style *****/
1059 8 : const char *pszBalloonStyleBgColor = CSLFetchNameValue(
1060 : papszOptions, CPLSPrintf("%s_balloonstyle_bgcolor", pszStyleName));
1061 8 : const char *pszBalloonStyleText = CSLFetchNameValue(
1062 : papszOptions, CPLSPrintf("%s_balloonstyle_text", pszStyleName));
1063 8 : int nR = 0;
1064 8 : int nG = 0;
1065 8 : int nB = 0;
1066 8 : int nA = 0;
1067 16 : OGRStylePen oStyleTool;
1068 9 : if ((pszBalloonStyleBgColor != nullptr &&
1069 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1070 9 : nA)) ||
1071 : pszBalloonStyleText != nullptr)
1072 : {
1073 : const BalloonStylePtr poKmlBalloonStyle =
1074 2 : poKmlFactory->CreateBalloonStyle();
1075 2 : if (pszBalloonStyleBgColor != nullptr &&
1076 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1077 : nA))
1078 2 : poKmlBalloonStyle->set_bgcolor(
1079 2 : Color32(static_cast<GByte>(nA), static_cast<GByte>(nB),
1080 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
1081 1 : if (pszBalloonStyleText != nullptr)
1082 1 : poKmlBalloonStyle->set_text(pszBalloonStyleText);
1083 1 : poKmlStyle->set_balloonstyle(poKmlBalloonStyle);
1084 : }
1085 :
1086 : /***** add the style to the container *****/
1087 8 : const DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1088 8 : poKmlDocument->add_styleselector(poKmlStyle);
1089 : }
1090 :
1091 : // Find style name that end with _normal and _highlight to create
1092 : // a StyleMap from both.
1093 : std::set<CPLString>::iterator aoSetNormalStylesIter =
1094 3 : aoSetNormalStyles.begin();
1095 4 : for (; aoSetNormalStylesIter != aoSetNormalStyles.end();
1096 1 : ++aoSetNormalStylesIter)
1097 : {
1098 2 : CPLString osStyleName(*aoSetNormalStylesIter);
1099 1 : if (aoSetHighlightStyles.find(osStyleName) !=
1100 2 : aoSetHighlightStyles.end())
1101 : {
1102 2 : StyleMapPtr poKmlStyleMap = poKmlFactory->CreateStyleMap();
1103 1 : poKmlStyleMap->set_id(osStyleName);
1104 :
1105 2 : PairPtr poKmlPairNormal = poKmlFactory->CreatePair();
1106 1 : poKmlPairNormal->set_key(STYLESTATE_NORMAL);
1107 1 : poKmlPairNormal->set_styleurl(
1108 : CPLSPrintf("#%s_normal", osStyleName.c_str()));
1109 1 : poKmlStyleMap->add_pair(poKmlPairNormal);
1110 :
1111 2 : PairPtr poKmlPairHighlight = poKmlFactory->CreatePair();
1112 1 : poKmlPairHighlight->set_key(STYLESTATE_HIGHLIGHT);
1113 1 : poKmlPairHighlight->set_styleurl(
1114 : CPLSPrintf("#%s_highlight", osStyleName.c_str()));
1115 1 : poKmlStyleMap->add_pair(poKmlPairHighlight);
1116 :
1117 : /***** add the style to the container *****/
1118 1 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1119 1 : poKmlDocument->add_styleselector(poKmlStyleMap);
1120 : }
1121 : }
1122 : }
1123 :
1124 : /******************************************************************************
1125 : Function to add a ListStyle and select it to a container.
1126 : ******************************************************************************/
1127 :
1128 131 : void createkmlliststyle(KmlFactory *poKmlFactory, const char *pszBaseName,
1129 : ContainerPtr poKmlLayerContainer,
1130 : DocumentPtr poKmlDocument,
1131 : const CPLString &osListStyleType,
1132 : const CPLString &osListStyleIconHref)
1133 : {
1134 131 : if (!osListStyleType.empty() || !osListStyleIconHref.empty())
1135 : {
1136 16 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1137 :
1138 8 : const char *pszStyleName = CPLSPrintf(
1139 16 : "%s_liststyle", OGRLIBKMLGetSanitizedNCName(pszBaseName).c_str());
1140 8 : poKmlStyle->set_id(pszStyleName);
1141 :
1142 8 : ListStylePtr poKmlListStyle = poKmlFactory->CreateListStyle();
1143 8 : poKmlStyle->set_liststyle(poKmlListStyle);
1144 8 : if (!osListStyleType.empty())
1145 : {
1146 5 : if (EQUAL(osListStyleType, "check"))
1147 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1148 4 : else if (EQUAL(osListStyleType, "radioFolder"))
1149 1 : poKmlListStyle->set_listitemtype(
1150 : kmldom::LISTITEMTYPE_RADIOFOLDER);
1151 3 : else if (EQUAL(osListStyleType, "checkOffOnly"))
1152 1 : poKmlListStyle->set_listitemtype(
1153 : kmldom::LISTITEMTYPE_CHECKOFFONLY);
1154 2 : else if (EQUAL(osListStyleType, "checkHideChildren"))
1155 1 : poKmlListStyle->set_listitemtype(
1156 : kmldom::LISTITEMTYPE_CHECKHIDECHILDREN);
1157 : else
1158 : {
1159 1 : CPLError(CE_Warning, CPLE_AppDefined,
1160 : "Invalid value for list style type: %s. "
1161 : "Defaulting to Check",
1162 : osListStyleType.c_str());
1163 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1164 : }
1165 : }
1166 :
1167 8 : if (!osListStyleIconHref.empty())
1168 : {
1169 6 : ItemIconPtr poItemIcon = poKmlFactory->CreateItemIcon();
1170 3 : poItemIcon->set_href(osListStyleIconHref.c_str());
1171 3 : poKmlListStyle->add_itemicon(poItemIcon);
1172 : }
1173 :
1174 8 : poKmlDocument->add_styleselector(poKmlStyle);
1175 8 : poKmlLayerContainer->set_styleurl(CPLSPrintf("#%s", pszStyleName));
1176 : }
1177 131 : }
|