/src/MapServer/src/mapogroutput.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: OGR Output (for WFS) |
6 | | * Author: Frank Warmerdam (warmerdam@pobox.com) |
7 | | * |
8 | | ********************************************************************** |
9 | | * Copyright (c) 2010, Frank Warmerdam <warmerdam@pobox.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 in |
19 | | * all copies of this Software or works derived from this Software. |
20 | | * |
21 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | | * 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 <assert.h> |
31 | | #include "mapserver.h" |
32 | | #include "mapproject.h" |
33 | | #include "mapthread.h" |
34 | | #include "mapows.h" |
35 | | |
36 | | #include "ogr_api.h" |
37 | | #include "ogr_srs_api.h" |
38 | | #include "cpl_conv.h" |
39 | | #include "cpl_vsi.h" |
40 | | #include "cpl_string.h" |
41 | | |
42 | | #include <string> |
43 | | |
44 | | /************************************************************************/ |
45 | | /* msInitDefaultOGROutputFormat() */ |
46 | | /************************************************************************/ |
47 | | |
48 | | int msInitDefaultOGROutputFormat(outputFormatObj *format) |
49 | | |
50 | 0 | { |
51 | 0 | OGRSFDriverH hDriver; |
52 | |
|
53 | 0 | msOGRInitialize(); |
54 | | |
55 | | /* -------------------------------------------------------------------- */ |
56 | | /* check that this driver exists. Note visiting drivers should */ |
57 | | /* be pretty threadsafe so don't bother acquiring the GDAL */ |
58 | | /* lock. */ |
59 | | /* -------------------------------------------------------------------- */ |
60 | 0 | hDriver = OGRGetDriverByName(format->driver + 4); |
61 | 0 | if (hDriver == NULL) { |
62 | 0 | msSetError(MS_MISCERR, "No OGR driver named `%s' available.", |
63 | 0 | "msInitDefaultOGROutputFormat()", format->driver + 4); |
64 | 0 | return MS_FAILURE; |
65 | 0 | } |
66 | | |
67 | 0 | if (!OGR_Dr_TestCapability(hDriver, ODrCCreateDataSource)) { |
68 | 0 | msSetError(MS_MISCERR, "OGR `%s' driver does not support output.", |
69 | 0 | "msInitDefaultOGROutputFormat()", format->driver + 4); |
70 | 0 | return MS_FAILURE; |
71 | 0 | } |
72 | | |
73 | | /* -------------------------------------------------------------------- */ |
74 | | /* Initialize the object. */ |
75 | | /* -------------------------------------------------------------------- */ |
76 | 0 | format->imagemode = MS_IMAGEMODE_FEATURE; |
77 | 0 | format->renderer = MS_RENDER_WITH_OGR; |
78 | | |
79 | | /* perhaps we should eventually hardcode mimetypes and extensions |
80 | | for some formats? */ |
81 | |
|
82 | 0 | return MS_SUCCESS; |
83 | 0 | } |
84 | | |
85 | | /************************************************************************/ |
86 | | /* msCSLConcatenate() */ |
87 | | /************************************************************************/ |
88 | | |
89 | 0 | static char **msCSLConcatenate(char **papszResult, char **papszToBeAdded) { |
90 | 0 | char **papszIter = papszToBeAdded; |
91 | 0 | while (papszIter && *papszIter) { |
92 | 0 | papszResult = CSLAddString(papszResult, *papszIter); |
93 | 0 | papszIter++; |
94 | 0 | } |
95 | 0 | return papszResult; |
96 | 0 | } |
97 | | |
98 | | /************************************************************************/ |
99 | | /* msOGRRecursiveFileList() */ |
100 | | /* */ |
101 | | /* Collect a list of all files under the named directory, */ |
102 | | /* including those in subdirectories. */ |
103 | | /************************************************************************/ |
104 | | |
105 | 0 | char **msOGRRecursiveFileList(const char *path) { |
106 | 0 | char **file_list; |
107 | 0 | char **result_list = NULL; |
108 | 0 | int i, count, change; |
109 | |
|
110 | 0 | file_list = CPLReadDir(path); |
111 | 0 | count = CSLCount(file_list); |
112 | | |
113 | | /* -------------------------------------------------------------------- */ |
114 | | /* Sort the file list so we always get them back in the same */ |
115 | | /* order - it makes autotests more stable. */ |
116 | | /* -------------------------------------------------------------------- */ |
117 | 0 | do { |
118 | 0 | change = 0; |
119 | 0 | for (i = 0; i < count - 1; i++) { |
120 | 0 | if (strcasecmp(file_list[i], file_list[i + 1]) > 0) { |
121 | 0 | char *temp = file_list[i]; |
122 | 0 | file_list[i] = file_list[i + 1]; |
123 | 0 | file_list[i + 1] = temp; |
124 | 0 | change = 1; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | } while (change); |
128 | | |
129 | | /* -------------------------------------------------------------------- */ |
130 | | /* collect names we want and process subdirectories. */ |
131 | | /* -------------------------------------------------------------------- */ |
132 | 0 | for (i = 0; i < count; i++) { |
133 | 0 | char full_filename[MS_MAXPATHLEN]; |
134 | 0 | VSIStatBufL sStatBuf; |
135 | |
|
136 | 0 | if (EQUAL(file_list[i], ".") || EQUAL(file_list[i], "..")) |
137 | 0 | continue; |
138 | | |
139 | 0 | strlcpy(full_filename, CPLFormFilename(path, file_list[i], NULL), |
140 | 0 | sizeof(full_filename)); |
141 | |
|
142 | 0 | if (VSIStatL(full_filename, &sStatBuf) != 0) |
143 | 0 | continue; |
144 | | |
145 | 0 | if (VSI_ISREG(sStatBuf.st_mode)) { |
146 | 0 | result_list = CSLAddString(result_list, full_filename); |
147 | 0 | } else if (VSI_ISDIR(sStatBuf.st_mode)) { |
148 | 0 | char **subfiles = msOGRRecursiveFileList(full_filename); |
149 | |
|
150 | 0 | result_list = msCSLConcatenate(result_list, subfiles); |
151 | |
|
152 | 0 | CSLDestroy(subfiles); |
153 | 0 | } |
154 | 0 | } |
155 | |
|
156 | 0 | CSLDestroy(file_list); |
157 | |
|
158 | 0 | return result_list; |
159 | 0 | } |
160 | | |
161 | | /************************************************************************/ |
162 | | /* msOGRCleanupDS() */ |
163 | | /************************************************************************/ |
164 | | static void msOGRCleanupDS(const char *datasource_name) |
165 | | |
166 | 0 | { |
167 | 0 | char **file_list; |
168 | 0 | char path[MS_MAXPATHLEN]; |
169 | 0 | int i; |
170 | 0 | VSIStatBufL sStatBuf; |
171 | |
|
172 | 0 | if (VSIStatL(datasource_name, &sStatBuf) != 0) |
173 | 0 | return; |
174 | 0 | if (VSI_ISDIR(sStatBuf.st_mode)) |
175 | 0 | strlcpy(path, datasource_name, sizeof(path)); |
176 | 0 | else |
177 | 0 | strlcpy(path, CPLGetPath(datasource_name), sizeof(path)); |
178 | |
|
179 | 0 | file_list = CPLReadDir(path); |
180 | |
|
181 | 0 | for (i = 0; file_list != NULL && file_list[i] != NULL; i++) { |
182 | 0 | char full_filename[MS_MAXPATHLEN]; |
183 | 0 | VSIStatBufL sStatBuf; |
184 | |
|
185 | 0 | if (EQUAL(file_list[i], ".") || EQUAL(file_list[i], "..")) |
186 | 0 | continue; |
187 | | |
188 | 0 | strlcpy(full_filename, CPLFormFilename(path, file_list[i], NULL), |
189 | 0 | sizeof(full_filename)); |
190 | |
|
191 | 0 | if (VSIStatL(full_filename, &sStatBuf) != 0) |
192 | 0 | continue; |
193 | | |
194 | 0 | if (VSI_ISREG(sStatBuf.st_mode)) { |
195 | 0 | VSIUnlink(full_filename); |
196 | 0 | } else if (VSI_ISDIR(sStatBuf.st_mode)) { |
197 | 0 | msOGRCleanupDS(full_filename); |
198 | 0 | } |
199 | 0 | } |
200 | |
|
201 | 0 | CSLDestroy(file_list); |
202 | |
|
203 | 0 | VSIRmdir(path); |
204 | 0 | } |
205 | | |
206 | | /************************************************************************/ |
207 | | /* msOGRSetPoints() */ |
208 | | /************************************************************************/ |
209 | | |
210 | | static void msOGRSetPoints(OGRGeometryH hGeom, lineObj *line, |
211 | 0 | int bWant2DOutput) { |
212 | 0 | int i; |
213 | 0 | if (bWant2DOutput) { |
214 | 0 | for (i = 0; i < line->numpoints; i++) { |
215 | 0 | OGR_G_SetPoint_2D(hGeom, i, line->point[i].x, line->point[i].y); |
216 | 0 | } |
217 | 0 | } else { |
218 | 0 | for (i = 0; i < line->numpoints; i++) { |
219 | 0 | OGR_G_SetPoint(hGeom, i, line->point[i].x, line->point[i].y, |
220 | 0 | line->point[i].z); |
221 | 0 | } |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | /************************************************************************/ |
226 | | /* msOGRWriteShape() */ |
227 | | /************************************************************************/ |
228 | | |
229 | | static int msOGRWriteShape(OGRLayerH hOGRLayer, shapeObj *shape, |
230 | | gmlItemListObj *item_list, int nFirstOGRFieldIndex, |
231 | | const char *pszFeatureid, bool geomTypeForced) |
232 | | |
233 | 0 | { |
234 | 0 | OGRGeometryH hGeom = NULL; |
235 | 0 | OGRFeatureH hFeat; |
236 | 0 | OGRErr eErr; |
237 | 0 | int i, out_field; |
238 | 0 | OGRwkbGeometryType eLayerGType, eFlattenLayerGType; |
239 | 0 | OGRFeatureDefnH hLayerDefn; |
240 | 0 | int bWant2DOutput; |
241 | |
|
242 | 0 | hLayerDefn = OGR_L_GetLayerDefn(hOGRLayer); |
243 | 0 | eLayerGType = OGR_FD_GetGeomType(hLayerDefn); |
244 | 0 | eFlattenLayerGType = wkbFlatten(eLayerGType); |
245 | 0 | bWant2DOutput = (eLayerGType == eFlattenLayerGType); |
246 | | |
247 | | /* -------------------------------------------------------------------- */ |
248 | | /* Transform point geometry. */ |
249 | | /* -------------------------------------------------------------------- */ |
250 | 0 | if (shape->type == MS_SHAPE_POINT) { |
251 | 0 | OGRGeometryH hMP = NULL; |
252 | 0 | int j; |
253 | |
|
254 | 0 | if (shape->numlines == 1 && shape->line[0].numpoints > 1) { |
255 | 0 | hGeom = OGR_G_CreateGeometry(wkbMultiPoint); |
256 | 0 | for (j = 0; j < shape->line[0].numpoints; j++) { |
257 | 0 | OGRGeometryH hPoint = OGR_G_CreateGeometry(wkbPoint); |
258 | 0 | if (bWant2DOutput) { |
259 | 0 | OGR_G_SetPoint_2D(hPoint, 0, shape->line[0].point[j].x, |
260 | 0 | shape->line[0].point[j].y); |
261 | 0 | } else { |
262 | 0 | OGR_G_SetPoint(hPoint, 0, shape->line[0].point[j].x, |
263 | 0 | shape->line[0].point[j].y, shape->line[0].point[j].z); |
264 | 0 | } |
265 | |
|
266 | 0 | OGR_G_AddGeometryDirectly(hGeom, hPoint); |
267 | 0 | } |
268 | 0 | } else { |
269 | 0 | if (shape->numlines > 1) |
270 | 0 | hMP = OGR_G_CreateGeometry(wkbMultiPoint); |
271 | |
|
272 | 0 | for (j = 0; j < shape->numlines; j++) { |
273 | 0 | if (shape->line[j].numpoints != 1) { |
274 | 0 | msSetError(MS_MISCERR, "Failed on odd point geometry.", |
275 | 0 | "msOGRWriteShape()"); |
276 | 0 | return MS_FAILURE; |
277 | 0 | } |
278 | | |
279 | 0 | hGeom = OGR_G_CreateGeometry(wkbPoint); |
280 | 0 | if (bWant2DOutput) { |
281 | 0 | OGR_G_SetPoint_2D(hGeom, 0, shape->line[j].point[0].x, |
282 | 0 | shape->line[j].point[0].y); |
283 | 0 | } else { |
284 | 0 | OGR_G_SetPoint(hGeom, 0, shape->line[j].point[0].x, |
285 | 0 | shape->line[j].point[0].y, shape->line[j].point[0].z); |
286 | 0 | } |
287 | |
|
288 | 0 | if (hMP != NULL) { |
289 | 0 | OGR_G_AddGeometryDirectly(hMP, hGeom); |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | 0 | if (hMP != NULL) |
294 | 0 | hGeom = hMP; |
295 | 0 | } |
296 | 0 | } |
297 | | |
298 | | /* -------------------------------------------------------------------- */ |
299 | | /* Transform line geometry. */ |
300 | | /* -------------------------------------------------------------------- */ |
301 | 0 | else if (shape->type == MS_SHAPE_LINE) { |
302 | 0 | OGRGeometryH hML = NULL; |
303 | 0 | int j; |
304 | |
|
305 | 0 | if (shape->numlines > 1) |
306 | 0 | hML = OGR_G_CreateGeometry(wkbMultiLineString); |
307 | |
|
308 | 0 | for (j = 0; j < shape->numlines; j++) { |
309 | |
|
310 | 0 | if (shape->line[j].numpoints < 2) { |
311 | 0 | msSetError(MS_MISCERR, "Failed on odd line geometry.", |
312 | 0 | "msOGRWriteShape()"); |
313 | 0 | return MS_FAILURE; |
314 | 0 | } |
315 | | |
316 | 0 | hGeom = OGR_G_CreateGeometry(wkbLineString); |
317 | |
|
318 | 0 | msOGRSetPoints(hGeom, &(shape->line[j]), bWant2DOutput); |
319 | |
|
320 | 0 | if (hML != NULL) { |
321 | 0 | OGR_G_AddGeometryDirectly(hML, hGeom); |
322 | 0 | hGeom = hML; |
323 | 0 | } |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | /* -------------------------------------------------------------------- */ |
328 | | /* Transform polygon geometry. */ |
329 | | /* -------------------------------------------------------------------- */ |
330 | 0 | else if (shape->type == MS_SHAPE_POLYGON) { |
331 | 0 | int iRing, iOuter; |
332 | 0 | int *outer_flags; |
333 | 0 | OGRGeometryH hMP = NULL; |
334 | |
|
335 | 0 | outer_flags = msGetOuterList(shape); |
336 | |
|
337 | 0 | for (iOuter = 0; iOuter < shape->numlines; iOuter++) { |
338 | 0 | int *inner_flags; |
339 | 0 | OGRGeometryH hRing; |
340 | |
|
341 | 0 | if (!outer_flags[iOuter]) |
342 | 0 | continue; |
343 | | |
344 | 0 | if (hGeom != nullptr) { |
345 | 0 | assert(hMP == nullptr); |
346 | 0 | hMP = OGR_G_CreateGeometry(wkbMultiPolygon); |
347 | 0 | OGR_G_AddGeometryDirectly(hMP, hGeom); |
348 | 0 | } |
349 | 0 | hGeom = OGR_G_CreateGeometry(wkbPolygon); |
350 | | |
351 | | /* handle outer ring */ |
352 | |
|
353 | 0 | hRing = OGR_G_CreateGeometry(wkbLinearRing); |
354 | |
|
355 | 0 | msOGRSetPoints(hRing, &(shape->line[iOuter]), bWant2DOutput); |
356 | |
|
357 | 0 | OGR_G_AddGeometryDirectly(hGeom, hRing); |
358 | | |
359 | | /* handle inner rings (holes) */ |
360 | 0 | inner_flags = msGetInnerList(shape, iOuter, outer_flags); |
361 | |
|
362 | 0 | for (iRing = 0; iRing < shape->numlines; iRing++) { |
363 | 0 | if (!inner_flags[iRing]) |
364 | 0 | continue; |
365 | | |
366 | 0 | hRing = OGR_G_CreateGeometry(wkbLinearRing); |
367 | |
|
368 | 0 | msOGRSetPoints(hRing, &(shape->line[iRing]), bWant2DOutput); |
369 | |
|
370 | 0 | OGR_G_AddGeometryDirectly(hGeom, hRing); |
371 | 0 | } |
372 | |
|
373 | 0 | free(inner_flags); |
374 | |
|
375 | 0 | if (hMP) { |
376 | 0 | OGR_G_AddGeometryDirectly(hMP, hGeom); |
377 | 0 | hGeom = nullptr; |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | 0 | free(outer_flags); |
382 | |
|
383 | 0 | if (hMP != nullptr) { |
384 | 0 | assert(hGeom == nullptr); |
385 | 0 | hGeom = hMP; |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | | /* -------------------------------------------------------------------- */ |
390 | | /* Consider trying to force the geometry to a new type if it */ |
391 | | /* doesn't match the layer. */ |
392 | | /* -------------------------------------------------------------------- */ |
393 | 0 | if (hGeom != NULL) { |
394 | 0 | OGRwkbGeometryType eFlattenFeatureGType = |
395 | 0 | wkbFlatten(OGR_G_GetGeometryType(hGeom)); |
396 | |
|
397 | 0 | if (eFlattenFeatureGType != eFlattenLayerGType) { |
398 | 0 | if (eFlattenLayerGType == wkbPolygon && geomTypeForced) |
399 | 0 | hGeom = OGR_G_ForceToPolygon(hGeom); |
400 | | |
401 | 0 | else if (eFlattenLayerGType == wkbMultiPolygon) |
402 | 0 | hGeom = OGR_G_ForceToMultiPolygon(hGeom); |
403 | | |
404 | 0 | else if (eFlattenLayerGType == wkbMultiPoint) |
405 | 0 | hGeom = OGR_G_ForceToMultiPoint(hGeom); |
406 | | |
407 | 0 | else if (eFlattenLayerGType == wkbMultiLineString) |
408 | 0 | hGeom = OGR_G_ForceToMultiLineString(hGeom); |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | /* -------------------------------------------------------------------- */ |
413 | | /* Consider flattening the geometry to 2D if we want 2D */ |
414 | | /* output. */ |
415 | | /* Note: this shouldn't be called in recent OGR versions where */ |
416 | | /* OGR_G_SetPoint_2D is properly honoured. */ |
417 | | /* -------------------------------------------------------------------- */ |
418 | |
|
419 | 0 | if (bWant2DOutput && hGeom != NULL) { |
420 | 0 | OGRwkbGeometryType eFeatureGType = OGR_G_GetGeometryType(hGeom); |
421 | 0 | if (eFeatureGType != wkbFlatten(eFeatureGType)) |
422 | 0 | OGR_G_FlattenTo2D(hGeom); |
423 | 0 | } |
424 | | |
425 | | /* -------------------------------------------------------------------- */ |
426 | | /* Create the feature, and attach the geometry. */ |
427 | | /* -------------------------------------------------------------------- */ |
428 | 0 | hFeat = OGR_F_Create(hLayerDefn); |
429 | |
|
430 | 0 | OGR_F_SetGeometryDirectly(hFeat, hGeom); |
431 | | |
432 | | /* -------------------------------------------------------------------- */ |
433 | | /* Set attributes. */ |
434 | | /* -------------------------------------------------------------------- */ |
435 | 0 | out_field = nFirstOGRFieldIndex; |
436 | 0 | for (i = 0; i < item_list->numitems; i++) { |
437 | 0 | gmlItemObj *item = item_list->items + i; |
438 | |
|
439 | 0 | if (pszFeatureid && !strcmp(pszFeatureid, item->name)) { |
440 | 0 | char *endptr; |
441 | 0 | long feature_id = strtol(shape->values[i], &endptr, 10); |
442 | 0 | if (endptr && *endptr == 0) { |
443 | | /* only set the featureid if it is numeric */ |
444 | 0 | OGR_F_SetFID(hFeat, feature_id); |
445 | 0 | } |
446 | 0 | } |
447 | |
|
448 | 0 | if (!item->visible) |
449 | 0 | continue; |
450 | | |
451 | | /* Avoid setting empty strings for numeric fields, so that OGR */ |
452 | | /* doesn't take them as 0. (#4633) */ |
453 | 0 | if (shape->values[i][0] == '\0') { |
454 | 0 | OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn(hLayerDefn, out_field); |
455 | 0 | OGRFieldType eFieldType = OGR_Fld_GetType(hFieldDefn); |
456 | 0 | if (eFieldType == OFTInteger || eFieldType == OFTReal || |
457 | 0 | eFieldType == OFTInteger64) { |
458 | 0 | out_field++; |
459 | 0 | continue; |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | 0 | const char *pszValue = shape->values[i]; |
464 | | #if GDAL_VERSION_MAJOR == 3 && GDAL_VERSION_MINOR < 7 |
465 | | // Boolean true and false values are handled in GDAL 3.7+ |
466 | | // otherwise any string values revert to false |
467 | | if (strcmp(pszValue, "true") == 0) { |
468 | | OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn(hLayerDefn, out_field); |
469 | | OGRFieldSubType eFieldSubType = OGR_Fld_GetSubType(hFieldDefn); |
470 | | if (eFieldSubType == OFSTBoolean) { |
471 | | pszValue = "1"; |
472 | | } |
473 | | } |
474 | | |
475 | | if (strcmp(pszValue, "false") == 0) { |
476 | | OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn(hLayerDefn, out_field); |
477 | | OGRFieldSubType eFieldSubType = OGR_Fld_GetSubType(hFieldDefn); |
478 | | if (eFieldSubType == OFSTBoolean) { |
479 | | pszValue = "0"; |
480 | | } |
481 | | } |
482 | | #endif |
483 | |
|
484 | 0 | OGR_F_SetFieldString(hFeat, out_field++, pszValue); |
485 | 0 | } |
486 | | |
487 | | /* -------------------------------------------------------------------- */ |
488 | | /* Write out and cleanup. */ |
489 | | /* -------------------------------------------------------------------- */ |
490 | 0 | eErr = OGR_L_CreateFeature(hOGRLayer, hFeat); |
491 | |
|
492 | 0 | OGR_F_Destroy(hFeat); |
493 | |
|
494 | 0 | if (eErr != OGRERR_NONE) { |
495 | 0 | msSetError(MS_OGRERR, "Attempt to write feature failed (code=%d):\n%s", |
496 | 0 | "msOGRWriteShape()", (int)eErr, CPLGetLastErrorMsg()); |
497 | 0 | } |
498 | |
|
499 | 0 | if (eErr == OGRERR_NONE) |
500 | 0 | return MS_SUCCESS; |
501 | 0 | else |
502 | 0 | return MS_FAILURE; |
503 | 0 | } |
504 | | |
505 | | /************************************************************************/ |
506 | | /* msOGRStdoutWriteFunction() */ |
507 | | /************************************************************************/ |
508 | | |
509 | | /* Used by /vsistdout/ */ |
510 | | static size_t msOGRStdoutWriteFunction(const void *ptr, size_t size, |
511 | 0 | size_t nmemb, FILE *stream) { |
512 | 0 | msIOContext *ioctx = (msIOContext *)stream; |
513 | 0 | return msIO_contextWrite(ioctx, ptr, size * nmemb) / size; |
514 | 0 | } |
515 | | |
516 | | /************************************************************************/ |
517 | | /* msOGROutputGetAdditionalFiles() */ |
518 | | /* */ |
519 | | /* Collect additional files specified in */ |
520 | | /* wfs/ows_additional_files_in_output of WEB.METADATA and LAYER.METADATA */ |
521 | | /************************************************************************/ |
522 | | |
523 | | /* Result to be freed with CSLDestroy() */ |
524 | 0 | static char **msOGROutputGetAdditionalFiles(mapObj *map) { |
525 | 0 | int i; |
526 | 0 | hashTableObj *hSetAdditionalFiles; |
527 | 0 | char **papszFiles = NULL; |
528 | |
|
529 | 0 | hSetAdditionalFiles = msCreateHashTable(); |
530 | |
|
531 | 0 | for (i = -1; i < map->numlayers; i++) { |
532 | 0 | const char *value; |
533 | 0 | if (i < 0) { |
534 | 0 | value = msOWSLookupMetadata(&(map->web.metadata), "FO", |
535 | 0 | "additional_files_in_output"); |
536 | 0 | } else { |
537 | 0 | layerObj *layer = GET_LAYER(map, i); |
538 | 0 | if (!layer->resultcache || layer->resultcache->numresults == 0) |
539 | 0 | continue; |
540 | 0 | value = msOWSLookupMetadata(&(layer->metadata), "FO", |
541 | 0 | "additional_files_in_output"); |
542 | 0 | } |
543 | | |
544 | 0 | if (value != NULL) { |
545 | 0 | char **papszList = CSLTokenizeString2(value, ",", CSLT_HONOURSTRINGS); |
546 | 0 | char **papszListIter = papszList; |
547 | 0 | while (papszListIter && *papszListIter) { |
548 | 0 | const char *file = *papszListIter; |
549 | 0 | VSIStatBufL sStat; |
550 | |
|
551 | 0 | if (strncmp(file, "http://", strlen("http://")) == 0 || |
552 | 0 | strncmp(file, "https://", strlen("https://")) == 0) { |
553 | | /* Remote file ? We will use /vsicurl_streaming/ to read it */ |
554 | 0 | if (msLookupHashTable(hSetAdditionalFiles, file) == NULL) { |
555 | 0 | msInsertHashTable(hSetAdditionalFiles, file, "YES"); |
556 | 0 | papszFiles = CSLAddString( |
557 | 0 | papszFiles, CPLSPrintf("/vsicurl_streaming/%s", file)); |
558 | 0 | } |
559 | 0 | } else { |
560 | 0 | int nLen = (int)strlen(file); |
561 | 0 | char filename[MS_MAXPATHLEN]; |
562 | |
|
563 | 0 | if (CPLIsFilenameRelative(file)) { |
564 | 0 | if (!map->shapepath) |
565 | 0 | msTryBuildPath(filename, map->mappath, file); |
566 | 0 | else |
567 | 0 | msTryBuildPath3(filename, map->mappath, map->shapepath, file); |
568 | 0 | } else |
569 | 0 | strlcpy(filename, file, MS_MAXPATHLEN); |
570 | |
|
571 | 0 | if (nLen > 2 && (strcmp(file + nLen - 1, "/") == 0 || |
572 | 0 | strcmp(file + nLen - 2, "/*") == 0)) { |
573 | 0 | *strrchr(filename, '/') = '\0'; |
574 | 0 | } else if (nLen > 2 && (strcmp(file + nLen - 1, "\\") == 0 || |
575 | 0 | strcmp(file + nLen - 2, "\\*") == 0)) { |
576 | 0 | *strrchr(filename, '\\') = '\0'; |
577 | 0 | } |
578 | |
|
579 | 0 | if (msLookupHashTable(hSetAdditionalFiles, filename) == NULL) { |
580 | 0 | msInsertHashTable(hSetAdditionalFiles, filename, "YES"); |
581 | 0 | if (VSIStatL(filename, &sStat) == 0) { |
582 | 0 | if (VSI_ISDIR(sStat.st_mode)) { |
583 | 0 | char **papszDirContent = msOGRRecursiveFileList(filename); |
584 | 0 | papszFiles = msCSLConcatenate(papszFiles, papszDirContent); |
585 | 0 | CSLDestroy(papszDirContent); |
586 | 0 | } else { |
587 | 0 | papszFiles = CSLAddString(papszFiles, filename); |
588 | 0 | } |
589 | 0 | } else { |
590 | 0 | msDebug("File %s does not exist.\n", filename); |
591 | 0 | } |
592 | 0 | } |
593 | 0 | } |
594 | |
|
595 | 0 | papszListIter++; |
596 | 0 | } |
597 | 0 | CSLDestroy(papszList); |
598 | 0 | } |
599 | 0 | } |
600 | |
|
601 | 0 | msFreeHashTable(hSetAdditionalFiles); |
602 | |
|
603 | 0 | return papszFiles; |
604 | 0 | } |
605 | | |
606 | | /************************************************************************/ |
607 | | /* msOGRWriteFromQuery() */ |
608 | | /************************************************************************/ |
609 | | |
610 | | int msOGRWriteFromQuery(mapObj *map, outputFormatObj *format, int sendheaders) |
611 | | |
612 | 0 | { |
613 | | /* -------------------------------------------------------------------- */ |
614 | | /* Variable declarations. */ |
615 | | /* -------------------------------------------------------------------- */ |
616 | 0 | OGRSFDriverH hDriver; |
617 | 0 | OGRDataSourceH hDS; |
618 | 0 | const char *storage; |
619 | 0 | const char *fo_filename; |
620 | 0 | const char *form; |
621 | 0 | char datasource_name[MS_MAXPATHLEN]; |
622 | 0 | char base_dir[MS_MAXPATHLEN]; |
623 | 0 | char *request_dir = NULL; |
624 | 0 | char **ds_options = NULL; |
625 | 0 | char **layer_options = NULL; |
626 | 0 | char **file_list = NULL; |
627 | 0 | int iLayer, i; |
628 | 0 | int bDataSourceNameIsRequestDir = FALSE; |
629 | 0 | int bUseFeatureId = MS_FALSE; |
630 | 0 | const char *pszMatchingFeatures; |
631 | 0 | int nMatchingFeatures = -1; |
632 | 0 | const char *pszFormatName = format->driver + 4; |
633 | |
|
634 | 0 | pszMatchingFeatures = |
635 | 0 | msGetOutputFormatOption(format, "_matching_features_", ""); |
636 | 0 | if (pszMatchingFeatures[0] != '\0') |
637 | 0 | nMatchingFeatures = atoi(pszMatchingFeatures); |
638 | | |
639 | | /* -------------------------------------------------------------------- */ |
640 | | /* Fetch the output format driver. */ |
641 | | /* -------------------------------------------------------------------- */ |
642 | 0 | msOGRInitialize(); |
643 | |
|
644 | 0 | hDriver = OGRGetDriverByName(pszFormatName); |
645 | 0 | if (hDriver == NULL) { |
646 | 0 | msSetError(MS_MISCERR, "No OGR driver named `%s' available.", |
647 | 0 | "msOGRWriteFromQuery()", pszFormatName); |
648 | 0 | return MS_FAILURE; |
649 | 0 | } |
650 | | |
651 | | /* -------------------------------------------------------------------- */ |
652 | | /* Capture datasource and layer creation options. */ |
653 | | /* -------------------------------------------------------------------- */ |
654 | 0 | for (i = 0; i < format->numformatoptions; i++) { |
655 | 0 | if (strncasecmp(format->formatoptions[i], "LCO:", 4) == 0) |
656 | 0 | layer_options = CSLAddString(layer_options, format->formatoptions[i] + 4); |
657 | 0 | if (strncasecmp(format->formatoptions[i], "DSCO:", 5) == 0) |
658 | 0 | ds_options = CSLAddString(ds_options, format->formatoptions[i] + 5); |
659 | 0 | } |
660 | 0 | if (EQUAL(pszFormatName, "GeoJSON") && nMatchingFeatures >= 0) { |
661 | 0 | const char *pszNativeData = |
662 | 0 | CSLFetchNameValueDef(layer_options, "NATIVE_DATA", "{}"); |
663 | 0 | if (pszNativeData[strlen(pszNativeData) - 1] == '}') { |
664 | 0 | std::string tmpl(pszNativeData); |
665 | 0 | tmpl.resize(tmpl.size() - 1); |
666 | 0 | if (strlen(pszNativeData) > 2) |
667 | 0 | tmpl += ','; |
668 | 0 | tmpl += "\"numberMatched\":"; |
669 | 0 | tmpl += std::to_string(nMatchingFeatures); |
670 | 0 | tmpl += '}'; |
671 | 0 | layer_options = CSLSetNameValue(layer_options, "NATIVE_MEDIA_TYPE", |
672 | 0 | "application/vnd.geo+json"); |
673 | 0 | layer_options = |
674 | 0 | CSLSetNameValue(layer_options, "NATIVE_DATA", tmpl.c_str()); |
675 | 0 | } |
676 | 0 | } |
677 | 0 | if (!strcasecmp("true", |
678 | 0 | msGetOutputFormatOption(format, "USE_FEATUREID", "false"))) { |
679 | 0 | bUseFeatureId = MS_TRUE; |
680 | 0 | } |
681 | | |
682 | | /* ==================================================================== */ |
683 | | /* Determine the output datasource name to use. */ |
684 | | /* ==================================================================== */ |
685 | 0 | storage = msGetOutputFormatOption(format, "STORAGE", "filesystem"); |
686 | 0 | if (EQUAL(storage, "stream") && !msIO_isStdContext()) { |
687 | 0 | msIOContext *ioctx = msIO_getHandler(stdout); |
688 | 0 | if (ioctx != NULL) |
689 | 0 | VSIStdoutSetRedirection(msOGRStdoutWriteFunction, (FILE *)ioctx); |
690 | 0 | else |
691 | | /* bug #4858, streaming output won't work if standard output has been |
692 | | * redirected, we switch to memory output in this case |
693 | | */ |
694 | 0 | storage = "memory"; |
695 | 0 | } |
696 | | |
697 | | /* -------------------------------------------------------------------- */ |
698 | | /* Where are we putting stuff? */ |
699 | | /* -------------------------------------------------------------------- */ |
700 | 0 | if (EQUAL(storage, "filesystem")) { |
701 | 0 | base_dir[0] = '\0'; |
702 | 0 | } else if (EQUAL(storage, "memory")) { |
703 | 0 | strcpy(base_dir, "/vsimem/ogr_out/"); |
704 | 0 | } else if (EQUAL(storage, "stream")) { |
705 | | /* handled later */ |
706 | 0 | } else { |
707 | 0 | msSetError(MS_MISCERR, "STORAGE=%s value not supported.", |
708 | 0 | "msOGRWriteFromQuery()", storage); |
709 | 0 | CSLDestroy(layer_options); |
710 | 0 | CSLDestroy(ds_options); |
711 | 0 | return MS_FAILURE; |
712 | 0 | } |
713 | | |
714 | | /* -------------------------------------------------------------------- */ |
715 | | /* Create a subdirectory to handle this request. */ |
716 | | /* -------------------------------------------------------------------- */ |
717 | 0 | if (!EQUAL(storage, "stream")) { |
718 | 0 | const char *dir_to_create; |
719 | 0 | if (strlen(base_dir) > 0) |
720 | 0 | request_dir = msTmpFile(map, NULL, base_dir, ""); |
721 | 0 | else |
722 | 0 | request_dir = msTmpFile(map, NULL, NULL, ""); |
723 | |
|
724 | 0 | if (request_dir[strlen(request_dir) - 1] == '.') |
725 | 0 | request_dir[strlen(request_dir) - 1] = '\0'; |
726 | |
|
727 | 0 | dir_to_create = request_dir; |
728 | | /* Workaround issue in GDAL versions released at this time : |
729 | | * GDAL issue fixed per https://trac.osgeo.org/gdal/ticket/6991 */ |
730 | 0 | if (EQUAL(storage, "memory") && |
731 | 0 | EQUAL(format->driver + 4, "ESRI Shapefile")) { |
732 | 0 | dir_to_create = base_dir; |
733 | 0 | } |
734 | |
|
735 | 0 | if (VSIMkdir(dir_to_create, 0777) != 0) { |
736 | 0 | msSetError(MS_MISCERR, "Attempt to create directory '%s' failed.", |
737 | 0 | "msOGRWriteFromQuery()", dir_to_create); |
738 | 0 | msFree(request_dir); |
739 | 0 | CSLDestroy(layer_options); |
740 | 0 | CSLDestroy(ds_options); |
741 | 0 | return MS_FAILURE; |
742 | 0 | } |
743 | 0 | } |
744 | | /* else handled later */ |
745 | | |
746 | | /* -------------------------------------------------------------------- */ |
747 | | /* Setup the full datasource name. */ |
748 | | /* -------------------------------------------------------------------- */ |
749 | 0 | form = msGetOutputFormatOption(format, "FORM", "zip"); |
750 | |
|
751 | 0 | if (EQUAL(form, "zip")) |
752 | 0 | fo_filename = msGetOutputFormatOption(format, "FILENAME", "result.zip"); |
753 | 0 | else |
754 | 0 | fo_filename = msGetOutputFormatOption(format, "FILENAME", "result.dat"); |
755 | | |
756 | | /* Validate that the filename does not contain any directory */ |
757 | | /* information, which might lead to removal of unwanted files. (#4086) */ |
758 | 0 | if (strchr(fo_filename, '/') != NULL || strchr(fo_filename, ':') != NULL || |
759 | 0 | strchr(fo_filename, '\\') != NULL) { |
760 | 0 | msSetError(MS_MISCERR, |
761 | 0 | "Invalid value for FILENAME option. " |
762 | 0 | "It must not contain any directory information.", |
763 | 0 | "msOGRWriteFromQuery()"); |
764 | 0 | msFree(request_dir); |
765 | 0 | CSLDestroy(layer_options); |
766 | 0 | CSLDestroy(ds_options); |
767 | 0 | return MS_FAILURE; |
768 | 0 | } |
769 | | |
770 | 0 | if (!EQUAL(storage, "stream")) { |
771 | 0 | if (!msBuildPath(datasource_name, request_dir, fo_filename)) { |
772 | 0 | msFree(request_dir); |
773 | 0 | CSLDestroy(layer_options); |
774 | 0 | CSLDestroy(ds_options); |
775 | 0 | return MS_FAILURE; |
776 | 0 | } |
777 | | |
778 | 0 | if (EQUAL(form, "zip")) { |
779 | | /* if generating a zip file, remove the zip extension for the internal */ |
780 | | /* filename */ |
781 | 0 | if (EQUAL(CPLGetExtension(datasource_name), "zip")) { |
782 | 0 | *strrchr(datasource_name, '.') = '\0'; |
783 | 0 | } |
784 | | |
785 | | /* and add .dat extension if user didn't provide another extension */ |
786 | 0 | if (EQUAL(CPLGetExtension(datasource_name), "")) { |
787 | 0 | strlcat(datasource_name, ".dat", sizeof(datasource_name)); |
788 | 0 | } |
789 | 0 | } |
790 | | |
791 | | /* Shapefile and MapInfo driver only properly work with multiple layers */ |
792 | | /* if the output dataset name is a directory */ |
793 | 0 | if (EQUAL(format->driver + 4, "ESRI Shapefile") || |
794 | 0 | EQUAL(format->driver + 4, "MapInfo File")) { |
795 | 0 | bDataSourceNameIsRequestDir = TRUE; |
796 | 0 | strcpy(datasource_name, request_dir); |
797 | 0 | } |
798 | 0 | } else |
799 | 0 | strcpy(datasource_name, "/vsistdout/"); |
800 | | |
801 | 0 | msFree(request_dir); |
802 | 0 | request_dir = NULL; |
803 | | |
804 | | /* -------------------------------------------------------------------- */ |
805 | | /* Emit content type headers for stream output now. */ |
806 | | /* -------------------------------------------------------------------- */ |
807 | 0 | if (EQUAL(storage, "stream")) { |
808 | 0 | if (sendheaders && format->mimetype) { |
809 | 0 | msIO_setHeader("Content-Type", "%s", format->mimetype); |
810 | 0 | msIO_sendHeaders(); |
811 | 0 | } else |
812 | 0 | msIO_fprintf(stdout, "%c", 10); |
813 | 0 | } |
814 | | |
815 | | /* ==================================================================== */ |
816 | | /* Create the datasource. */ |
817 | | /* ==================================================================== */ |
818 | 0 | hDS = OGR_Dr_CreateDataSource(hDriver, datasource_name, ds_options); |
819 | 0 | CSLDestroy(ds_options); |
820 | |
|
821 | 0 | if (hDS == NULL) { |
822 | 0 | msOGRCleanupDS(datasource_name); |
823 | 0 | msSetError(MS_MISCERR, |
824 | 0 | "OGR CreateDataSource failed for '%s' with driver '%s'.", |
825 | 0 | "msOGRWriteFromQuery()", datasource_name, format->driver + 4); |
826 | 0 | CSLDestroy(layer_options); |
827 | 0 | return MS_FAILURE; |
828 | 0 | } |
829 | | |
830 | | /* ==================================================================== */ |
831 | | /* Process each layer with a resultset. */ |
832 | | /* ==================================================================== */ |
833 | 0 | for (iLayer = 0; iLayer < map->numlayers; iLayer++) { |
834 | 0 | int status = 0; |
835 | 0 | layerObj *layer = GET_LAYER(map, iLayer); |
836 | 0 | shapeObj resultshape; |
837 | 0 | OGRLayerH hOGRLayer; |
838 | 0 | OGRwkbGeometryType eGeomType; |
839 | 0 | OGRSpatialReferenceH srs = NULL; |
840 | 0 | gmlItemListObj *item_list = NULL; |
841 | 0 | const char *value; |
842 | 0 | char *pszWKT; |
843 | 0 | int nFirstOGRFieldIndex = -1; |
844 | 0 | const char *pszFeatureid = NULL; |
845 | |
|
846 | 0 | if (!layer->resultcache) |
847 | 0 | continue; |
848 | | |
849 | | /* -------------------------------------------------------------------- */ |
850 | | /* Will we need to reproject? */ |
851 | | /* -------------------------------------------------------------------- */ |
852 | 0 | if (layer->transform == MS_TRUE) |
853 | 0 | layer->project = |
854 | 0 | msProjectionsDiffer(&(layer->projection), &(layer->map->projection)); |
855 | | |
856 | | /* -------------------------------------------------------------------- */ |
857 | | /* Establish the geometry type to use for the created layer. */ |
858 | | /* First we consult the wfs_geomtype field and fallback to */ |
859 | | /* deriving something from the type of the mapserver layer. */ |
860 | | /* -------------------------------------------------------------------- */ |
861 | 0 | value = msOWSLookupMetadata(&(layer->metadata), "FOG", "geomtype"); |
862 | 0 | bool geomTypeForced = true; |
863 | 0 | if (value == NULL) { |
864 | 0 | geomTypeForced = false; |
865 | 0 | if (layer->type == MS_LAYER_POINT) |
866 | 0 | value = "Point"; |
867 | 0 | else if (layer->type == MS_LAYER_LINE) |
868 | 0 | value = "LineString"; |
869 | 0 | else if (layer->type == MS_LAYER_POLYGON) |
870 | 0 | value = "Polygon"; |
871 | 0 | else |
872 | 0 | value = "Geometry"; |
873 | 0 | } |
874 | |
|
875 | 0 | if (bUseFeatureId) |
876 | 0 | pszFeatureid = |
877 | 0 | msOWSLookupMetadata(&(layer->metadata), "FOG", "featureid"); |
878 | |
|
879 | 0 | if (strcasecmp(value, "Point") == 0) |
880 | 0 | eGeomType = wkbPoint; |
881 | 0 | else if (strcasecmp(value, "LineString") == 0) |
882 | 0 | eGeomType = wkbLineString; |
883 | 0 | else if (strcasecmp(value, "Polygon") == 0) |
884 | 0 | eGeomType = wkbPolygon; |
885 | 0 | else if (strcasecmp(value, "MultiPoint") == 0) |
886 | 0 | eGeomType = wkbMultiPoint; |
887 | 0 | else if (strcasecmp(value, "MultiLineString") == 0) |
888 | 0 | eGeomType = wkbMultiLineString; |
889 | 0 | else if (strcasecmp(value, "MultiPolygon") == 0) |
890 | 0 | eGeomType = wkbMultiPolygon; |
891 | 0 | else if (strcasecmp(value, "GeometryCollection") == 0) |
892 | 0 | eGeomType = wkbGeometryCollection; |
893 | 0 | else if (strcasecmp(value, "Point25D") == 0) |
894 | 0 | eGeomType = wkbPoint25D; |
895 | 0 | else if (strcasecmp(value, "LineString25D") == 0) |
896 | 0 | eGeomType = wkbLineString25D; |
897 | 0 | else if (strcasecmp(value, "Polygon25D") == 0) |
898 | 0 | eGeomType = wkbPolygon25D; |
899 | 0 | else if (strcasecmp(value, "MultiPoint25D") == 0) |
900 | 0 | eGeomType = wkbMultiPoint25D; |
901 | 0 | else if (strcasecmp(value, "MultiLineString25D") == 0) |
902 | 0 | eGeomType = wkbMultiLineString25D; |
903 | 0 | else if (strcasecmp(value, "MultiPolygon25D") == 0) |
904 | 0 | eGeomType = wkbMultiPolygon25D; |
905 | 0 | else if (strcasecmp(value, "GeometryCollection25D") == 0) |
906 | 0 | eGeomType = wkbGeometryCollection25D; |
907 | 0 | else if (strcasecmp(value, "Unknown") == 0 || |
908 | 0 | strcasecmp(value, "Geometry") == 0) |
909 | 0 | eGeomType = wkbUnknown; |
910 | 0 | else if (strcasecmp(value, "None") == 0) |
911 | 0 | eGeomType = wkbNone; |
912 | 0 | else |
913 | 0 | eGeomType = wkbUnknown; |
914 | | |
915 | | /* -------------------------------------------------------------------- */ |
916 | | /* Create a spatial reference. */ |
917 | | /* -------------------------------------------------------------------- */ |
918 | 0 | pszWKT = msProjectionObj2OGCWKT(&(map->projection)); |
919 | 0 | if (pszWKT != NULL) { |
920 | 0 | srs = OSRNewSpatialReference(pszWKT); |
921 | 0 | msFree(pszWKT); |
922 | 0 | } |
923 | | |
924 | | /* -------------------------------------------------------------------- */ |
925 | | /* Create the corresponding OGR Layer. */ |
926 | | /* -------------------------------------------------------------------- */ |
927 | 0 | hOGRLayer = |
928 | 0 | OGR_DS_CreateLayer(hDS, layer->name, srs, eGeomType, layer_options); |
929 | 0 | if (hOGRLayer == NULL) { |
930 | 0 | OGR_DS_Destroy(hDS); |
931 | 0 | msOGRCleanupDS(datasource_name); |
932 | 0 | msSetError( |
933 | 0 | MS_MISCERR, |
934 | 0 | "OGR OGR_DS_CreateLayer failed for layer '%s' with driver '%s'.", |
935 | 0 | "msOGRWriteFromQuery()", layer->name, format->driver + 4); |
936 | 0 | CSLDestroy(layer_options); |
937 | 0 | return MS_FAILURE; |
938 | 0 | } |
939 | | |
940 | 0 | if (srs != NULL) |
941 | 0 | OSRRelease(srs); |
942 | | |
943 | | /* -------------------------------------------------------------------- */ |
944 | | /* Create appropriate attributes on this layer. */ |
945 | | /* -------------------------------------------------------------------- */ |
946 | 0 | item_list = msGMLGetItems(layer, "G"); |
947 | 0 | assert(item_list->numitems == layer->numitems); |
948 | | |
949 | 0 | for (i = 0; i < layer->numitems; i++) { |
950 | 0 | OGRFieldDefnH hFldDefn; |
951 | 0 | OGRErr eErr; |
952 | 0 | const char *name; |
953 | 0 | gmlItemObj *item = item_list->items + i; |
954 | 0 | OGRFieldType eType; |
955 | 0 | OGRFieldSubType sType = OGRFieldSubType::OFSTNone; |
956 | |
|
957 | 0 | if (!item->visible) |
958 | 0 | continue; |
959 | | |
960 | 0 | if (item->alias) |
961 | 0 | name = item->alias; |
962 | 0 | else |
963 | 0 | name = item->name; |
964 | |
|
965 | 0 | if (item->type == NULL) |
966 | 0 | eType = OFTString; |
967 | 0 | else if (EQUAL(item->type, "Integer")) |
968 | 0 | eType = OFTInteger; |
969 | 0 | else if (EQUAL(item->type, "Long")) |
970 | 0 | eType = OFTInteger64; |
971 | 0 | else if (EQUAL(item->type, "Real")) |
972 | 0 | eType = OFTReal; |
973 | 0 | else if (EQUAL(item->type, "Character")) |
974 | 0 | eType = OFTString; |
975 | 0 | else if (EQUAL(item->type, "Date")) |
976 | 0 | eType = OFTDate; |
977 | 0 | else if (EQUAL(item->type, "Time")) |
978 | 0 | eType = OFTTime; |
979 | 0 | else if (EQUAL(item->type, "DateTime")) |
980 | 0 | eType = OFTDateTime; |
981 | 0 | else if (EQUAL(item->type, "Boolean")) { |
982 | 0 | eType = OFTInteger; |
983 | 0 | sType = OFSTBoolean; |
984 | 0 | } else |
985 | 0 | eType = OFTString; |
986 | |
|
987 | 0 | hFldDefn = OGR_Fld_Create(name, eType); |
988 | 0 | if (sType != OGRFieldSubType::OFSTNone) { |
989 | 0 | OGR_Fld_SetSubType(hFldDefn, sType); |
990 | 0 | } |
991 | 0 | if (item->width != 0) |
992 | 0 | OGR_Fld_SetWidth(hFldDefn, item->width); |
993 | 0 | if (item->precision != 0) |
994 | 0 | OGR_Fld_SetPrecision(hFldDefn, item->precision); |
995 | |
|
996 | 0 | eErr = OGR_L_CreateField(hOGRLayer, hFldDefn, TRUE); |
997 | 0 | OGR_Fld_Destroy(hFldDefn); |
998 | |
|
999 | 0 | if (eErr != OGRERR_NONE) { |
1000 | 0 | msSetError(MS_OGRERR, |
1001 | 0 | "Failed to create field '%s' in output feature schema:\n%s", |
1002 | 0 | "msOGRWriteFromQuery()", layer->items[i], |
1003 | 0 | CPLGetLastErrorMsg()); |
1004 | |
|
1005 | 0 | OGR_DS_Destroy(hDS); |
1006 | 0 | msOGRCleanupDS(datasource_name); |
1007 | 0 | msGMLFreeItems(item_list); |
1008 | 0 | CSLDestroy(layer_options); |
1009 | 0 | return MS_FAILURE; |
1010 | 0 | } |
1011 | | |
1012 | | /* The index of the first field we create is not necessarily 0 */ |
1013 | 0 | if (nFirstOGRFieldIndex < 0) |
1014 | 0 | nFirstOGRFieldIndex = |
1015 | 0 | OGR_FD_GetFieldCount(OGR_L_GetLayerDefn(hOGRLayer)) - 1; |
1016 | 0 | } |
1017 | | |
1018 | | /* -------------------------------------------------------------------- */ |
1019 | | /* Setup joins if needed. This is likely untested. */ |
1020 | | /* -------------------------------------------------------------------- */ |
1021 | 0 | if (layer->numjoins > 0) { |
1022 | 0 | int j; |
1023 | 0 | for (j = 0; j < layer->numjoins; j++) { |
1024 | 0 | status = msJoinConnect(layer, &(layer->joins[j])); |
1025 | 0 | if (status != MS_SUCCESS) { |
1026 | 0 | OGR_DS_Destroy(hDS); |
1027 | 0 | msOGRCleanupDS(datasource_name); |
1028 | 0 | msGMLFreeItems(item_list); |
1029 | 0 | CSLDestroy(layer_options); |
1030 | 0 | return status; |
1031 | 0 | } |
1032 | 0 | } |
1033 | 0 | } |
1034 | | |
1035 | 0 | msInitShape(&resultshape); |
1036 | | |
1037 | | /* -------------------------------------------------------------------- */ |
1038 | | /* Loop over all the shapes in the resultcache. */ |
1039 | | /* -------------------------------------------------------------------- */ |
1040 | 0 | for (i = 0; i < layer->resultcache->numresults; i++) { |
1041 | |
|
1042 | 0 | msFreeShape(&resultshape); /* init too */ |
1043 | | |
1044 | | /* |
1045 | | ** Read the shape. |
1046 | | */ |
1047 | 0 | bool shapeFromCache = false; |
1048 | 0 | if (layer->resultcache->results[i].shape) { |
1049 | | /* msDebug("Using cached shape %ld\n", |
1050 | | * layer->resultcache->results[i].shapeindex); */ |
1051 | 0 | shapeFromCache = true; |
1052 | 0 | status = |
1053 | 0 | msCopyShape(layer->resultcache->results[i].shape, &resultshape); |
1054 | 0 | } else { |
1055 | 0 | status = msLayerGetShape(layer, &resultshape, |
1056 | 0 | &(layer->resultcache->results[i])); |
1057 | 0 | } |
1058 | |
|
1059 | 0 | if (status != MS_SUCCESS) { |
1060 | 0 | OGR_DS_Destroy(hDS); |
1061 | 0 | msOGRCleanupDS(datasource_name); |
1062 | 0 | msGMLFreeItems(item_list); |
1063 | 0 | msFreeShape(&resultshape); |
1064 | 0 | CSLDestroy(layer_options); |
1065 | 0 | return status; |
1066 | 0 | } |
1067 | | |
1068 | | /* |
1069 | | ** Perform classification, and some annotation related magic. |
1070 | | */ |
1071 | 0 | resultshape.classindex = |
1072 | 0 | msShapeGetClass(layer, map, &resultshape, NULL, -1); |
1073 | |
|
1074 | 0 | if (resultshape.classindex >= 0 && |
1075 | 0 | (layer->_class[resultshape.classindex]->text.string || |
1076 | 0 | layer->labelitem) && |
1077 | 0 | layer->_class[resultshape.classindex]->numlabels > 0 && |
1078 | 0 | layer->_class[resultshape.classindex]->labels[0]->size != -1) { |
1079 | 0 | resultshape.text = msShapeGetLabelAnnotation( |
1080 | 0 | layer, &resultshape, |
1081 | 0 | layer->_class[resultshape.classindex]->labels[0]); |
1082 | 0 | } |
1083 | | |
1084 | | /* |
1085 | | ** prepare any necessary JOINs here (one-to-one only) |
1086 | | */ |
1087 | 0 | if (layer->numjoins > 0) { |
1088 | 0 | int j; |
1089 | |
|
1090 | 0 | for (j = 0; j < layer->numjoins; j++) { |
1091 | 0 | if (layer->joins[j].type == MS_JOIN_ONE_TO_ONE) { |
1092 | 0 | msJoinPrepare(&(layer->joins[j]), &resultshape); |
1093 | 0 | msJoinNext(&(layer->joins[j])); /* fetch the first row */ |
1094 | 0 | } |
1095 | 0 | } |
1096 | 0 | } |
1097 | |
|
1098 | 0 | if (layer->project && resultshape.type != MS_SHAPE_NULL && |
1099 | 0 | shapeFromCache != true) { |
1100 | 0 | reprojectionObj *reprojector = |
1101 | 0 | msLayerGetReprojectorToMap(layer, layer->map); |
1102 | 0 | if (reprojector) |
1103 | 0 | status = msProjectShapeEx(reprojector, &resultshape); |
1104 | 0 | else |
1105 | 0 | status = MS_FAILURE; |
1106 | 0 | } |
1107 | | |
1108 | | /* |
1109 | | ** Write out the feature to OGR. |
1110 | | */ |
1111 | |
|
1112 | 0 | if (status == MS_SUCCESS) |
1113 | 0 | status = |
1114 | 0 | msOGRWriteShape(hOGRLayer, &resultshape, item_list, |
1115 | 0 | nFirstOGRFieldIndex, pszFeatureid, geomTypeForced); |
1116 | |
|
1117 | 0 | if (status != MS_SUCCESS) { |
1118 | 0 | OGR_DS_Destroy(hDS); |
1119 | 0 | msOGRCleanupDS(datasource_name); |
1120 | 0 | msGMLFreeItems(item_list); |
1121 | 0 | msFreeShape(&resultshape); |
1122 | 0 | CSLDestroy(layer_options); |
1123 | 0 | return status; |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | 0 | msGMLFreeItems(item_list); |
1128 | 0 | msFreeShape(&resultshape); /* init too */ |
1129 | 0 | } |
1130 | | |
1131 | | /* -------------------------------------------------------------------- */ |
1132 | | /* Close the datasource. */ |
1133 | | /* -------------------------------------------------------------------- */ |
1134 | 0 | OGR_DS_Destroy(hDS); |
1135 | |
|
1136 | 0 | CSLDestroy(layer_options); |
1137 | | |
1138 | | /* -------------------------------------------------------------------- */ |
1139 | | /* Get list of resulting files. */ |
1140 | | /* -------------------------------------------------------------------- */ |
1141 | |
|
1142 | 0 | if (EQUAL(form, "simple")) { |
1143 | 0 | file_list = CSLAddString(NULL, datasource_name); |
1144 | 0 | } else { |
1145 | 0 | char datasource_path[MS_MAXPATHLEN]; |
1146 | |
|
1147 | 0 | if (bDataSourceNameIsRequestDir) |
1148 | 0 | file_list = msOGRRecursiveFileList(datasource_name); |
1149 | 0 | else { |
1150 | 0 | strncpy(datasource_path, CPLGetPath(datasource_name), MS_MAXPATHLEN - 1); |
1151 | 0 | file_list = msOGRRecursiveFileList(datasource_path); |
1152 | 0 | } |
1153 | 0 | } |
1154 | | |
1155 | | /* -------------------------------------------------------------------- */ |
1156 | | /* If our "storage" is stream then the output has already been */ |
1157 | | /* sent back to the client and we don't need to copy it now. */ |
1158 | | /* -------------------------------------------------------------------- */ |
1159 | 0 | if (EQUAL(storage, "stream")) { |
1160 | | /* already done */ |
1161 | 0 | } |
1162 | | |
1163 | | /* -------------------------------------------------------------------- */ |
1164 | | /* Handle case of simple file written to stdout. */ |
1165 | | /* -------------------------------------------------------------------- */ |
1166 | 0 | else if (EQUAL(form, "simple")) { |
1167 | 0 | char buffer[1024]; |
1168 | 0 | int bytes_read; |
1169 | 0 | VSILFILE *fp; |
1170 | 0 | const char *jsonp; |
1171 | |
|
1172 | 0 | jsonp = msGetOutputFormatOption(format, "JSONP", NULL); |
1173 | 0 | if (sendheaders) { |
1174 | 0 | if (!jsonp) |
1175 | 0 | msIO_setHeader("Content-Disposition", "attachment; filename=%s", |
1176 | 0 | CPLGetFilename(file_list[0])); |
1177 | 0 | if (format->mimetype) |
1178 | 0 | msIO_setHeader("Content-Type", "%s", format->mimetype); |
1179 | 0 | msIO_sendHeaders(); |
1180 | 0 | } else |
1181 | 0 | msIO_fprintf(stdout, "%c", 10); |
1182 | |
|
1183 | 0 | fp = VSIFOpenL(file_list[0], "r"); |
1184 | 0 | if (fp == NULL) { |
1185 | 0 | msSetError(MS_MISCERR, "Failed to open result file '%s'.", |
1186 | 0 | "msOGRWriteFromQuery()", file_list[0]); |
1187 | 0 | msOGRCleanupDS(datasource_name); |
1188 | 0 | return MS_FAILURE; |
1189 | 0 | } |
1190 | | |
1191 | 0 | if (jsonp != NULL) |
1192 | 0 | msIO_fprintf(stdout, "%s(", jsonp); |
1193 | |
|
1194 | 0 | while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fp)) > 0) |
1195 | 0 | msIO_fwrite(buffer, 1, bytes_read, stdout); |
1196 | 0 | VSIFCloseL(fp); |
1197 | |
|
1198 | 0 | if (jsonp != NULL) |
1199 | 0 | msIO_fprintf(stdout, ");\n"); |
1200 | 0 | } |
1201 | | |
1202 | | /* -------------------------------------------------------------------- */ |
1203 | | /* Handle the case of a multi-part result. */ |
1204 | | /* -------------------------------------------------------------------- */ |
1205 | 0 | else if (EQUAL(form, "multipart")) { |
1206 | 0 | char **papszAdditionalFiles; |
1207 | 0 | static const char *boundary = "xxOGRBoundaryxx"; |
1208 | 0 | msIO_setHeader("Content-Type", "multipart/mixed; boundary=%s", boundary); |
1209 | 0 | msIO_sendHeaders(); |
1210 | 0 | msIO_fprintf(stdout, "--%s\r\n", boundary); |
1211 | |
|
1212 | 0 | papszAdditionalFiles = msOGROutputGetAdditionalFiles(map); |
1213 | 0 | file_list = msCSLConcatenate(file_list, papszAdditionalFiles); |
1214 | 0 | CSLDestroy(papszAdditionalFiles); |
1215 | |
|
1216 | 0 | for (i = 0; file_list != NULL && file_list[i] != NULL; i++) { |
1217 | 0 | VSILFILE *fp; |
1218 | 0 | int bytes_read; |
1219 | 0 | char buffer[1024]; |
1220 | |
|
1221 | 0 | if (sendheaders) |
1222 | 0 | msIO_fprintf(stdout, |
1223 | 0 | "Content-Disposition: attachment; filename=%s\r\n" |
1224 | 0 | "Content-Type: application/binary\r\n" |
1225 | 0 | "Content-Transfer-Encoding: binary\r\n\r\n", |
1226 | 0 | CPLGetFilename(file_list[i])); |
1227 | |
|
1228 | 0 | fp = VSIFOpenL(file_list[i], "r"); |
1229 | 0 | if (fp == NULL) { |
1230 | 0 | msSetError(MS_MISCERR, "Failed to open result file '%s'.", |
1231 | 0 | "msOGRWriteFromQuery()", file_list[0]); |
1232 | 0 | msOGRCleanupDS(datasource_name); |
1233 | 0 | return MS_FAILURE; |
1234 | 0 | } |
1235 | | |
1236 | 0 | while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fp)) > 0) |
1237 | 0 | msIO_fwrite(buffer, 1, bytes_read, stdout); |
1238 | 0 | VSIFCloseL(fp); |
1239 | |
|
1240 | 0 | if (file_list[i + 1] == NULL) |
1241 | 0 | msIO_fprintf(stdout, "\r\n--%s--\r\n", boundary); |
1242 | 0 | else |
1243 | 0 | msIO_fprintf(stdout, "\r\n--%s\r\n", boundary); |
1244 | 0 | } |
1245 | 0 | } |
1246 | | |
1247 | | /* -------------------------------------------------------------------- */ |
1248 | | /* Handle the case of a zip file result. */ |
1249 | | /* -------------------------------------------------------------------- */ |
1250 | 0 | else if (EQUAL(form, "zip")) { |
1251 | 0 | VSILFILE *fp; |
1252 | 0 | char *zip_filename = msTmpFile(map, NULL, "/vsimem/ogrzip/", "zip"); |
1253 | 0 | void *hZip; |
1254 | 0 | int bytes_read; |
1255 | 0 | char buffer[1024]; |
1256 | 0 | char **papszAdditionalFiles; |
1257 | |
|
1258 | 0 | hZip = CPLCreateZip(zip_filename, NULL); |
1259 | |
|
1260 | 0 | papszAdditionalFiles = msOGROutputGetAdditionalFiles(map); |
1261 | 0 | file_list = msCSLConcatenate(file_list, papszAdditionalFiles); |
1262 | 0 | CSLDestroy(papszAdditionalFiles); |
1263 | |
|
1264 | 0 | const char *timestamp; |
1265 | 0 | timestamp = msGetOutputFormatOption(format, "TIMESTAMP", "NOW"); |
1266 | 0 | char **papszOptions = nullptr; |
1267 | |
|
1268 | 0 | for (i = 0; file_list != NULL && file_list[i] != NULL; i++) { |
1269 | |
|
1270 | 0 | const std::string osArchiveFilename(CPLGetFilename(file_list[i])); |
1271 | 0 | #if GDAL_VERSION_MAJOR > 3 || \ |
1272 | 0 | (GDAL_VERSION_MAJOR == 3 && GDAL_VERSION_MINOR >= 7) |
1273 | 0 | papszOptions = CSLAddNameValue(papszOptions, "TIMESTAMP", timestamp); |
1274 | 0 | if (CPLAddFileInZip(hZip, osArchiveFilename.c_str(), file_list[i], |
1275 | 0 | nullptr, papszOptions, nullptr, nullptr) != CE_None) { |
1276 | 0 | msSetError(MS_MISCERR, "CPLAddFileInZip() failed for %s", |
1277 | 0 | "msOGRWriteFromQuery()", file_list[i]); |
1278 | 0 | CPLCloseZip(hZip); |
1279 | 0 | CSLDestroy(papszOptions); |
1280 | 0 | msOGRCleanupDS(datasource_name); |
1281 | 0 | return MS_FAILURE; |
1282 | 0 | } |
1283 | | #else |
1284 | | if (CPLCreateFileInZip(hZip, osArchiveFilename.c_str(), NULL) != |
1285 | | CE_None) { |
1286 | | msSetError(MS_MISCERR, "CPLWriteFileInZip() failed for %s", |
1287 | | "msOGRWriteFromQuery()", file_list[i]); |
1288 | | CPLCloseZip(hZip); |
1289 | | msOGRCleanupDS(datasource_name); |
1290 | | return MS_FAILURE; |
1291 | | } |
1292 | | |
1293 | | fp = VSIFOpenL(file_list[i], "r"); |
1294 | | if (fp == NULL) { |
1295 | | CPLCloseZip(hZip); |
1296 | | msSetError(MS_MISCERR, "Failed to open result file '%s'.", |
1297 | | "msOGRWriteFromQuery()", file_list[i]); |
1298 | | msOGRCleanupDS(datasource_name); |
1299 | | return MS_FAILURE; |
1300 | | } |
1301 | | |
1302 | | while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fp)) > 0) { |
1303 | | if (CPLWriteFileInZip(hZip, buffer, bytes_read) != CE_None) { |
1304 | | msSetError(MS_MISCERR, "CPLWriteFileInZip() failed for %s", |
1305 | | "msOGRWriteFromQuery()", file_list[i]); |
1306 | | VSIFCloseL(fp); |
1307 | | CPLCloseFileInZip(hZip); |
1308 | | CPLCloseZip(hZip); |
1309 | | msOGRCleanupDS(datasource_name); |
1310 | | return MS_FAILURE; |
1311 | | } |
1312 | | } |
1313 | | VSIFCloseL(fp); |
1314 | | |
1315 | | CPLCloseFileInZip(hZip); |
1316 | | #endif |
1317 | 0 | } |
1318 | 0 | CPLCloseZip(hZip); |
1319 | 0 | CSLDestroy(papszOptions); |
1320 | |
|
1321 | 0 | if (sendheaders) { |
1322 | 0 | const char *zip_filename = fo_filename; |
1323 | | /* Make sure the filename is ended by .zip */ |
1324 | 0 | if (!EQUAL(CPLGetExtension(zip_filename), "zip") && |
1325 | 0 | !EQUAL(CPLGetExtension(zip_filename), "kmz")) |
1326 | 0 | zip_filename = CPLFormFilename(NULL, fo_filename, "zip"); |
1327 | 0 | msIO_setHeader("Content-Disposition", "attachment; filename=%s", |
1328 | 0 | zip_filename); |
1329 | 0 | msIO_setHeader("Content-Type", "application/zip"); |
1330 | 0 | msIO_sendHeaders(); |
1331 | 0 | } |
1332 | |
|
1333 | 0 | fp = VSIFOpenL(zip_filename, "r"); |
1334 | 0 | if (fp == NULL) { |
1335 | 0 | msSetError(MS_MISCERR, "Failed to open zip file '%s'.", |
1336 | 0 | "msOGRWriteFromQuery()", file_list[0]); |
1337 | 0 | msOGRCleanupDS(datasource_name); |
1338 | 0 | return MS_FAILURE; |
1339 | 0 | } |
1340 | | |
1341 | 0 | while ((bytes_read = VSIFReadL(buffer, 1, sizeof(buffer), fp)) > 0) |
1342 | 0 | msIO_fwrite(buffer, 1, bytes_read, stdout); |
1343 | 0 | VSIFCloseL(fp); |
1344 | |
|
1345 | 0 | msFree(zip_filename); |
1346 | 0 | } |
1347 | | |
1348 | | /* -------------------------------------------------------------------- */ |
1349 | | /* Handle illegal form value. */ |
1350 | | /* -------------------------------------------------------------------- */ |
1351 | 0 | else { |
1352 | 0 | msSetError(MS_MISCERR, "Unsupported FORM=%s value.", |
1353 | 0 | "msOGRWriteFromQuery()", form); |
1354 | 0 | msOGRCleanupDS(datasource_name); |
1355 | 0 | return MS_FAILURE; |
1356 | 0 | } |
1357 | | |
1358 | 0 | msOGRCleanupDS(datasource_name); |
1359 | |
|
1360 | 0 | CSLDestroy(file_list); |
1361 | |
|
1362 | 0 | return MS_SUCCESS; |
1363 | 0 | } |
1364 | | |
1365 | | /************************************************************************/ |
1366 | | /* msPopulateRenderVTableOGR() */ |
1367 | | /************************************************************************/ |
1368 | | |
1369 | 0 | int msPopulateRendererVTableOGR(rendererVTableObj *renderer) { |
1370 | 0 | (void)renderer; |
1371 | | /* we aren't really a normal renderer so we leave everything default */ |
1372 | 0 | return MS_SUCCESS; |
1373 | 0 | } |