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