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 =
796 0 : VSIFOpenL(CPLFormFilename("/vsicurl/", pszUrlTmp, nullptr),
797 0 : "r")) != nullptr ||
798 0 : (fp = VSIFOpenL(pszUrlTmp, "r")) != nullptr)
799 : {
800 0 : char szbuf[1025] = {};
801 0 : std::string oStyle = "";
802 :
803 : /***** loop, read and copy to a string *****/
804 0 : do
805 : {
806 : const size_t nRead =
807 0 : VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
808 0 : if (nRead == 0)
809 0 : break;
810 :
811 : /***** copy buf to the string *****/
812 0 : szbuf[nRead] = '\0';
813 0 : oStyle.append(szbuf);
814 0 : } while (!VSIFEofL(fp) && !VSIFErrorL(fp));
815 :
816 0 : VSIFCloseL(fp);
817 :
818 : /***** parse the kml into the dom *****/
819 0 : std::string oKmlErrors;
820 0 : ElementPtr poKmlRoot = kmldom::Parse(oStyle, &oKmlErrors);
821 :
822 0 : if (!poKmlRoot)
823 : {
824 0 : CPLError(CE_Warning, CPLE_OpenFailed,
825 : "ERROR parsing style kml %s :%s", pszUrlTmp,
826 : oKmlErrors.c_str());
827 0 : CPLFree(pszUrlTmp);
828 0 : CPLFree(pszUrl);
829 0 : CPLFree(pszStyleMapId);
830 :
831 0 : return nullptr;
832 : }
833 :
834 : /***** get the root container *****/
835 : kmldom::KmlFactory *poKmlFactory =
836 0 : kmldom::KmlFactory::GetFactory();
837 0 : ContainerPtr poKmlContainer;
838 0 : if (!(poKmlContainer = MyGetContainerFromRoot(
839 0 : poKmlFactory, std::move(poKmlRoot))))
840 : {
841 0 : CPLFree(pszUrlTmp);
842 0 : CPLFree(pszUrl);
843 0 : CPLFree(pszStyleMapId);
844 :
845 0 : return nullptr;
846 : }
847 :
848 : /**** parse the styles into the table *****/
849 0 : ParseStyles(AsDocument(poKmlContainer), &poStyleTable);
850 :
851 : /***** look for the style we need to map to in the table *****/
852 0 : const char *pszTest = poStyleTable->Find(pszRemoteStyleName);
853 :
854 : /***** if found copy it to the table as a new style *****/
855 0 : if (pszTest)
856 0 : poStyleTable->AddStyle(pszStyleMapId, pszTest);
857 : }
858 0 : CPLFree(pszUrlTmp);
859 : }
860 : }
861 :
862 : /***** FIXME Add support here for relative links inside KML. *****/
863 15 : CPLFree(pszUrl);
864 15 : CPLFree(pszStyleMapId);
865 :
866 15 : return nullptr;
867 : }
868 :
869 16 : StyleSelectorPtr StyleFromStyleMap(const StyleMapPtr &poKmlStyleMap,
870 : OGRStyleTable *poStyleTable)
871 : {
872 : /***** check the config option to see if the *****/
873 : /***** user wants normal or highlighted mapping *****/
874 : const char *pszStyleMapKey =
875 16 : CPLGetConfigOption("LIBKML_STYLEMAP_KEY", "normal");
876 32 : const int nStyleMapKey = EQUAL(pszStyleMapKey, "highlight")
877 16 : ? STYLESTATE_HIGHLIGHT
878 : : STYLESTATE_NORMAL;
879 :
880 : /***** Loop through the stylemap pairs and look for the "normal" one *****/
881 17 : for (size_t i = 0; i < poKmlStyleMap->get_pair_array_size(); ++i)
882 : {
883 17 : PairPtr myPair = poKmlStyleMap->get_pair_array_at(i);
884 :
885 : /***** is it the right one of the pair? *****/
886 17 : if (myPair->get_key() == nStyleMapKey)
887 : {
888 16 : if (myPair->has_styleselector())
889 : return StyleFromStyleSelector(myPair->get_styleselector(),
890 1 : poStyleTable);
891 15 : else if (myPair->has_styleurl())
892 : return StyleFromStyleURL(poKmlStyleMap, myPair->get_styleurl(),
893 15 : poStyleTable);
894 : }
895 : }
896 :
897 0 : return nullptr;
898 : }
899 :
900 : /******************************************************************************
901 : Function to parse a style table out of a document.
902 : ******************************************************************************/
903 :
904 209 : void ParseStyles(DocumentPtr poKmlDocument, OGRStyleTable **poStyleTable)
905 : {
906 : /***** if document is null just bail now *****/
907 209 : if (!poKmlDocument)
908 2 : return;
909 :
910 : /***** loop over the Styles *****/
911 207 : const size_t nKmlStyles = poKmlDocument->get_styleselector_array_size();
912 :
913 : /***** Lets first build the style table. *****/
914 : /***** to begin this is just proper styles. *****/
915 377 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
916 : {
917 : StyleSelectorPtr poKmlStyle =
918 170 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
919 :
920 : /***** Everything that is not a style you skip *****/
921 170 : if (!poKmlStyle->IsA(kmldom::Type_Style))
922 16 : continue;
923 :
924 : /***** We need to check to see if this is the first style. if it *****/
925 : /***** is we will not have a style table and need to create one *****/
926 :
927 154 : if (!*poStyleTable)
928 28 : *poStyleTable = new OGRStyleTable();
929 :
930 154 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
931 : }
932 :
933 : /***** Now we have to loop back around and get the style maps. We *****/
934 : /***** have to do this a second time since the stylemap might matter *****/
935 : /***** and we are just looping reference styles that are farther *****/
936 : /***** down in the file. Order through the XML as it is parsed. *****/
937 :
938 377 : for (size_t iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++)
939 : {
940 : StyleSelectorPtr poKmlStyle =
941 170 : poKmlDocument->get_styleselector_array_at(iKmlStyle);
942 :
943 : /***** Everything that is not a stylemap you skip *****/
944 170 : if (!poKmlStyle->IsA(kmldom::Type_StyleMap))
945 154 : continue;
946 :
947 : /***** We need to check to see if this is the first style. if it *****/
948 : /***** is we will not have a style table and need to create one *****/
949 16 : if (!*poStyleTable)
950 0 : *poStyleTable = new OGRStyleTable();
951 :
952 : /***** copy the style the style map points to since *****/
953 :
954 16 : char *pszStyleMapId = CPLStrdup(poKmlStyle->get_id().c_str());
955 : poKmlStyle =
956 16 : StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyle), *poStyleTable);
957 16 : if (!poKmlStyle)
958 : {
959 15 : CPLFree(pszStyleMapId);
960 15 : continue;
961 : }
962 1 : char *pszStyleId = CPLStrdup(poKmlStyle->get_id().c_str());
963 :
964 1 : kml2styletable(*poStyleTable, AsStyle(AsElement(poKmlStyle)));
965 :
966 : // Change the name of the new style in the style table
967 :
968 1 : const char *pszTest = (*poStyleTable)->Find(pszStyleId);
969 : // If we found the style we want in the style table we...
970 1 : if (pszTest)
971 : {
972 1 : (*poStyleTable)->AddStyle(pszStyleMapId, pszTest);
973 1 : (*poStyleTable)->RemoveStyle(pszStyleId);
974 : }
975 1 : CPLFree(pszStyleId);
976 1 : CPLFree(pszStyleMapId);
977 : }
978 : }
979 :
980 : /******************************************************************************
981 : Function to add a style table to a kml container.
982 : ******************************************************************************/
983 :
984 6 : void styletable2kml(OGRStyleTable *poOgrStyleTable, KmlFactory *poKmlFactory,
985 : ContainerPtr poKmlContainer, char **papszOptions)
986 : {
987 : /***** just return if the styletable is null *****/
988 6 : if (!poOgrStyleTable)
989 2 : return;
990 :
991 8 : std::set<CPLString> aoSetNormalStyles;
992 8 : std::set<CPLString> aoSetHighlightStyles;
993 4 : poOgrStyleTable->ResetStyleStringReading();
994 :
995 : // Collect styles that end with _normal or _highlight.
996 14 : while (poOgrStyleTable->GetNextStyle() != nullptr)
997 : {
998 10 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
999 :
1000 10 : if (strlen(pszStyleName) > strlen("_normal") &&
1001 6 : EQUAL(pszStyleName + strlen(pszStyleName) - strlen("_normal"),
1002 : "_normal"))
1003 : {
1004 4 : CPLString osName(pszStyleName);
1005 2 : osName.resize(strlen(pszStyleName) - strlen("_normal"));
1006 4 : aoSetNormalStyles.insert(osName);
1007 : }
1008 8 : else if (strlen(pszStyleName) > strlen("_highlight") &&
1009 4 : EQUAL(pszStyleName + strlen(pszStyleName) -
1010 : strlen("_highlight"),
1011 : "_highlight"))
1012 : {
1013 4 : CPLString osName(pszStyleName);
1014 2 : osName.resize(strlen(pszStyleName) - strlen("_highlight"));
1015 2 : aoSetHighlightStyles.insert(osName);
1016 : }
1017 : }
1018 :
1019 : /***** parse the style table *****/
1020 4 : poOgrStyleTable->ResetStyleStringReading();
1021 :
1022 4 : const char *pszStyleString = nullptr;
1023 14 : while ((pszStyleString = poOgrStyleTable->GetNextStyle()) != nullptr)
1024 : {
1025 10 : const char *pszStyleName = poOgrStyleTable->GetLastStyleName();
1026 :
1027 20 : if (aoSetNormalStyles.find(pszStyleName) != aoSetNormalStyles.end() &&
1028 10 : aoSetHighlightStyles.find(pszStyleName) !=
1029 10 : aoSetHighlightStyles.end())
1030 : {
1031 0 : continue;
1032 : }
1033 :
1034 : /***** add the style header to the kml *****/
1035 20 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1036 :
1037 10 : poKmlStyle->set_id(pszStyleName);
1038 :
1039 : /***** parse the style string *****/
1040 10 : addstylestring2kml(pszStyleString, poKmlStyle, poKmlFactory, nullptr);
1041 :
1042 : /***** add balloon style *****/
1043 10 : const char *pszBalloonStyleBgColor = CSLFetchNameValue(
1044 : papszOptions, CPLSPrintf("%s_balloonstyle_bgcolor", pszStyleName));
1045 10 : const char *pszBalloonStyleText = CSLFetchNameValue(
1046 : papszOptions, CPLSPrintf("%s_balloonstyle_text", pszStyleName));
1047 10 : int nR = 0;
1048 10 : int nG = 0;
1049 10 : int nB = 0;
1050 10 : int nA = 0;
1051 20 : OGRStylePen oStyleTool;
1052 11 : if ((pszBalloonStyleBgColor != nullptr &&
1053 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1054 11 : nA)) ||
1055 : pszBalloonStyleText != nullptr)
1056 : {
1057 : const BalloonStylePtr poKmlBalloonStyle =
1058 2 : poKmlFactory->CreateBalloonStyle();
1059 2 : if (pszBalloonStyleBgColor != nullptr &&
1060 1 : oStyleTool.GetRGBFromString(pszBalloonStyleBgColor, nR, nG, nB,
1061 : nA))
1062 2 : poKmlBalloonStyle->set_bgcolor(
1063 2 : Color32(static_cast<GByte>(nA), static_cast<GByte>(nB),
1064 : static_cast<GByte>(nG), static_cast<GByte>(nR)));
1065 1 : if (pszBalloonStyleText != nullptr)
1066 1 : poKmlBalloonStyle->set_text(pszBalloonStyleText);
1067 1 : poKmlStyle->set_balloonstyle(poKmlBalloonStyle);
1068 : }
1069 :
1070 : /***** add the style to the container *****/
1071 10 : const DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1072 10 : poKmlDocument->add_styleselector(poKmlStyle);
1073 : }
1074 :
1075 : // Find style name that end with _normal and _highlight to create
1076 : // a StyleMap from both.
1077 : std::set<CPLString>::iterator aoSetNormalStylesIter =
1078 4 : aoSetNormalStyles.begin();
1079 6 : for (; aoSetNormalStylesIter != aoSetNormalStyles.end();
1080 2 : ++aoSetNormalStylesIter)
1081 : {
1082 4 : CPLString osStyleName(*aoSetNormalStylesIter);
1083 2 : if (aoSetHighlightStyles.find(osStyleName) !=
1084 4 : aoSetHighlightStyles.end())
1085 : {
1086 4 : StyleMapPtr poKmlStyleMap = poKmlFactory->CreateStyleMap();
1087 2 : poKmlStyleMap->set_id(osStyleName);
1088 :
1089 4 : PairPtr poKmlPairNormal = poKmlFactory->CreatePair();
1090 2 : poKmlPairNormal->set_key(STYLESTATE_NORMAL);
1091 2 : poKmlPairNormal->set_styleurl(
1092 : CPLSPrintf("#%s_normal", osStyleName.c_str()));
1093 2 : poKmlStyleMap->add_pair(poKmlPairNormal);
1094 :
1095 4 : PairPtr poKmlPairHighlight = poKmlFactory->CreatePair();
1096 2 : poKmlPairHighlight->set_key(STYLESTATE_HIGHLIGHT);
1097 2 : poKmlPairHighlight->set_styleurl(
1098 : CPLSPrintf("#%s_highlight", osStyleName.c_str()));
1099 2 : poKmlStyleMap->add_pair(poKmlPairHighlight);
1100 :
1101 : /***** add the style to the container *****/
1102 2 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
1103 2 : poKmlDocument->add_styleselector(poKmlStyleMap);
1104 : }
1105 : }
1106 : }
1107 :
1108 : /******************************************************************************
1109 : Function to add a ListStyle and select it to a container.
1110 : ******************************************************************************/
1111 :
1112 212 : void createkmlliststyle(KmlFactory *poKmlFactory, const char *pszBaseName,
1113 : ContainerPtr poKmlLayerContainer,
1114 : DocumentPtr poKmlDocument,
1115 : const CPLString &osListStyleType,
1116 : const CPLString &osListStyleIconHref)
1117 : {
1118 212 : if (!osListStyleType.empty() || !osListStyleIconHref.empty())
1119 : {
1120 16 : StylePtr poKmlStyle = poKmlFactory->CreateStyle();
1121 :
1122 8 : const char *pszStyleName = CPLSPrintf(
1123 16 : "%s_liststyle", OGRLIBKMLGetSanitizedNCName(pszBaseName).c_str());
1124 8 : poKmlStyle->set_id(pszStyleName);
1125 :
1126 8 : ListStylePtr poKmlListStyle = poKmlFactory->CreateListStyle();
1127 8 : poKmlStyle->set_liststyle(poKmlListStyle);
1128 8 : if (!osListStyleType.empty())
1129 : {
1130 5 : if (EQUAL(osListStyleType, "check"))
1131 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1132 4 : else if (EQUAL(osListStyleType, "radioFolder"))
1133 1 : poKmlListStyle->set_listitemtype(
1134 : kmldom::LISTITEMTYPE_RADIOFOLDER);
1135 3 : else if (EQUAL(osListStyleType, "checkOffOnly"))
1136 1 : poKmlListStyle->set_listitemtype(
1137 : kmldom::LISTITEMTYPE_CHECKOFFONLY);
1138 2 : else if (EQUAL(osListStyleType, "checkHideChildren"))
1139 1 : poKmlListStyle->set_listitemtype(
1140 : kmldom::LISTITEMTYPE_CHECKHIDECHILDREN);
1141 : else
1142 : {
1143 1 : CPLError(CE_Warning, CPLE_AppDefined,
1144 : "Invalid value for list style type: %s. "
1145 : "Defaulting to Check",
1146 : osListStyleType.c_str());
1147 1 : poKmlListStyle->set_listitemtype(kmldom::LISTITEMTYPE_CHECK);
1148 : }
1149 : }
1150 :
1151 8 : if (!osListStyleIconHref.empty())
1152 : {
1153 6 : ItemIconPtr poItemIcon = poKmlFactory->CreateItemIcon();
1154 3 : poItemIcon->set_href(osListStyleIconHref.c_str());
1155 3 : poKmlListStyle->add_itemicon(poItemIcon);
1156 : }
1157 :
1158 8 : poKmlDocument->add_styleselector(poKmlStyle);
1159 8 : poKmlLayerContainer->set_styleurl(CPLSPrintf("#%s", pszStyleName));
1160 : }
1161 212 : }
|