Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: JML Translator
4 : * Purpose: Implements OGRJMLWriterLayer class.
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "ogr_jml.h"
13 : #include "cpl_conv.h"
14 : #include "ogr_p.h"
15 :
16 : #include <cstdlib>
17 :
18 : /************************************************************************/
19 : /* OGRJMLWriterLayer() */
20 : /************************************************************************/
21 :
22 38 : OGRJMLWriterLayer::OGRJMLWriterLayer(const char *pszLayerName,
23 : OGRSpatialReference *poSRS,
24 : OGRJMLDataset *poDSIn, VSILFILE *fpIn,
25 : bool bAddRGBFieldIn,
26 : bool bAddOGRStyleFieldIn,
27 38 : bool bClassicGMLIn)
28 38 : : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn(pszLayerName)), fp(fpIn),
29 : bFeaturesWritten(false), bAddRGBField(bAddRGBFieldIn),
30 : bAddOGRStyleField(bAddOGRStyleFieldIn), bClassicGML(bClassicGMLIn),
31 76 : nNextFID(0), nBBoxOffset(0)
32 : {
33 38 : SetDescription(poFeatureDefn->GetName());
34 38 : poFeatureDefn->Reference();
35 :
36 38 : if (poSRS)
37 : {
38 1 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
39 1 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
40 1 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "EPSG") &&
41 : pszAuthCode != nullptr)
42 : {
43 1 : osSRSAttr = " srsName=\"http://www.opengis.net/gml/srs/epsg.xml#";
44 1 : osSRSAttr += pszAuthCode;
45 1 : osSRSAttr += "\"";
46 : }
47 : }
48 :
49 38 : VSIFPrintfL(fp,
50 : "<?xml version='1.0' encoding='UTF-8'?>\n"
51 : "<JCSDataFile xmlns:gml=\"http://www.opengis.net/gml\" "
52 : "xmlns:xsi=\"http://www.w3.org/2000/10/XMLSchema-instance\" >\n"
53 : "<JCSGMLInputTemplate>\n"
54 : "<CollectionElement>featureCollection</CollectionElement>\n"
55 : "<FeatureElement>feature</FeatureElement>\n"
56 : "<GeometryElement>geometry</GeometryElement>\n"
57 : "<CRSElement>boundedBy</CRSElement>\n"
58 : "<ColumnDefinitions>\n");
59 38 : }
60 :
61 : /************************************************************************/
62 : /* ~OGRJMLWriterLayer() */
63 : /************************************************************************/
64 :
65 76 : OGRJMLWriterLayer::~OGRJMLWriterLayer()
66 : {
67 38 : if (!bFeaturesWritten)
68 : {
69 17 : VSIFPrintfL(fp,
70 : "</ColumnDefinitions>\n</JCSGMLInputTemplate>\n"
71 : "<featureCollection>\n"
72 : " <gml:boundedBy>\n"
73 : " <gml:Box%s>\n"
74 : " <gml:coordinates decimal=\".\" cs=\",\" ts=\" "
75 : "\">0.00,0.00 -1.00,-1.00</gml:coordinates>\n"
76 : " </gml:Box>\n"
77 : " </gml:boundedBy>\n",
78 : osSRSAttr.c_str());
79 : }
80 21 : else if (nBBoxOffset > 0)
81 : {
82 21 : VSIFSeekL(fp, nBBoxOffset, SEEK_SET);
83 21 : if (sLayerExtent.IsInit())
84 : {
85 : char szBuffer[101];
86 19 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.10f,%.10f %.10f,%.10f",
87 : sLayerExtent.MinX, sLayerExtent.MinY, sLayerExtent.MaxX,
88 : sLayerExtent.MaxY);
89 19 : VSIFPrintfL(fp, "%s", szBuffer);
90 : }
91 : else
92 : {
93 2 : VSIFPrintfL(fp, "0.00,0.00 -1.00,-1.00");
94 : }
95 21 : VSIFSeekL(fp, 0, SEEK_END);
96 : }
97 38 : VSIFPrintfL(fp, "</featureCollection>\n</JCSDataFile>\n");
98 38 : poFeatureDefn->Release();
99 76 : }
100 :
101 : /************************************************************************/
102 : /* WriteColumnDeclaration() */
103 : /************************************************************************/
104 :
105 111 : void OGRJMLWriterLayer::WriteColumnDeclaration(const char *pszName,
106 : const char *pszType)
107 : {
108 111 : char *pszEscapedName = OGRGetXML_UTF8_EscapedString(pszName);
109 111 : if (bClassicGML)
110 : {
111 0 : VSIFPrintfL(fp,
112 : " <column>\n"
113 : " <name>%s</name>\n"
114 : " <type>%s</type>\n"
115 : " <valueElement elementName=\"%s\"/>\n"
116 : " <valueLocation position=\"body\"/>\n"
117 : " </column>\n",
118 : pszEscapedName, pszType, pszEscapedName);
119 : }
120 : else
121 : {
122 111 : VSIFPrintfL(fp,
123 : " <column>\n"
124 : " <name>%s</name>\n"
125 : " <type>%s</type>\n"
126 : " <valueElement elementName=\"property\" "
127 : "attributeName=\"name\" attributeValue=\"%s\"/>\n"
128 : " <valueLocation position=\"body\"/>\n"
129 : " </column>\n",
130 : pszEscapedName, pszType, pszEscapedName);
131 : }
132 111 : CPLFree(pszEscapedName);
133 111 : }
134 :
135 : /************************************************************************/
136 : /* ICreateFeature() */
137 : /************************************************************************/
138 :
139 72 : OGRErr OGRJMLWriterLayer::ICreateFeature(OGRFeature *poFeature)
140 :
141 : {
142 : /* Finish column declaration if we haven't yet created a feature */
143 72 : if (!bFeaturesWritten)
144 : {
145 21 : if (bAddOGRStyleField && poFeatureDefn->GetFieldIndex("OGR_STYLE") < 0)
146 : {
147 2 : WriteColumnDeclaration("OGR_STYLE", "STRING");
148 : }
149 21 : if (bAddRGBField && poFeatureDefn->GetFieldIndex("R_G_B") < 0)
150 : {
151 18 : WriteColumnDeclaration("R_G_B", "STRING");
152 : }
153 21 : VSIFPrintfL(fp,
154 : "</ColumnDefinitions>\n</JCSGMLInputTemplate>\n"
155 : "<featureCollection>\n"
156 : " <gml:boundedBy>\n"
157 : " <gml:Box%s>\n"
158 : " <gml:coordinates decimal=\".\" cs=\",\" ts=\" \">",
159 : osSRSAttr.c_str());
160 21 : if (EQUAL(poDS->GetDescription(), "/vsistdout/"))
161 : {
162 0 : VSIFPrintfL(fp, "0.00,0.00 -1.00,-1.00");
163 : }
164 : else
165 : {
166 21 : nBBoxOffset = VSIFTellL(fp);
167 21 : VSIFPrintfL(fp,
168 : // 100 characters reserved
169 : " "
170 : " ");
171 : }
172 21 : VSIFPrintfL(fp, "</gml:coordinates>\n"
173 : " </gml:Box>\n"
174 : " </gml:boundedBy>\n");
175 21 : bFeaturesWritten = true;
176 : }
177 :
178 72 : if (bClassicGML)
179 0 : VSIFPrintfL(fp, " <featureMember>\n");
180 72 : VSIFPrintfL(fp, " <feature>\n");
181 :
182 : /* Add geometry */
183 72 : VSIFPrintfL(fp, " <geometry>\n");
184 72 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
185 72 : if (poGeom != nullptr)
186 : {
187 36 : if (!poGeom->IsEmpty())
188 : {
189 36 : OGREnvelope sExtent;
190 36 : poGeom->getEnvelope(&sExtent);
191 36 : sLayerExtent.Merge(sExtent);
192 : }
193 36 : char *pszGML = poGeom->exportToGML();
194 36 : VSIFPrintfL(fp, " %s\n", pszGML);
195 36 : CPLFree(pszGML);
196 : }
197 : else
198 : {
199 36 : VSIFPrintfL(fp, " %s\n",
200 : "<gml:MultiGeometry></gml:MultiGeometry>");
201 : }
202 72 : VSIFPrintfL(fp, " </geometry>\n");
203 :
204 : /* Add fields */
205 418 : for (int i = 0; i < poFeature->GetFieldCount(); i++)
206 : {
207 346 : char *pszName = OGRGetXML_UTF8_EscapedString(
208 346 : poFeatureDefn->GetFieldDefn(i)->GetNameRef());
209 346 : if (bClassicGML)
210 0 : VSIFPrintfL(fp, " <%s>", pszName);
211 : else
212 346 : VSIFPrintfL(fp, " <property name=\"%s\">", pszName);
213 346 : if (poFeature->IsFieldSetAndNotNull(i))
214 : {
215 : const OGRFieldType eType =
216 250 : poFeatureDefn->GetFieldDefn(i)->GetType();
217 250 : if (eType == OFTString)
218 : {
219 52 : char *pszValue = OGRGetXML_UTF8_EscapedString(
220 : poFeature->GetFieldAsString(i));
221 52 : VSIFPrintfL(fp, "%s", pszValue);
222 52 : CPLFree(pszValue);
223 : }
224 198 : else if (eType == OFTDateTime)
225 : {
226 50 : int nYear = 0;
227 50 : int nMonth = 0;
228 50 : int nDay = 0;
229 50 : int nHour = 0;
230 50 : int nMinute = 0;
231 50 : int nTZFlag = 0;
232 50 : float fSecond = 0.0f;
233 50 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay, &nHour,
234 : &nMinute, &fSecond, &nTZFlag);
235 : /* When writing time zone, OpenJUMP expects .XXX seconds */
236 : /* to be written */
237 50 : if (nTZFlag > 1 || OGR_GET_MS(fSecond) != 0)
238 1 : VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%06.3f", nYear,
239 : nMonth, nDay, nHour, nMinute, fSecond);
240 : else
241 49 : VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02d", nYear,
242 : nMonth, nDay, nHour, nMinute, (int)fSecond);
243 50 : if (nTZFlag > 1)
244 : {
245 1 : int nOffset = (nTZFlag - 100) * 15;
246 1 : int nHours = (int)(nOffset / 60); // round towards zero
247 1 : int nMinutes = std::abs(nOffset - nHours * 60);
248 :
249 1 : if (nOffset < 0)
250 : {
251 0 : VSIFPrintfL(fp, "-");
252 0 : nHours = std::abs(nHours);
253 : }
254 : else
255 1 : VSIFPrintfL(fp, "+");
256 :
257 1 : VSIFPrintfL(fp, "%02d%02d", nHours, nMinutes);
258 : }
259 : }
260 : else
261 : {
262 148 : VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString(i));
263 : }
264 : }
265 346 : if (bClassicGML)
266 0 : VSIFPrintfL(fp, "</%s>\n", pszName);
267 : else
268 346 : VSIFPrintfL(fp, "</property>\n");
269 346 : CPLFree(pszName);
270 : }
271 :
272 : /* Add OGR_STYLE from feature style string (if asked) */
273 72 : if (bAddOGRStyleField && poFeatureDefn->GetFieldIndex("OGR_STYLE") < 0)
274 : {
275 2 : if (bClassicGML)
276 0 : VSIFPrintfL(fp, " <OGR_STYLE>");
277 : else
278 2 : VSIFPrintfL(fp, " <property name=\"%s\">", "OGR_STYLE");
279 2 : if (poFeature->GetStyleString() != nullptr)
280 : {
281 : char *pszValue =
282 2 : OGRGetXML_UTF8_EscapedString(poFeature->GetStyleString());
283 2 : VSIFPrintfL(fp, "%s", pszValue);
284 2 : CPLFree(pszValue);
285 : }
286 2 : if (bClassicGML)
287 0 : VSIFPrintfL(fp, "</OGR_STYLE>\n");
288 : else
289 2 : VSIFPrintfL(fp, "</property>\n");
290 : }
291 :
292 : /* Derive R_G_B field from feature style string */
293 72 : if (bAddRGBField && poFeatureDefn->GetFieldIndex("R_G_B") < 0)
294 : {
295 68 : if (bClassicGML)
296 0 : VSIFPrintfL(fp, " <R_G_B>");
297 : else
298 68 : VSIFPrintfL(fp, " <property name=\"%s\">", "R_G_B");
299 68 : if (poFeature->GetStyleString() != nullptr)
300 : {
301 : OGRwkbGeometryType eGeomType =
302 3 : poGeom ? wkbFlatten(poGeom->getGeometryType()) : wkbUnknown;
303 6 : OGRStyleMgr oMgr;
304 3 : oMgr.InitFromFeature(poFeature);
305 6 : for (int i = 0; i < oMgr.GetPartCount(); i++)
306 : {
307 3 : OGRStyleTool *poTool = oMgr.GetPart(i);
308 3 : if (poTool != nullptr)
309 : {
310 3 : const char *pszColor = nullptr;
311 5 : if (poTool->GetType() == OGRSTCPen &&
312 5 : eGeomType != wkbPolygon && eGeomType != wkbMultiPolygon)
313 : {
314 : GBool bIsNull;
315 2 : pszColor = ((OGRStylePen *)poTool)->Color(bIsNull);
316 2 : if (bIsNull)
317 0 : pszColor = nullptr;
318 : }
319 1 : else if (poTool->GetType() == OGRSTCBrush)
320 : {
321 : GBool bIsNull;
322 : pszColor =
323 1 : ((OGRStyleBrush *)poTool)->ForeColor(bIsNull);
324 1 : if (bIsNull)
325 0 : pszColor = nullptr;
326 : }
327 : int R, G, B, A;
328 6 : if (pszColor != nullptr &&
329 6 : poTool->GetRGBFromString(pszColor, R, G, B, A) &&
330 3 : A != 0)
331 : {
332 3 : VSIFPrintfL(fp, "%02X%02X%02X", R, G, B);
333 : }
334 3 : delete poTool;
335 : }
336 : }
337 : }
338 68 : if (bClassicGML)
339 0 : VSIFPrintfL(fp, "</R_G_B>\n");
340 : else
341 68 : VSIFPrintfL(fp, "</property>\n");
342 : }
343 :
344 72 : VSIFPrintfL(fp, " </feature>\n");
345 72 : if (bClassicGML)
346 0 : VSIFPrintfL(fp, " </featureMember>\n");
347 :
348 72 : poFeature->SetFID(nNextFID++);
349 :
350 72 : return OGRERR_NONE;
351 : }
352 :
353 : /************************************************************************/
354 : /* CreateField() */
355 : /************************************************************************/
356 :
357 92 : OGRErr OGRJMLWriterLayer::CreateField(const OGRFieldDefn *poFieldDefn,
358 : int bApproxOK)
359 : {
360 92 : if (bFeaturesWritten)
361 1 : return OGRERR_FAILURE;
362 :
363 91 : if (!bAddRGBField && strcmp(poFieldDefn->GetNameRef(), "R_G_B") == 0)
364 0 : return OGRERR_FAILURE;
365 :
366 91 : const char *pszType = nullptr;
367 91 : OGRFieldType eType = poFieldDefn->GetType();
368 91 : if (eType == OFTInteger)
369 : {
370 17 : pszType = "INTEGER";
371 : }
372 74 : else if (eType == OFTInteger64)
373 : {
374 0 : pszType = "OBJECT";
375 : }
376 74 : else if (eType == OFTReal)
377 : {
378 17 : pszType = "DOUBLE";
379 : }
380 57 : else if (eType == OFTDate || eType == OFTDateTime)
381 : {
382 35 : pszType = "DATE";
383 : }
384 : else
385 : {
386 22 : if (eType != OFTString)
387 : {
388 1 : if (bApproxOK)
389 : {
390 1 : CPLError(
391 : CE_Warning, CPLE_AppDefined,
392 : "Field of type %s unhandled natively. Converting to string",
393 : OGRFieldDefn::GetFieldTypeName(eType));
394 : }
395 : else
396 : {
397 0 : CPLError(CE_Warning, CPLE_AppDefined,
398 : "Field of type %s unhandled natively.",
399 : OGRFieldDefn::GetFieldTypeName(eType));
400 0 : return OGRERR_FAILURE;
401 : }
402 : }
403 22 : pszType = "STRING";
404 : }
405 91 : WriteColumnDeclaration(poFieldDefn->GetNameRef(), pszType);
406 :
407 91 : poFeatureDefn->AddFieldDefn(poFieldDefn);
408 91 : return OGRERR_NONE;
409 : }
410 :
411 : /************************************************************************/
412 : /* TestCapability() */
413 : /************************************************************************/
414 :
415 79 : int OGRJMLWriterLayer::TestCapability(const char *pszCap)
416 :
417 : {
418 79 : if (EQUAL(pszCap, OLCStringsAsUTF8))
419 1 : return TRUE;
420 78 : if (EQUAL(pszCap, OLCSequentialWrite))
421 17 : return TRUE;
422 61 : if (EQUAL(pszCap, OLCCreateField))
423 18 : return !bFeaturesWritten;
424 :
425 43 : return FALSE;
426 : }
427 :
428 : /************************************************************************/
429 : /* GetDataset() */
430 : /************************************************************************/
431 :
432 17 : GDALDataset *OGRJMLWriterLayer::GetDataset()
433 : {
434 17 : return poDS;
435 : }
|