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 6 : osUrlTmp = CPLFormFilenameSafe(
251 4 : CPLGetDirnameSafe(poOgrDS->GetDescription())
252 : .c_str(),
253 2 : osUrlTmp.c_str(), nullptr);
254 : }
255 2 : CPLDebug("LIBKML", "Trying to resolve style %s",
256 : osUrlTmp.c_str());
257 :
258 2 : VSILFILE *fp = VSIFOpenL(osUrlTmp.c_str(), "r");
259 2 : if (fp)
260 : {
261 2 : char szbuf[1025] = {'\0'};
262 6 : std::string oStyle = "";
263 :
264 : /***** loop, read and copy to a string *****/
265 0 : do
266 : {
267 : const size_t nRead =
268 2 : VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp);
269 :
270 2 : if (nRead == 0)
271 0 : break;
272 :
273 : /***** copy buf to the string *****/
274 :
275 2 : szbuf[nRead] = '\0';
276 2 : oStyle.append(szbuf);
277 2 : } while (!VSIFEofL(fp) && !VSIFErrorL(fp));
278 :
279 2 : VSIFCloseL(fp);
280 :
281 : /***** parse the kml into the ds style table *****/
282 :
283 2 : if (poOgrDS->ParseIntoStyleTable(
284 2 : &oStyle, osStyleFilename.c_str()))
285 : {
286 2 : bRetry = true;
287 : }
288 : }
289 : }
290 : }
291 : }
292 :
293 499 : if (!bRetry)
294 : {
295 497 : if (osStyleString.empty())
296 : {
297 : // Note: storing the style URL doesn't really follow
298 : // the OGR Feature Style string spec (https://gdal.org/user/ogr_feature_style.html#style-string-syntax),
299 : // but is better than nothing
300 1 : poOgrFeat->SetStyleString(osUrl.c_str());
301 : }
302 : else
303 : {
304 496 : poOgrFeat->SetStyleString(osStyleString.c_str());
305 : }
306 497 : break;
307 : }
308 : }
309 :
310 : /***** does the placemark have a style selector *****/
311 1251 : if (poKmlFeature->has_styleselector())
312 : {
313 2 : StyleSelectorPtr poKmlStyleSelector = poKmlFeature->get_styleselector();
314 :
315 : /***** is the style a style? *****/
316 1 : if (poKmlStyleSelector->IsA(kmldom::Type_Style))
317 : {
318 2 : StylePtr poKmlStyle = AsStyle(poKmlStyleSelector);
319 :
320 1 : OGRStyleMgr *poOgrSM = new OGRStyleMgr;
321 :
322 : /***** if were resolving style the feature *****/
323 : /***** might already have styling to add too *****/
324 : const char *pszResolve =
325 1 : CPLGetConfigOption("LIBKML_RESOLVE_STYLE", "no");
326 1 : if (CPLTestBool(pszResolve))
327 : {
328 0 : poOgrSM->InitFromFeature(poOgrFeat);
329 : }
330 : else
331 : {
332 : /***** if featyurestyle gets a name tool this needs
333 : changed to the above *****/
334 1 : poOgrSM->InitStyleString(nullptr);
335 : }
336 :
337 : /***** read the style *****/
338 1 : kml2stylestring(std::move(poKmlStyle), poOgrSM);
339 :
340 : /***** add the style to the feature *****/
341 1 : poOgrFeat->SetStyleString(poOgrSM->GetStyleString(nullptr));
342 :
343 1 : delete poOgrSM;
344 : }
345 : /***** is the style a stylemap? *****/
346 0 : else if (poKmlStyleSelector->IsA(kmldom::Type_StyleMap))
347 : {
348 : // TODO: Need to figure out what to do with a style map.
349 : }
350 : }
351 1251 : }
|