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