/src/MapServer/src/mapflatgeobuf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implements support for FlatGeobuf access. |
6 | | * Authors: Björn Harrtell |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
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 |
22 | | * OR 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 | | #define NEED_IGNORE_RET_VAL |
31 | | |
32 | | #include <limits.h> |
33 | | #include <assert.h> |
34 | | #include "mapserver.h" |
35 | | #include "mapows.h" |
36 | | |
37 | | #include "flatgeobuf/flatgeobuf_c.h" |
38 | | |
39 | | #include <cpl_conv.h> |
40 | | #include <ogr_srs_api.h> |
41 | | |
42 | | static void msFGBPassThroughFieldDefinitions(layerObj *layer, |
43 | 0 | flatgeobuf_ctx *ctx) { |
44 | 0 | for (int i = 0; i < ctx->columns_len; i++) { |
45 | 0 | char item[255]; |
46 | 0 | char gml_width[32], gml_precision[32]; |
47 | 0 | const char *gml_type = NULL; |
48 | |
|
49 | 0 | const flatgeobuf_column *column = &(ctx->columns[i]); |
50 | 0 | strncpy(item, column->name, 255 - 1); |
51 | |
|
52 | 0 | gml_width[0] = '\0'; |
53 | 0 | gml_precision[0] = '\0'; |
54 | |
|
55 | 0 | switch (column->type) { |
56 | 0 | case flatgeobuf_column_type_byte: |
57 | 0 | case flatgeobuf_column_type_ubyte: |
58 | 0 | case flatgeobuf_column_type_bool: |
59 | 0 | case flatgeobuf_column_type_short: |
60 | 0 | case flatgeobuf_column_type_ushort: |
61 | 0 | case flatgeobuf_column_type_int: |
62 | 0 | case flatgeobuf_column_type_uint: |
63 | 0 | gml_type = "Integer"; |
64 | 0 | sprintf(gml_width, "%d", 4); |
65 | 0 | break; |
66 | 0 | case flatgeobuf_column_type_long: |
67 | 0 | case flatgeobuf_column_type_ulong: |
68 | 0 | gml_type = "Long"; |
69 | 0 | sprintf(gml_width, "%d", 8); |
70 | 0 | break; |
71 | 0 | case flatgeobuf_column_type_float: |
72 | 0 | case flatgeobuf_column_type_double: |
73 | 0 | gml_type = "Real"; |
74 | 0 | sprintf(gml_width, "%d", 8); |
75 | 0 | sprintf(gml_precision, "%d", 15); |
76 | 0 | break; |
77 | 0 | case flatgeobuf_column_type_string: |
78 | 0 | case flatgeobuf_column_type_json: |
79 | 0 | case flatgeobuf_column_type_datetime: |
80 | 0 | default: |
81 | 0 | gml_type = "Character"; |
82 | 0 | sprintf(gml_width, "%d", 4096); |
83 | 0 | break; |
84 | 0 | } |
85 | | |
86 | 0 | msUpdateGMLFieldMetadata(layer, item, gml_type, gml_width, gml_precision, |
87 | 0 | 0); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | void msFlatGeobufLayerFreeItemInfo(layerObj *layer) { |
92 | 0 | if (layer->iteminfo) { |
93 | 0 | free(layer->iteminfo); |
94 | 0 | layer->iteminfo = NULL; |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | 0 | int msFlatGeobufLayerInitItemInfo(layerObj *layer) { |
99 | 0 | if (!layer->layerinfo) { |
100 | 0 | msSetError(MS_FGBERR, "FlatGeobuf layer has not been opened.", |
101 | 0 | "msFlatGeobufLayerInitItemInfo()"); |
102 | 0 | return MS_FAILURE; |
103 | 0 | } |
104 | | |
105 | 0 | msFlatGeobufLayerFreeItemInfo(layer); |
106 | |
|
107 | 0 | flatgeobuf_ctx *ctx; |
108 | 0 | ctx = layer->layerinfo; |
109 | 0 | if (!ctx) |
110 | 0 | return MS_FAILURE; |
111 | | |
112 | 0 | for (int j = 0; j < ctx->columns_len; j++) { |
113 | 0 | ctx->columns[j].itemindex = -1; |
114 | 0 | for (int i = 0; i < layer->numitems; i++) { |
115 | 0 | if (strcasecmp(layer->items[i], ctx->columns[j].name) == 0) { |
116 | 0 | ctx->columns[j].itemindex = i; |
117 | 0 | break; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | |
|
122 | 0 | return MS_SUCCESS; |
123 | 0 | } |
124 | | |
125 | 0 | int msFlatGeobufLayerOpen(layerObj *layer) { |
126 | 0 | char szPath[MS_MAXPATHLEN]; |
127 | 0 | int ret; |
128 | |
|
129 | 0 | if (layer->layerinfo) |
130 | 0 | return MS_SUCCESS; |
131 | | |
132 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
133 | 0 | return MS_FAILURE; |
134 | | |
135 | 0 | flatgeobuf_ctx *ctx = flatgeobuf_init_ctx(); |
136 | 0 | layer->layerinfo = ctx; |
137 | |
|
138 | 0 | ctx->file = |
139 | 0 | VSIFOpenL(msBuildPath(szPath, layer->map->mappath, layer->data), "rb"); |
140 | 0 | if (!ctx->file) |
141 | 0 | ctx->file = VSIFOpenL(msBuildPath3(szPath, layer->map->mappath, |
142 | 0 | layer->map->shapepath, layer->data), |
143 | 0 | "rb"); |
144 | 0 | if (!ctx->file) { |
145 | 0 | layer->layerinfo = NULL; |
146 | 0 | flatgeobuf_free_ctx(ctx); |
147 | 0 | return MS_FAILURE; |
148 | 0 | } |
149 | | |
150 | 0 | ret = flatgeobuf_check_magicbytes(ctx); |
151 | 0 | if (ret == -1) { |
152 | 0 | layer->layerinfo = NULL; |
153 | 0 | flatgeobuf_free_ctx(ctx); |
154 | 0 | return MS_FAILURE; |
155 | 0 | } |
156 | | |
157 | 0 | ret = flatgeobuf_decode_header(ctx); |
158 | 0 | if (ret == -1) { |
159 | 0 | layer->layerinfo = NULL; |
160 | 0 | flatgeobuf_free_ctx(ctx); |
161 | 0 | return MS_FAILURE; |
162 | 0 | } |
163 | | |
164 | 0 | if (layer->projection.numargs > 0 && |
165 | 0 | EQUAL(layer->projection.args[0], "auto")) { |
166 | 0 | OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL); |
167 | 0 | char *pszWKT = NULL; |
168 | 0 | if (ctx->srid > 0) { |
169 | 0 | if (OSRImportFromEPSG(hSRS, ctx->srid) != OGRERR_NONE) { |
170 | 0 | OSRDestroySpatialReference(hSRS); |
171 | 0 | flatgeobuf_free_ctx(ctx); |
172 | 0 | return MS_FAILURE; |
173 | 0 | } |
174 | 0 | if (OSRExportToWkt(hSRS, &pszWKT) != OGRERR_NONE) { |
175 | 0 | OSRDestroySpatialReference(hSRS); |
176 | 0 | flatgeobuf_free_ctx(ctx); |
177 | 0 | return MS_FAILURE; |
178 | 0 | } |
179 | 0 | } else if (ctx->wkt == NULL) { |
180 | 0 | OSRDestroySpatialReference(hSRS); |
181 | 0 | return MS_SUCCESS; |
182 | 0 | } |
183 | 0 | int bOK = MS_FALSE; |
184 | 0 | if (msOGCWKT2ProjectionObj(ctx->wkt ? ctx->wkt : pszWKT, |
185 | 0 | &(layer->projection), |
186 | 0 | layer->debug) == MS_SUCCESS) |
187 | 0 | bOK = MS_TRUE; |
188 | 0 | CPLFree(pszWKT); |
189 | 0 | OSRDestroySpatialReference(hSRS); |
190 | 0 | if (bOK != MS_TRUE) |
191 | 0 | if (layer->debug || layer->map->debug) |
192 | 0 | msDebug("Unable to get SRS from FlatGeobuf '%s' for layer '%s'.\n", |
193 | 0 | szPath, layer->name); |
194 | 0 | } |
195 | | |
196 | 0 | return MS_SUCCESS; |
197 | 0 | } |
198 | | |
199 | 0 | int msFlatGeobufLayerIsOpen(layerObj *layer) { |
200 | 0 | if (layer->layerinfo) |
201 | 0 | return MS_TRUE; |
202 | 0 | else |
203 | 0 | return MS_FALSE; |
204 | 0 | } |
205 | | |
206 | 0 | int msFlatGeobufLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery) { |
207 | 0 | (void)isQuery; |
208 | 0 | flatgeobuf_ctx *ctx; |
209 | 0 | ctx = layer->layerinfo; |
210 | 0 | if (!ctx) |
211 | 0 | return MS_FAILURE; |
212 | | |
213 | 0 | if (!ctx->has_extent || !ctx->index_node_size) |
214 | 0 | return MS_SUCCESS; |
215 | | |
216 | 0 | if (msRectOverlap(&ctx->bounds, &rect) != MS_TRUE) |
217 | 0 | return MS_DONE; |
218 | | |
219 | 0 | if (msRectContained(&ctx->bounds, &rect) == MS_FALSE && |
220 | 0 | ctx->index_node_size > 0) { |
221 | 0 | flatgeobuf_index_search(ctx, &rect); |
222 | 0 | if (ctx->search_result_len == 0) |
223 | 0 | return MS_DONE; |
224 | 0 | } else { |
225 | 0 | flatgeobuf_index_skip(ctx); |
226 | 0 | } |
227 | | |
228 | 0 | return MS_SUCCESS; |
229 | 0 | } |
230 | | |
231 | 0 | int msFlatGeobufLayerNextShape(layerObj *layer, shapeObj *shape) { |
232 | 0 | flatgeobuf_ctx *ctx; |
233 | 0 | ctx = layer->layerinfo; |
234 | 0 | if (!ctx) |
235 | 0 | return MS_FAILURE; |
236 | | |
237 | 0 | do { |
238 | 0 | if (ctx->search_result) { |
239 | 0 | if (ctx->search_index >= ctx->search_result_len) |
240 | 0 | return MS_DONE; |
241 | 0 | flatgeobuf_search_item item = ctx->search_result[ctx->search_index]; |
242 | 0 | if (VSIFSeekL(ctx->file, ctx->feature_offset + item.offset, SEEK_SET) == |
243 | 0 | -1) { |
244 | 0 | msSetError(MS_FGBERR, "Unable to seek in file", |
245 | 0 | "msFlatGeobufLayerNextShape"); |
246 | 0 | return MS_FAILURE; |
247 | 0 | } |
248 | 0 | ctx->offset = ctx->feature_offset + item.offset; |
249 | 0 | ctx->search_index++; |
250 | 0 | ctx->feature_index = item.index; |
251 | 0 | } |
252 | 0 | int ret = flatgeobuf_decode_feature(ctx, layer, shape); |
253 | 0 | if (ret == -1) |
254 | 0 | return MS_FAILURE; |
255 | 0 | shape->index = ctx->feature_index; |
256 | 0 | if (!ctx->search_result) |
257 | 0 | ctx->feature_index++; |
258 | 0 | if (ctx->done) |
259 | 0 | return MS_DONE; |
260 | 0 | if (ctx->is_null_geom) { |
261 | 0 | msFreeCharArray(shape->values, shape->numvalues); |
262 | 0 | shape->values = NULL; |
263 | 0 | } |
264 | 0 | } while (ctx->is_null_geom); |
265 | | |
266 | 0 | return MS_SUCCESS; |
267 | 0 | } |
268 | | |
269 | | int msFlatGeobufLayerGetShape(layerObj *layer, shapeObj *shape, |
270 | 0 | resultObj *record) { |
271 | |
|
272 | 0 | (void)shape; |
273 | 0 | (void)record; |
274 | 0 | flatgeobuf_ctx *ctx; |
275 | 0 | ctx = layer->layerinfo; |
276 | 0 | if (!ctx) |
277 | 0 | return MS_FAILURE; |
278 | 0 | long i = record->shapeindex; |
279 | 0 | if (i < 0 || (uint64_t)i >= ctx->features_count) { |
280 | 0 | msSetError(MS_MISCERR, "Invalid feature id", "msFlatGeobufLayerGetShape"); |
281 | 0 | return MS_FAILURE; |
282 | 0 | } |
283 | 0 | uint64_t offset; |
284 | 0 | flatgeobuf_read_feature_offset(ctx, i, &offset); |
285 | 0 | if (VSIFSeekL(ctx->file, ctx->feature_offset + offset, SEEK_SET) == -1) { |
286 | 0 | msSetError(MS_FGBERR, "Unable to seek in file", |
287 | 0 | "msFlatGeobufLayerGetShape"); |
288 | 0 | return MS_FAILURE; |
289 | 0 | } |
290 | 0 | int ret = flatgeobuf_decode_feature(ctx, layer, shape); |
291 | 0 | if (ret == -1) |
292 | 0 | return MS_FAILURE; |
293 | 0 | return MS_SUCCESS; |
294 | 0 | } |
295 | | |
296 | 0 | int msFlatGeobufLayerClose(layerObj *layer) { |
297 | 0 | flatgeobuf_ctx *ctx; |
298 | 0 | ctx = layer->layerinfo; |
299 | 0 | if (!ctx) |
300 | 0 | return MS_SUCCESS; |
301 | 0 | VSIFCloseL(ctx->file); |
302 | 0 | flatgeobuf_free_ctx(ctx); |
303 | 0 | layer->layerinfo = NULL; |
304 | 0 | return MS_SUCCESS; |
305 | 0 | } |
306 | | |
307 | 0 | int msFlatGeobufLayerGetItems(layerObj *layer) { |
308 | 0 | const char *value; |
309 | 0 | flatgeobuf_ctx *ctx; |
310 | 0 | ctx = layer->layerinfo; |
311 | 0 | if (!ctx) |
312 | 0 | return MS_FAILURE; |
313 | 0 | layer->numitems = ctx->columns_len; |
314 | |
|
315 | 0 | char **items = (char **)malloc(sizeof(char *) * ctx->columns_len); |
316 | 0 | for (int i = 0; i < ctx->columns_len; i++) |
317 | 0 | items[i] = msStrdup(ctx->columns[i].name); |
318 | 0 | layer->items = items; |
319 | |
|
320 | 0 | if ((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL && |
321 | 0 | strcasecmp(value, "auto") == 0) |
322 | 0 | msFGBPassThroughFieldDefinitions(layer, ctx); |
323 | |
|
324 | 0 | return msLayerInitItemInfo(layer); |
325 | 0 | } |
326 | | |
327 | 0 | int msFlatGeobufLayerGetExtent(layerObj *layer, rectObj *extent) { |
328 | 0 | flatgeobuf_ctx *ctx; |
329 | 0 | ctx = layer->layerinfo; |
330 | 0 | extent->minx = ctx->xmin; |
331 | 0 | extent->miny = ctx->ymin; |
332 | 0 | extent->maxx = ctx->xmax; |
333 | 0 | extent->maxy = ctx->ymax; |
334 | 0 | return MS_SUCCESS; |
335 | 0 | } |
336 | | |
337 | 0 | int msFlatGeobufLayerSupportsCommonFilters(layerObj *layer) { |
338 | 0 | (void)layer; |
339 | 0 | return MS_TRUE; |
340 | 0 | } |
341 | | |
342 | 0 | int msFlatGeobufLayerInitializeVirtualTable(layerObj *layer) { |
343 | 0 | assert(layer != NULL); |
344 | 0 | assert(layer->vtable != NULL); |
345 | | |
346 | 0 | layer->vtable->LayerSupportsCommonFilters = |
347 | 0 | msFlatGeobufLayerSupportsCommonFilters; |
348 | 0 | layer->vtable->LayerInitItemInfo = msFlatGeobufLayerInitItemInfo; |
349 | 0 | layer->vtable->LayerFreeItemInfo = msFlatGeobufLayerFreeItemInfo; |
350 | 0 | layer->vtable->LayerOpen = msFlatGeobufLayerOpen; |
351 | 0 | layer->vtable->LayerIsOpen = msFlatGeobufLayerIsOpen; |
352 | 0 | layer->vtable->LayerWhichShapes = msFlatGeobufLayerWhichShapes; |
353 | 0 | layer->vtable->LayerNextShape = msFlatGeobufLayerNextShape; |
354 | 0 | layer->vtable->LayerGetShape = msFlatGeobufLayerGetShape; |
355 | | /* layer->vtable->LayerGetShapeCount, use default */ |
356 | 0 | layer->vtable->LayerClose = msFlatGeobufLayerClose; |
357 | 0 | layer->vtable->LayerGetItems = msFlatGeobufLayerGetItems; |
358 | 0 | layer->vtable->LayerGetExtent = msFlatGeobufLayerGetExtent; |
359 | | /* layer->vtable->LayerGetAutoStyle, use default */ |
360 | | /* layer->vtable->LayerCloseConnection, use default */ |
361 | 0 | layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter; |
362 | | /* layer->vtable->LayerTranslateFilter, use default */ |
363 | | /* layer->vtable->LayerApplyFilterToLayer, use default */ |
364 | | /* layer->vtable->LayerCreateItems, use default */ |
365 | | /* layer->vtable->LayerGetNumFeatures, use default */ |
366 | |
|
367 | 0 | return MS_SUCCESS; |
368 | 0 | } |