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) 2010-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 "ogrsf_frmts.h"
17 : #include "ogr_featurestyle.h"
18 : #include <string>
19 : #include "ogrlibkmlfeaturestyle.h"
20 : #include "ogrlibkmlstyle.h"
21 :
22 : using kmldom::FeaturePtr;
23 : using kmldom::IconStylePtr;
24 : using kmldom::KmlFactory;
25 : using kmldom::LabelStylePtr;
26 : using kmldom::LineStylePtr;
27 : using kmldom::PolyStylePtr;
28 : using kmldom::StyleMapPtr;
29 : using kmldom::StylePtr;
30 : using kmldom::StyleSelectorPtr;
31 :
32 : /******************************************************************************
33 : function to write out a features style to kml
34 :
35 : Args:
36 : poOgrLayer the layer the feature is in
37 : poOgrFeat the feature
38 : poKmlFactory the kml dom factory
39 : poKmlFeature the placemark to add it to
40 :
41 : Returns:
42 : nothing
43 : ******************************************************************************/
44 :
45 202 : void featurestyle2kml(OGRLIBKMLDataSource *poOgrDS, OGRLayer *poOgrLayer,
46 : OGRFeature *poOgrFeat, KmlFactory *poKmlFactory,
47 : FeaturePtr poKmlFeature)
48 : {
49 : /***** get the style table *****/
50 202 : OGRStyleTable *poOgrSTBL = nullptr;
51 :
52 202 : const char *pszStyleString = poOgrFeat->GetStyleString();
53 :
54 : /***** does the feature have style? *****/
55 202 : if (pszStyleString && pszStyleString[0] != '\0')
56 : {
57 : /***** does it ref a style table? *****/
58 6 : if (*pszStyleString == '@')
59 : {
60 5 : const char *pszStyleName = pszStyleString + 1;
61 :
62 : /***** Is the name in the layer style table *****/
63 5 : OGRStyleTable *hSTBLLayer = poOgrLayer->GetStyleTable();
64 : const char *pszTest = (hSTBLLayer != nullptr)
65 5 : ? hSTBLLayer->Find(pszStyleName)
66 5 : : nullptr;
67 :
68 5 : if (pszTest)
69 : {
70 0 : string oTmp = "#";
71 0 : oTmp.append(pszStyleName);
72 0 : poKmlFeature->set_styleurl(oTmp);
73 : }
74 : /***** assume its a dataset style,
75 : maybe the user will add it later *****/
76 : else
77 : {
78 10 : string oTmp;
79 :
80 5 : if (!poOgrDS->GetStylePath().empty())
81 2 : oTmp.append(poOgrDS->GetStylePath());
82 5 : oTmp.append("#");
83 5 : oTmp.append(pszStyleName);
84 :
85 5 : poKmlFeature->set_styleurl(oTmp);
86 : }
87 : }
88 : /***** no style table ref *****/
89 : else
90 : {
91 : /***** parse the style string *****/
92 : const StylePtr poKmlStyle = addstylestring2kml(
93 3 : pszStyleString, nullptr, poKmlFactory, poKmlFeature);
94 :
95 : /***** add the style to the placemark *****/
96 1 : if (poKmlStyle)
97 1 : poKmlFeature->set_styleselector(poKmlStyle);
98 6 : }
99 : }
100 : /***** get the style table *****/
101 196 : else if ((poOgrSTBL = poOgrFeat->GetStyleTable()) != nullptr)
102 : {
103 : /***** parse the style table *****/
104 0 : poOgrSTBL->ResetStyleStringReading();
105 :
106 0 : while ((pszStyleString = poOgrSTBL->GetNextStyle()) != nullptr)
107 : {
108 0 : if (*pszStyleString == '@')
109 : {
110 0 : const char *pszStyleName = pszStyleString + 1;
111 :
112 : /***** is the name in the layer style table *****/
113 0 : OGRStyleTable *poOgrSTBLLayer = nullptr;
114 :
115 0 : if ((poOgrSTBLLayer = poOgrLayer->GetStyleTable()) != nullptr)
116 : {
117 0 : poOgrSTBLLayer->Find(pszStyleName);
118 : }
119 :
120 : /***** Assume its a dataset style, *****/
121 : /***** mayby the user will add it later. *****/
122 :
123 0 : string oTmp;
124 0 : if (!poOgrDS->GetStylePath().empty())
125 0 : oTmp.append(poOgrDS->GetStylePath());
126 0 : oTmp.append("#");
127 0 : oTmp.append(pszStyleName);
128 :
129 0 : poKmlFeature->set_styleurl(oTmp);
130 : }
131 : else
132 : {
133 : /***** parse the style string *****/
134 : const StylePtr poKmlStyle = addstylestring2kml(
135 0 : pszStyleString, nullptr, poKmlFactory, poKmlFeature);
136 0 : if (poKmlStyle)
137 : {
138 : /***** Add the style to the placemark. *****/
139 0 : poKmlFeature->set_styleselector(poKmlStyle);
140 : }
141 : }
142 : }
143 : }
144 202 : }
145 :
146 : /******************************************************************************
147 : function to read a kml style into ogr's featurestyle
148 : ******************************************************************************/
149 :
150 1251 : void kml2featurestyle(FeaturePtr poKmlFeature, OGRLIBKMLDataSource *poOgrDS,
151 : OGRLayer *poOgrLayer, OGRFeature *poOgrFeat)
152 : {
153 : /***** does the placemark have a style url? *****/
154 1251 : const int nStyleURLIterations = poKmlFeature->has_styleurl() ? 2 : 0;
155 1253 : for (int i = 0; i < nStyleURLIterations; ++i)
156 : {
157 : /***** is the name in the layer style table *****/
158 499 : std::string osUrl(poKmlFeature->get_styleurl());
159 :
160 : // Starting with GDAL 3.9.2, style URLs in KMZ files we generate start
161 : // with ../style/style.kml# to reflect the file hierarchy
162 : // Strip the leading ../ for correct resolution.
163 499 : constexpr const char *DOTDOT_URL = "../style/style.kml#";
164 576 : if (osUrl.size() > strlen(DOTDOT_URL) &&
165 77 : memcmp(osUrl.data(), DOTDOT_URL, strlen(DOTDOT_URL)) == 0)
166 : {
167 4 : osUrl = osUrl.substr(strlen("../"));
168 : }
169 :
170 499 : OGRStyleTable *poOgrSTBLLayer = nullptr;
171 499 : const char *pszTest = nullptr;
172 499 : bool bRetry = false;
173 :
174 499 : std::string osStyleString;
175 :
176 : /***** is it a layer style ? *****/
177 989 : if (!osUrl.empty() && osUrl.front() == '#' &&
178 490 : (poOgrSTBLLayer = poOgrLayer->GetStyleTable()) != nullptr)
179 : {
180 41 : pszTest = poOgrSTBLLayer->Find(osUrl.c_str() + 1);
181 : }
182 :
183 499 : if (pszTest)
184 : {
185 : /***** should we resolve the style *****/
186 : const char *pszResolve =
187 41 : CPLGetConfigOption("LIBKML_RESOLVE_STYLE", "no");
188 :
189 41 : if (CPLTestBool(pszResolve))
190 : {
191 0 : osStyleString = pszTest;
192 : }
193 : else
194 : {
195 41 : osStyleString = std::string("@").append(osUrl.c_str() + 1);
196 : }
197 : }
198 : /***** is it a dataset style? *****/
199 : else
200 : {
201 458 : const size_t nPathLen = poOgrDS->GetStylePath().size();
202 464 : if (osUrl.size() > nPathLen && osUrl[nPathLen] == '#' &&
203 6 : (nPathLen == 0 ||
204 6 : strncmp(osUrl.c_str(), poOgrDS->GetStylePath().c_str(),
205 : nPathLen) == 0))
206 : {
207 : /***** should we resolve the style *****/
208 : const char *pszResolve =
209 455 : CPLGetConfigOption("LIBKML_RESOLVE_STYLE", "no");
210 :
211 455 : if (CPLTestBool(pszResolve) &&
212 460 : (poOgrSTBLLayer = poOgrDS->GetStyleTable()) != nullptr &&
213 5 : (pszTest = poOgrSTBLLayer->Find(osUrl.c_str() + nPathLen +
214 : 1)) != nullptr)
215 : {
216 3 : osStyleString = pszTest;
217 : }
218 : else
219 : {
220 : osStyleString =
221 452 : std::string("@").append(osUrl.c_str() + nPathLen + 1);
222 : }
223 : }
224 :
225 : /**** its someplace else *****/
226 458 : if (i == 0 && osStyleString.empty())
227 : {
228 : const char *pszFetch =
229 3 : CPLGetConfigOption("LIBKML_EXTERNAL_STYLE", "no");
230 :
231 3 : if (CPLTestBool(pszFetch))
232 : {
233 : /***** load up the style table *****/
234 4 : std::string osUrlTmp(osUrl);
235 2 : const auto nPoundPos = osUrlTmp.find('#');
236 2 : if (nPoundPos != std::string::npos)
237 : {
238 2 : osUrlTmp.resize(nPoundPos);
239 : }
240 4 : const std::string osStyleFilename(osUrlTmp);
241 :
242 4 : if (STARTS_WITH(osUrlTmp.c_str(), "http://") ||
243 2 : STARTS_WITH(osUrlTmp.c_str(), "https://"))
244 : {
245 : osUrlTmp =
246 0 : std::string("/vsicurl_streaming/").append(osUrlTmp);
247 : }
248 2 : else if (CPLIsFilenameRelative(osUrlTmp.c_str()))
249 : {
250 : osUrlTmp = CPLFormFilename(
251 2 : CPLGetDirname(poOgrDS->GetDescription()),
252 2 : osUrlTmp.c_str(), nullptr);
253 : }
254 2 : CPLDebug("LIBKML", "Trying to resolve style %s",
255 : osUrlTmp.c_str());
256 :
257 2 : VSILFILE *fp = VSIFOpenL(osUrlTmp.c_str(), "r");
258 2 : if (fp)
259 : {
260 2 : char szbuf[1025] = {'\0'};
261 6 : std::string oStyle = "";
262 :
263 : /***** loop, read and copy to a string *****/
264 0 : do
265 : {
266 : const size_t nRead =
267 2 : VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
268 :
269 2 : if (nRead == 0)
270 0 : break;
271 :
272 : /***** copy buf to the string *****/
273 :
274 2 : szbuf[nRead] = '\0';
275 2 : oStyle.append(szbuf);
276 2 : } while (!VSIFEofL(fp) && !VSIFErrorL(fp));
277 :
278 2 : VSIFCloseL(fp);
279 :
280 : /***** parse the kml into the ds style table *****/
281 :
282 2 : if (poOgrDS->ParseIntoStyleTable(
283 2 : &oStyle, osStyleFilename.c_str()))
284 : {
285 2 : bRetry = true;
286 : }
287 : }
288 : }
289 : }
290 : }
291 :
292 499 : if (!bRetry)
293 : {
294 497 : if (osStyleString.empty())
295 : {
296 : // Note: storing the style URL doesn't really follow
297 : // the OGR Feature Style string spec (https://gdal.org/user/ogr_feature_style.html#style-string-syntax),
298 : // but is better than nothing
299 1 : poOgrFeat->SetStyleString(osUrl.c_str());
300 : }
301 : else
302 : {
303 496 : poOgrFeat->SetStyleString(osStyleString.c_str());
304 : }
305 497 : break;
306 : }
307 : }
308 :
309 : /***** does the placemark have a style selector *****/
310 1251 : if (poKmlFeature->has_styleselector())
311 : {
312 2 : StyleSelectorPtr poKmlStyleSelector = poKmlFeature->get_styleselector();
313 :
314 : /***** is the style a style? *****/
315 1 : if (poKmlStyleSelector->IsA(kmldom::Type_Style))
316 : {
317 2 : StylePtr poKmlStyle = AsStyle(poKmlStyleSelector);
318 :
319 1 : 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 1 : CPLGetConfigOption("LIBKML_RESOLVE_STYLE", "no");
325 1 : if (CPLTestBool(pszResolve))
326 : {
327 0 : poOgrSM->InitFromFeature(poOgrFeat);
328 : }
329 : else
330 : {
331 : /***** if featyurestyle gets a name tool this needs
332 : changed to the above *****/
333 1 : poOgrSM->InitStyleString(nullptr);
334 : }
335 :
336 : /***** read the style *****/
337 1 : kml2stylestring(std::move(poKmlStyle), poOgrSM);
338 :
339 : /***** add the style to the feature *****/
340 1 : poOgrFeat->SetStyleString(poOgrSM->GetStyleString(nullptr));
341 :
342 1 : delete poOgrSM;
343 : }
344 : /***** is the style a stylemap? *****/
345 0 : else if (poKmlStyleSelector->IsA(kmldom::Type_StyleMap))
346 : {
347 : // TODO: Need to figure out what to do with a style map.
348 : }
349 : }
350 1251 : }
|