Coverage Report

Created: 2025-06-22 06:59

/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
}