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