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