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),
592 : cpl::down_cast<OGRStylePen *>(poOgrTmpST));
593 :
594 103 : poOgrNewSM->AddPart(poOgrStylePen);
595 :
596 103 : delete poOgrStylePen;
597 103 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
598 : }
599 :
600 : /***** polystyle / brush *****/
601 156 : if (poKmlStyle->has_polystyle())
602 : {
603 83 : poOgrNewSM->InitStyleString(nullptr);
604 :
605 166 : PolyStylePtr poKmlPolyStyle = poKmlStyle->get_polystyle();
606 :
607 83 : OGRStyleTool *poOgrTmpST = nullptr;
608 166 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
609 : {
610 83 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
611 :
612 83 : if (!poOgrST)
613 0 : continue;
614 :
615 83 : if (poOgrST->GetType() == OGRSTCBrush && poOgrTmpST == nullptr)
616 : {
617 0 : poOgrTmpST = poOgrST;
618 : }
619 : else
620 : {
621 83 : poOgrNewSM->AddPart(poOgrST);
622 83 : delete poOgrST;
623 : }
624 : }
625 :
626 : OGRStyleBrush *poOgrStyleBrush =
627 83 : kml2brush(std::move(poKmlPolyStyle),
628 : cpl::down_cast<OGRStyleBrush *>(poOgrTmpST));
629 :
630 83 : poOgrNewSM->AddPart(poOgrStyleBrush);
631 :
632 83 : delete poOgrStyleBrush;
633 83 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
634 : }
635 :
636 : /***** iconstyle / symbol *****/
637 156 : if (poKmlStyle->has_iconstyle())
638 : {
639 52 : poOgrNewSM->InitStyleString(nullptr);
640 :
641 104 : IconStylePtr poKmlIconStyle = poKmlStyle->get_iconstyle();
642 :
643 52 : OGRStyleTool *poOgrTmpST = nullptr;
644 64 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
645 : {
646 12 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
647 :
648 12 : if (!poOgrST)
649 0 : continue;
650 :
651 12 : if (poOgrST->GetType() == OGRSTCSymbol && poOgrTmpST == nullptr)
652 : {
653 0 : poOgrTmpST = poOgrST;
654 : }
655 : else
656 : {
657 12 : poOgrNewSM->AddPart(poOgrST);
658 12 : delete poOgrST;
659 : }
660 : }
661 :
662 : OGRStyleSymbol *poOgrStyleSymbol =
663 52 : kml2symbol(std::move(poKmlIconStyle),
664 : cpl::down_cast<OGRStyleSymbol *>(poOgrTmpST));
665 :
666 52 : poOgrNewSM->AddPart(poOgrStyleSymbol);
667 :
668 52 : delete poOgrStyleSymbol;
669 52 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
670 : }
671 :
672 : /***** labelstyle / label *****/
673 156 : if (poKmlStyle->has_labelstyle())
674 : {
675 5 : poOgrNewSM->InitStyleString(nullptr);
676 :
677 10 : LabelStylePtr poKmlLabelStyle = poKmlStyle->get_labelstyle();
678 :
679 5 : OGRStyleTool *poOgrTmpST = nullptr;
680 10 : for (int i = 0; i < poOgrSM->GetPartCount(nullptr); i++)
681 : {
682 5 : OGRStyleTool *poOgrST = poOgrSM->GetPart(i, nullptr);
683 :
684 5 : if (!poOgrST)
685 0 : continue;
686 :
687 5 : if (poOgrST->GetType() == OGRSTCLabel && poOgrTmpST == nullptr)
688 : {
689 0 : poOgrTmpST = poOgrST;
690 : }
691 : else
692 : {
693 5 : poOgrNewSM->AddPart(poOgrST);
694 5 : delete poOgrST;
695 : }
696 : }
697 :
698 : OGRStyleLabel *poOgrStyleLabel =
699 5 : kml2label(std::move(poKmlLabelStyle),
700 : cpl::down_cast<OGRStyleLabel *>(poOgrTmpST));
701 :
702 5 : poOgrNewSM->AddPart(poOgrStyleLabel);
703 :
704 5 : delete poOgrStyleLabel;
705 5 : poOgrSM->InitStyleString(poOgrNewSM->GetStyleString(nullptr));
706 : }
707 :
708 156 : delete poOgrNewSM;
709 156 : }
710 :
711 : /******************************************************************************
712 : Function to get the container from the kmlroot.
713 :
714 : Args: poKmlRoot the root element
715 :
716 : Returns: root if its a container, if its a kml the container it
717 : contains, or NULL
718 :
719 : ******************************************************************************/
720 :
721 0 : static ContainerPtr MyGetContainerFromRoot(KmlFactory *poKmlFactory,
722 : ElementPtr poKmlRoot)
723 : {
724 0 : ContainerPtr poKmlContainer = nullptr;
725 :
726 0 : if (poKmlRoot)
727 : {
728 : /***** skip over the <kml> we want the container *****/
729 0 : if (poKmlRoot->IsA(kmldom::Type_kml))
730 : {
731 0 : KmlPtr poKmlKml = AsKml(poKmlRoot);
732 :
733 0 : if (poKmlKml && poKmlKml->has_feature())
734 : {
735 0 : FeaturePtr poKmlFeat = poKmlKml->get_feature();
736 :
737 0 : if (poKmlFeat->IsA(kmldom::Type_Container))
738 : {
739 0 : poKmlContainer = AsContainer(poKmlFeat);
740 : }
741 0 : else if (poKmlFeat->IsA(kmldom::Type_Placemark))
742 : {
743 0 : poKmlContainer = poKmlFactory->CreateDocument();
744 0 : poKmlContainer->add_feature(
745 0 : kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
746 : }
747 : }
748 : }
749 0 : else if (poKmlRoot->IsA(kmldom::Type_Container))
750 : {
751 0 : poKmlContainer = AsContainer(std::move(poKmlRoot));
752 : }
753 : }
754 :
755 0 : return poKmlContainer;
756 : }
757 :
758 15 : static StyleSelectorPtr StyleFromStyleURL(const StyleMapPtr &stylemap,
759 : const string &styleurl,
760 : OGRStyleTable *poStyleTable)
761 : {
762 : // TODO:: Parse the styleURL.
763 15 : char *pszUrl = CPLStrdup(styleurl.c_str());
764 15 : char *pszStyleMapId = CPLStrdup(stylemap->get_id().c_str());
765 :
766 : /***** Is it an internal style ref that starts with a #? *****/
767 15 : if (*pszUrl == '#' && poStyleTable)
768 : {
769 : /***** Search the style table for the style we *****/
770 : /***** want and copy it back into the table. *****/
771 15 : const char *pszTest = poStyleTable->Find(pszUrl + 1);
772 15 : if (pszTest)
773 : {
774 15 : poStyleTable->AddStyle(pszStyleMapId, pszTest);
775 15 : }
776 : }
777 :
778 : /***** We have a real URL and need to go out and fetch it *****/
779 : /***** FIXME this could be a relative path in a kmz *****/
780 0 : else if (strchr(pszUrl, '#'))
781 : {
782 : const char *pszFetch =
783 0 : CPLGetConfigOption("LIBKML_EXTERNAL_STYLE", "no");
784 0 : if (CPLTestBool(pszFetch))
785 : {
786 : /***** Lets go out and fetch the style from the external URL *****/
787 0 : char *pszUrlTmp = CPLStrdup(pszUrl);
788 0 : char *pszPound = strchr(pszUrlTmp, '#');
789 0 : char *pszRemoteStyleName = nullptr;
790 : // Chop off the stuff (style id) after the URL
791 0 : if (pszPound != nullptr)
792 : {
793 0 : *pszPound = '\0';
794 0 : pszRemoteStyleName = pszPound + 1;
795 : }
796 :
797 : /***** try it as a url then a file *****/
798 0 : VSILFILE *fp = nullptr;
799 0 : if ((fp = VSIFOpenL(
800 0 : CPLFormFilenameSafe("/vsicurl/", pszUrlTmp, nullptr)
801 : .c_str(),
802 0 : "r")) != nullptr ||
803 0 : (fp = VSIFOpenL(pszUrlTmp, "r")) != nullptr)
804 : {
805 0 : char szbuf[1025] = {};
806 0 : std::string oStyle = "";
807 :
808 : /***** loop, read and copy to a string *****/
809 0 : do
810 : {
811 : const size_t nRead =
812 0 : VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
813 0 : if (nRead == 0)
814 0 : break;
815 :
816 : /***** copy buf to the string *****/
817 0 : szbuf[nRead] = '\0';
818 0 : oStyle.append(szbuf);
819 0 : } while (!VSIFEofL(fp) && !VSIFErrorL(fp));
820 :
821 0 : VSIFCloseL(fp);
822 :
823 : /***** parse the kml into the dom *****/
824 0 : std::string oKmlErrors;
825 0 : ElementPtr poKmlRoot = kmldom::Parse(oStyle, &oKmlErrors);
826 :
827 0 : if (!poKmlRoot)
828 : {
829 0 : CPLError(CE_Warning, CPLE_OpenFailed,
830 : "ERROR parsing style kml %s :%s", pszUrlTmp,
831 : oKmlErrors.c_str());
832 0 : CPLFree(pszUrlTmp);
833 0 : CPLFree(pszUrl);
834 0 : CPLFree(pszStyleMapId);
835 :
836 0 : return nullptr;
837 : }
838 :
839 : /***** get the root container *****/
840 : kmldom::KmlFactory *poKmlFactory =
841 0 : kmldom::KmlFactory::GetFactory();
842 0 : ContainerPtr poKmlContainer;
843 0 : if (!(poKmlContainer = MyGetContainerFromRoot(
844 0 : poKmlFactory, std::move(poKmlRoot))))
845 : {
846 0 : CPLFree(pszUrlTmp);
847 0 : CPLFree(pszUrl);
848 0 : CPLFree(pszStyleMapId);
849 :
850 0 : return nullptr;
851 : }
852 :
853 : /**** parse the styles into the table *****/
854 0 : ParseStyles(AsDocument(poKmlContainer), &poStyleTable);
855 :
856 : /***** look for the style we need to map to in the table *****/
857 0 : const char *pszTest = poStyleTable->Find(pszRemoteStyleName);
858 :
859 : /***** if found copy it to the table as a new style *****/
860 0 : if (pszTest)
861 0 : poStyleTable->AddStyle(pszStyleMapId, pszTest);
862 : }
863 0 : CPLFree(pszUrlTmp);
864 : }
865 : }
866 :
867 : /***** FIXME Add support here for relative links inside KML. *****/
868 15 : CPLFree(pszUrl);
869 15 : CPLFree(pszStyleMapId);
870 :
871 15 : return nullptr;
872 : }
873 :
874 16 : StyleSelectorPtr StyleFromStyleMap(const StyleMapPtr &poKmlStyleMap,
875 : OGRStyleTable *poStyleTable)
876 : {
877 : /***** check the config option to see if the *****/
878 : /***** user wants normal or highlighted mapping *****/
879 : const char *pszStyleMapKey =
880 16 : CPLGetConfigOption("LIBKML_STYLEMAP_KEY", "normal");
881 32 : const int nStyleMapKey = EQUAL(pszStyleMapKey, "highlight")
882 16 : ? STYLESTATE_HIGHLIGHT
883 : : STYLESTATE_NORMAL;
884 :
885 : /***** Loop through the stylemap pairs and look for the "normal" one *****/
886 17 : for (size_t i = 0; i < poKmlStyleMap->get_pair_array_size(); ++i)
887 : {
888 17 : PairPtr myPair = poKmlStyleMap->get_pair_array_at(i);
889 :
890 : /***** is it the right one of the pair? *****/
891 17 : if (myPair->get_key() == nStyleMapKey)
892 : {
893 16 : if (myPair->has_styleselector())
894 : return StyleFromStyleSelector(myPair->get_styleselector(),
895 1 : poStyleTable);
896 15 : else if (myPair->has_styleurl())
897 : return StyleFromStyleURL(poKmlStyleMap, myPair->get_styleurl(),
898 15 : poStyleTable);
899 : }
900 : }
901 :
902 0 : return nullptr;
903 : }
904 :
905 : /******************************************************************************
906 : Function to parse a style table out of a document.
907 : ******************************************************************************/
908 :
909 214 : void ParseStyles(DocumentPtr poKmlDocument, OGRStyleTable **poStyleTable)
910 : {
911 : /***** if document is null just bail now *****/
912 214 : if (!poKmlDocument)
913 2 : return;
914 :
915 : /***** loop over the Styles *****/
916 212 : const size_t nKmlStyles = poKmlDocument->get_styleselector_array_size();
917 :
918 : /***** Lets first build the style table. *****/
919 : /***** to begin this is just proper styles. *****/
920 382 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
921 : {
922 : StyleSelectorPtr poKmlStyle =
923 170 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
924 :
925 : /***** Everything that is not a style you skip *****/
926 170 : if (!poKmlStyle->IsA(kmldom::Type_Style))
927 16 : continue;
928 :
929 : /***** We need to check to see if this is the first style. if it *****/
930 : /***** is we will not have a style table and need to create one *****/
931 :
932 154 : if (!*poStyleTable)
933 28 : *poStyleTable = new OGRStyleTable();
934 :
935 154 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
936 : }
937 :
938 : /***** Now we have to loop back around and get the style maps. We *****/
939 : /***** have to do this a second time since the stylemap might matter *****/
940 : /***** and we are just looping reference styles that are farther *****/
941 : /***** down in the file. Order through the XML as it is parsed. *****/
942 :
943 382 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
944 : {
945 : StyleSelectorPtr poKmlStyle =
946 170 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
947 :
948 : /***** Everything that is not a stylemap you skip *****/
949 170 : if (!poKmlStyle->IsA(kmldom::Type_StyleMap))
950 154 : continue;
951 :
952 : /***** We need to check to see if this is the first style. if it *****/
953 : /***** is we will not have a style table and need to create one *****/
954 16 : if (!*poStyleTable)
955 0 : *poStyleTable = new OGRStyleTable();
956 :
957 : /***** copy the style the style map points to since *****/
958 :
959 16 : char *pszStyleMapId = CPLStrdup(poKmlStyle->get_id().c_str());
960 : poKmlStyle =
961 16 : StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyle), *poStyleTable);
962 16 : if (!poKmlStyle)
963 : {
964 15 : CPLFree(pszStyleMapId);
965 15 : continue;
966 : }
967 1 : char *pszStyleId = CPLStrdup(poKmlStyle->get_id().c_str());
968 :
969 1 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
970 :
971 : // Change the name of the new style in the style table
972 :
973 1 : const char *pszTest = (*poStyleTable)->Find(pszStyleId);
974 : // If we found the style we want in the style table we...
975 1 : if (pszTest)
976 : {
977 1 : (*poStyleTable)->AddStyle(pszStyleMapId, pszTest);
978 1 : (*poStyleTable)->RemoveStyle(pszStyleId);
979 : }
980 1 : CPLFree(pszStyleId);
981 1 : CPLFree(pszStyleMapId);
982 : }
983 : }
984 :
985 : /******************************************************************************
986 : Function to add a style table to a kml container.
987 : ******************************************************************************/
988 :
989 6 : void styletable2kml(OGRStyleTable *poOgrStyleTable, KmlFactory *poKmlFactory,
990 : ContainerPtr poKmlContainer, char **papszOptions)
991 : {
992 : /***** just return if the styletable is null *****/
993 6 : if (!poOgrStyleTable)
994 2 : return;
995 :
996 8 : std::set<CPLString> aoSetNormalStyles;
997 8 : std::set<CPLString> aoSetHighlightStyles;
998 4 : poOgrStyleTable->ResetStyleStringReading();
999 :
1000 : // Collect styles that end with _normal or _highlight.
1001 14 : while (poOgrStyleTable->GetNextStyle() != nullptr)
1002 : {
1003 10 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
1004 :
1005 10 : if (strlen(pszStyleName) > strlen("_normal") &&
1006 6 : EQUAL(pszStyleName + strlen(pszStyleName) - strlen("_normal"),
1007 : "_normal"))
1008 : {
1009 4 : CPLString osName(pszStyleName);
1010 2 : osName.resize(strlen(pszStyleName) - strlen("_normal"));
1011 4 : aoSetNormalStyles.insert(std::move(osName));
1012 : }
1013 8 : else if (strlen(pszStyleName) > strlen("_highlight") &&
1014 4 : EQUAL(pszStyleName + strlen(pszStyleName) -
1015 : strlen("_highlight"),
1016 : "_highlight"))
1017 : {
1018 4 : CPLString osName(pszStyleName);
1019 2 : osName.resize(strlen(pszStyleName) - strlen("_highlight"));
1020 2 : aoSetHighlightStyles.insert(std::move(osName));
1021 : }
1022 : }
1023 :
1024 : /***** parse the style table *****/
1025 4 : poOgrStyleTable->ResetStyleStringReading();
1026 :
1027 4 : const char *pszStyleString = nullptr;
1028 14 : while ((pszStyleString = poOgrStyleTable->GetNextStyle()) != nullptr)
1029 : {
1030 10 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
1031 :
1032 20 : if (aoSetNormalStyles.find(pszStyleName) != aoSetNormalStyles.end() &&
1033 10 : aoSetHighlightStyles.find(pszStyleName) !=
1034 10 : aoSetHighlightStyles.end())
1035 : {
1036 0 : continue;
1037 : }
1038 :
1039 : /***** add the style header to the kml *****/
1040 20 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1041 :
1042 10 : poKmlStyle->set_id(pszStyleName);
1043 :
1044 : /***** parse the style string *****/
1045 10 : addstylestring2kml(pszStyleString, poKmlStyle, poKmlFactory, nullptr);
1046 :
1047 : /***** add balloon style *****/
1048 10 : const char *pszBalloonStyleBgColor = CSLFetchNameValue(
1049 : papszOptions, CPLSPrintf("%s_balloonstyle_bgcolor", pszStyleName));
1050 10 : const char *pszBalloonStyleText = CSLFetchNameValue(
1051 : papszOptions, CPLSPrintf("%s_balloonstyle_text", pszStyleName));
1052 10 : int nR = 0;
1053 10 : int nG = 0;
1054 10 : int nB = 0;
1055 10 : int nA = 0;
1056 20 : OGRStylePen oStyleTool;
1057 11 : if ((pszBalloonStyleBgColor != nullptr &&
1058 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1059 11 : nA)) ||
1060 : pszBalloonStyleText != nullptr)
1061 : {
1062 : const BalloonStylePtr poKmlBalloonStyle =
1063 2 : poKmlFactory->CreateBalloonStyle();
1064 2 : if (pszBalloonStyleBgColor != nullptr &&
1065 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1066 : nA))
1067 2 : poKmlBalloonStyle->set_bgcolor(
1068 2 : Color32(static_cast<GByte>(nA), static_cast<GByte>(nB),
1069 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
1070 1 : if (pszBalloonStyleText != nullptr)
1071 1 : poKmlBalloonStyle->set_text(pszBalloonStyleText);
1072 1 : poKmlStyle->set_balloonstyle(poKmlBalloonStyle);
1073 : }
1074 :
1075 : /***** add the style to the container *****/
1076 10 : const DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1077 10 : poKmlDocument->add_styleselector(poKmlStyle);
1078 : }
1079 :
1080 : // Find style name that end with _normal and _highlight to create
1081 : // a StyleMap from both.
1082 : std::set<CPLString>::iterator aoSetNormalStylesIter =
1083 4 : aoSetNormalStyles.begin();
1084 6 : for (; aoSetNormalStylesIter != aoSetNormalStyles.end();
1085 2 : ++aoSetNormalStylesIter)
1086 : {
1087 4 : CPLString osStyleName(*aoSetNormalStylesIter);
1088 2 : if (aoSetHighlightStyles.find(osStyleName) !=
1089 4 : aoSetHighlightStyles.end())
1090 : {
1091 4 : StyleMapPtr poKmlStyleMap = poKmlFactory->CreateStyleMap();
1092 2 : poKmlStyleMap->set_id(osStyleName);
1093 :
1094 4 : PairPtr poKmlPairNormal = poKmlFactory->CreatePair();
1095 2 : poKmlPairNormal->set_key(STYLESTATE_NORMAL);
1096 2 : poKmlPairNormal->set_styleurl(
1097 : CPLSPrintf("#%s_normal", osStyleName.c_str()));
1098 2 : poKmlStyleMap->add_pair(poKmlPairNormal);
1099 :
1100 4 : PairPtr poKmlPairHighlight = poKmlFactory->CreatePair();
1101 2 : poKmlPairHighlight->set_key(STYLESTATE_HIGHLIGHT);
1102 2 : poKmlPairHighlight->set_styleurl(
1103 : CPLSPrintf("#%s_highlight", osStyleName.c_str()));
1104 2 : poKmlStyleMap->add_pair(poKmlPairHighlight);
1105 :
1106 : /***** add the style to the container *****/
1107 2 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1108 2 : poKmlDocument->add_styleselector(poKmlStyleMap);
1109 : }
1110 : }
1111 : }
1112 :
1113 : /******************************************************************************
1114 : Function to add a ListStyle and select it to a container.
1115 : ******************************************************************************/
1116 :
1117 216 : void createkmlliststyle(KmlFactory *poKmlFactory, const char *pszBaseName,
1118 : ContainerPtr poKmlLayerContainer,
1119 : DocumentPtr poKmlDocument,
1120 : const CPLString &osListStyleType,
1121 : const CPLString &osListStyleIconHref)
1122 : {
1123 216 : if (!osListStyleType.empty() || !osListStyleIconHref.empty())
1124 : {
1125 16 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1126 :
1127 8 : const char *pszStyleName = CPLSPrintf(
1128 16 : "%s_liststyle", OGRLIBKMLGetSanitizedNCName(pszBaseName).c_str());
1129 8 : poKmlStyle->set_id(pszStyleName);
1130 :
1131 8 : ListStylePtr poKmlListStyle = poKmlFactory->CreateListStyle();
1132 8 : poKmlStyle->set_liststyle(poKmlListStyle);
1133 8 : if (!osListStyleType.empty())
1134 : {
1135 5 : if (EQUAL(osListStyleType, "check"))
1136 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1137 4 : else if (EQUAL(osListStyleType, "radioFolder"))
1138 1 : poKmlListStyle->set_listitemtype(
1139 : kmldom::LISTITEMTYPE_RADIOFOLDER);
1140 3 : else if (EQUAL(osListStyleType, "checkOffOnly"))
1141 1 : poKmlListStyle->set_listitemtype(
1142 : kmldom::LISTITEMTYPE_CHECKOFFONLY);
1143 2 : else if (EQUAL(osListStyleType, "checkHideChildren"))
1144 1 : poKmlListStyle->set_listitemtype(
1145 : kmldom::LISTITEMTYPE_CHECKHIDECHILDREN);
1146 : else
1147 : {
1148 1 : CPLError(CE_Warning, CPLE_AppDefined,
1149 : "Invalid value for list style type: %s. "
1150 : "Defaulting to Check",
1151 : osListStyleType.c_str());
1152 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1153 : }
1154 : }
1155 :
1156 8 : if (!osListStyleIconHref.empty())
1157 : {
1158 6 : ItemIconPtr poItemIcon = poKmlFactory->CreateItemIcon();
1159 3 : poItemIcon->set_href(osListStyleIconHref.c_str());
1160 3 : poKmlListStyle->add_itemicon(poItemIcon);
1161 : }
1162 :
1163 8 : poKmlDocument->add_styleselector(poKmlStyle);
1164 8 : poKmlLayerContainer->set_styleurl(CPLSPrintf("#%s", pszStyleName));
1165 : }
1166 216 : }
|