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) 2010-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 <ogrsf_frmts.h>
33 #include <ogr_featurestyle.h>
34 #include <string>
35 #include "ogrlibkmlfeaturestyle.h"
36 #include "ogrlibkmlstyle.h"
37 
38 CPL_CVSID("$Id: ogrlibkmlfeaturestyle.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
39 
40 using kmldom::FeaturePtr;
41 using kmldom::IconStylePtr;
42 using kmldom::KmlFactory;
43 using kmldom::LabelStylePtr;
44 using kmldom::LineStylePtr;
45 using kmldom::PolyStylePtr;
46 using kmldom::StyleMapPtr;
47 using kmldom::StylePtr;
48 using kmldom::StyleSelectorPtr;
49 
50 /******************************************************************************
51  function to write out a features style to kml
52 
53 Args:
54             poOgrLayer      the layer the feature is in
55             poOgrFeat       the feature
56             poKmlFactory    the kml dom factory
57             poKmlFeature    the placemark to add it to
58 
59 Returns:
60             nothing
61 ******************************************************************************/
62 
featurestyle2kml(OGRLIBKMLDataSource * poOgrDS,OGRLayer * poOgrLayer,OGRFeature * poOgrFeat,KmlFactory * poKmlFactory,FeaturePtr poKmlFeature)63 void featurestyle2kml(
64     OGRLIBKMLDataSource * poOgrDS,
65     OGRLayer * poOgrLayer,
66     OGRFeature * poOgrFeat,
67     KmlFactory * poKmlFactory,
68     FeaturePtr poKmlFeature )
69 {
70     /***** get the style table *****/
71     OGRStyleTable *poOgrSTBL = nullptr;
72 
73     const char *pszStyleString = poOgrFeat->GetStyleString();
74 
75     /***** does the feature have style? *****/
76     if( pszStyleString && pszStyleString[0] != '\0' )
77     {
78         /***** does it ref a style table? *****/
79         if( *pszStyleString == '@' )
80         {
81             const char* pszStyleName = pszStyleString + 1;
82 
83             /***** Is the name in the layer style table *****/
84             OGRStyleTable *hSTBLLayer = poOgrLayer->GetStyleTable();
85             const char *pszTest = (hSTBLLayer != nullptr) ?
86                 hSTBLLayer->Find( pszStyleName ) : nullptr;
87 
88             if( pszTest )
89             {
90                 string oTmp = "#";
91                 oTmp.append( pszStyleName );
92                 poKmlFeature->set_styleurl( oTmp );
93             }
94             /***** assume its a dataset style,
95                    maybe the user will add it later *****/
96             else
97             {
98                 string oTmp;
99 
100                 if( poOgrDS->GetStylePath() )
101                     oTmp.append( poOgrDS->GetStylePath() );
102                 oTmp.append( "#" );
103                 oTmp.append( pszStyleName );
104 
105                 poKmlFeature->set_styleurl( oTmp );
106             }
107         }
108         /***** no style table ref *****/
109         else
110         {
111             /***** parse the style string *****/
112             const StylePtr poKmlStyle =
113                 addstylestring2kml( pszStyleString, nullptr, poKmlFactory,
114                                     poKmlFeature );
115 
116             /***** add the style to the placemark *****/
117             if( poKmlStyle )
118                 poKmlFeature->set_styleselector( poKmlStyle );
119         }
120     }
121     /***** get the style table *****/
122     else if( ( poOgrSTBL = poOgrFeat->GetStyleTable() ) != nullptr )
123     {
124         /***** parse the style table *****/
125         poOgrSTBL->ResetStyleStringReading();
126 
127         while( ( pszStyleString = poOgrSTBL->GetNextStyle() ) != nullptr )
128         {
129             if( *pszStyleString == '@' )
130             {
131                 const char* pszStyleName = pszStyleString + 1;
132 
133                 /***** is the name in the layer style table *****/
134                 OGRStyleTable *poOgrSTBLLayer = nullptr;
135 
136                 if( ( poOgrSTBLLayer = poOgrLayer->GetStyleTable() )
137                     != nullptr )
138                 {
139                     poOgrSTBLLayer->Find( pszStyleName );
140                 }
141 
142                 /***** Assume its a dataset style,       *****/
143                 /***** mayby the user will add it later. *****/
144 
145                 string oTmp;
146                 if( poOgrDS->GetStylePath() )
147                     oTmp.append( poOgrDS->GetStylePath() );
148                 oTmp.append( "#" );
149                 oTmp.append( pszStyleName );
150 
151                 poKmlFeature->set_styleurl( oTmp );
152             }
153             else
154             {
155                 /***** parse the style string *****/
156                 const StylePtr poKmlStyle =
157                     addstylestring2kml( pszStyleString, nullptr,
158                                      poKmlFactory, poKmlFeature );
159                 if( poKmlStyle )
160                 {
161                     /***** Add the style to the placemark. *****/
162                     poKmlFeature->set_styleselector( poKmlStyle );
163                 }
164             }
165         }
166     }
167 }
168 
169 /******************************************************************************
170  function to read a kml style into ogr's featurestyle
171 ******************************************************************************/
172 
kml2featurestyle(FeaturePtr poKmlFeature,OGRLIBKMLDataSource * poOgrDS,OGRLayer * poOgrLayer,OGRFeature * poOgrFeat)173 void kml2featurestyle(
174     FeaturePtr poKmlFeature,
175     OGRLIBKMLDataSource * poOgrDS,
176     OGRLayer * poOgrLayer,
177     OGRFeature * poOgrFeat )
178 {
179     /***** does the placemark have a style url? *****/
180     if( poKmlFeature->has_styleurl() )
181     {
182         const string poKmlStyleUrl = poKmlFeature->get_styleurl();
183 
184         /***** is the name in the layer style table *****/
185         char *pszUrl = CPLStrdup( poKmlStyleUrl.c_str() );
186 
187         OGRStyleTable *poOgrSTBLLayer = nullptr;
188         const char *pszTest = nullptr;
189 
190         /***** is it a layer style ? *****/
191         if( *pszUrl == '#' &&
192             ( poOgrSTBLLayer = poOgrLayer->GetStyleTable() ) != nullptr )
193         {
194              pszTest = poOgrSTBLLayer->Find( pszUrl + 1 );
195         }
196 
197         if( pszTest )
198         {
199             /***** should we resolve the style *****/
200             const char *pszResolve =
201                 CPLGetConfigOption( "LIBKML_RESOLVE_STYLE", "no" );
202 
203             if( CPLTestBool(pszResolve) )
204             {
205                 poOgrFeat->SetStyleString( pszTest );
206             }
207             else
208             {
209                 *pszUrl = '@';
210                 poOgrFeat->SetStyleString( pszUrl );
211             }
212         }
213         /***** is it a dataset style? *****/
214         else
215         {
216             const int nPathLen =
217                 static_cast<int>(strlen( poOgrDS->GetStylePath() ));
218 
219             if( nPathLen == 0 ||
220                 EQUALN( pszUrl, poOgrDS->GetStylePath(), nPathLen ) )
221             {
222                 /***** should we resolve the style *****/
223                 const char *pszResolve =
224                     CPLGetConfigOption( "LIBKML_RESOLVE_STYLE", "no" );
225 
226                 if( CPLTestBool(pszResolve) &&
227                     ( poOgrSTBLLayer = poOgrDS->GetStyleTable() ) != nullptr &&
228                     ( pszTest =
229                           poOgrSTBLLayer->Find(pszUrl + nPathLen + 1) ) != nullptr
230                     )
231                 {
232                     poOgrFeat->SetStyleString( pszTest );
233                 }
234                 else
235                 {
236                     pszUrl[nPathLen] = '@';
237                     poOgrFeat->SetStyleString( pszUrl + nPathLen );
238                 }
239             }
240             /**** its someplace else *****/
241             else
242             {
243                 const char *pszFetch =
244                     CPLGetConfigOption( "LIBKML_EXTERNAL_STYLE", "no" );
245 
246                 if( CPLTestBool(pszFetch) )
247                 {
248                     /***** load up the style table *****/
249                     char *pszUrlTmp = CPLStrdup(pszUrl);
250                     char *pszPound = strchr(pszUrlTmp, '#');
251                     if( pszPound != nullptr )
252                     {
253                         *pszPound = '\0';
254                     }
255 
256                     /***** try it as a url then a file *****/
257                     VSILFILE *fp = nullptr;
258                     if( (fp = VSIFOpenL(
259                              CPLFormFilename( "/vsicurl/", pszUrlTmp, nullptr ),
260                               "r" )) != nullptr ||
261                         (fp = VSIFOpenL( pszUrlTmp, "r" )) != nullptr )
262                     {
263                         char szbuf[1025] = { '\0' };
264                         std::string oStyle = "";
265 
266                         /***** loop, read and copy to a string *****/
267                         do {
268                             const size_t nRead =
269                                 VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
270 
271                             if( nRead == 0 )
272                                 break;
273 
274                             /***** copy buf to the string *****/
275 
276                             szbuf[nRead] = '\0';
277                             oStyle.append( szbuf );
278                         } while( !VSIFEofL(fp) );
279 
280                         VSIFCloseL(fp);
281 
282                         /***** parse the kml into the ds style table *****/
283 
284                         if( poOgrDS->ParseIntoStyleTable(&oStyle, pszUrlTmp) )
285                         {
286                             kml2featurestyle( poKmlFeature,
287                                               poOgrDS,
288                                               poOgrLayer,
289                                               poOgrFeat );
290                         }
291                         else
292                         {
293                             /***** if failed just store the url *****/
294                             poOgrFeat->SetStyleString( pszUrl );
295                         }
296                     }
297                     CPLFree(pszUrlTmp);
298                 }
299                 else
300                 {
301                     poOgrFeat->SetStyleString( pszUrl );
302                 }
303             }
304         }
305         CPLFree( pszUrl );
306     }
307 
308     /***** does the placemark have a style selector *****/
309     if( poKmlFeature->has_styleselector() )
310     {
311         StyleSelectorPtr poKmlStyleSelector =
312             poKmlFeature->get_styleselector();
313 
314         /***** is the style a style? *****/
315         if( poKmlStyleSelector->IsA( kmldom::Type_Style ) )
316         {
317             StylePtr poKmlStyle = AsStyle( poKmlStyleSelector );
318 
319             OGRStyleMgr *poOgrSM = new OGRStyleMgr;
320 
321             /***** if were resolving style the feature *****/
322             /***** might already have styling to add too *****/
323             const char *pszResolve =
324                 CPLGetConfigOption( "LIBKML_RESOLVE_STYLE", "no" );
325             if( CPLTestBool(pszResolve) ) {
326                  poOgrSM->InitFromFeature( poOgrFeat );
327             }
328             else
329             {
330                 /***** if featyurestyle gets a name tool this needs
331                        changed to the above *****/
332                 poOgrSM->InitStyleString( nullptr );
333             }
334 
335             /***** read the style *****/
336             kml2stylestring( poKmlStyle, poOgrSM );
337 
338             /***** add the style to the feature *****/
339             poOgrFeat->SetStyleString(poOgrSM->GetStyleString(nullptr));
340 
341             delete poOgrSM;
342         }
343         /***** is the style a stylemap? *****/
344         else if( poKmlStyleSelector->IsA( kmldom::Type_StyleMap ) )
345         {
346             // TODO: Need to figure out what to do with a style map.
347         }
348     }
349 }
350