/src/MapServer/src/mapunion.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implementation of the union layer data provider (RFC-68). |
6 | | * Author: Tamas Szekeres (szekerest@gmail.com). |
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 _CRT_SECURE_NO_WARNINGS 1 |
31 | | |
32 | | /* $Id$ */ |
33 | | #include <assert.h> |
34 | | #include "mapserver.h" |
35 | | |
36 | | #define MSUNION_NUMITEMS 3 |
37 | 0 | #define MSUNION_SOURCELAYERNAME "Union_SourceLayerName" |
38 | 0 | #define MSUNION_SOURCELAYERNAMEINDEX -100 |
39 | 0 | #define MSUNION_SOURCELAYERGROUP "Union_SourceLayerGroup" |
40 | 0 | #define MSUNION_SOURCELAYERGROUPINDEX -101 |
41 | | #define MSUNION_SOURCELAYERVISIBLE "Union_SourceLayerVisible" |
42 | 0 | #define MSUNION_SOURCELAYERVISIBLEINDEX -102 |
43 | | |
44 | | typedef struct { |
45 | | int layerIndex; /* current source layer index */ |
46 | | int classIndex; /* current class index */ |
47 | | char *classText; /* current class text (autostyle) */ |
48 | | int layerCount; /* number of the source layers */ |
49 | | layerObj *layers; /* structure to the source layers */ |
50 | | int *status; /* the layer status */ |
51 | | int *classgroup; /* current array of the valid classes */ |
52 | | int nclasses; /* number of the valid classes */ |
53 | | reprojectionObj *reprojectorSrcLayerToLayer; |
54 | | int reprojectorCurSrcLayer; |
55 | | } msUnionLayerInfo; |
56 | | |
57 | | /* Close the the combined layer */ |
58 | 0 | int msUnionLayerClose(layerObj *const layer) { |
59 | 0 | int i; |
60 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
61 | |
|
62 | 0 | if (!layerinfo) |
63 | 0 | return MS_SUCCESS; |
64 | | |
65 | 0 | if (!layer->map) |
66 | 0 | return MS_FAILURE; |
67 | | |
68 | 0 | msProjectDestroyReprojector(layerinfo->reprojectorSrcLayerToLayer); |
69 | 0 | for (i = 0; i < layerinfo->layerCount; i++) { |
70 | 0 | layerObj *const srclayer = &layerinfo->layers[i]; |
71 | 0 | msLayerClose(srclayer); |
72 | 0 | freeLayer(srclayer); |
73 | 0 | } |
74 | 0 | msFree(layerinfo->layers); |
75 | 0 | msFree(layerinfo->status); |
76 | 0 | msFree(layerinfo->classgroup); |
77 | 0 | msFree(layerinfo->classText); |
78 | 0 | msFree(layerinfo); |
79 | 0 | layer->layerinfo = NULL; |
80 | |
|
81 | 0 | return MS_SUCCESS; |
82 | 0 | } |
83 | | |
84 | 0 | int isScaleInRange(mapObj *map, layerObj *const layer) { |
85 | 0 | if (map->scaledenom > 0) { |
86 | 0 | int i; |
87 | | /* layer scale boundaries should be checked first */ |
88 | 0 | if ((layer->maxscaledenom > 0) && (map->scaledenom > layer->maxscaledenom)) |
89 | 0 | return MS_FALSE; |
90 | | |
91 | 0 | if ((layer->minscaledenom > 0) && (map->scaledenom <= layer->minscaledenom)) |
92 | 0 | return MS_FALSE; |
93 | | |
94 | | /* now check class scale boundaries (all layers *must* pass these tests) */ |
95 | 0 | if (layer->numclasses > 0) { |
96 | 0 | for (i = 0; i < layer->numclasses; i++) { |
97 | 0 | if ((layer->_class[i]->maxscaledenom > 0) && |
98 | 0 | (map->scaledenom > layer->_class[i]->maxscaledenom)) |
99 | 0 | continue; /* can skip this one, next class */ |
100 | 0 | if ((layer->_class[i]->minscaledenom > 0) && |
101 | 0 | (map->scaledenom <= layer->_class[i]->minscaledenom)) |
102 | 0 | continue; /* can skip this one, next class */ |
103 | | |
104 | 0 | break; /* can't skip this class (or layer for that matter) */ |
105 | 0 | } |
106 | 0 | if (i == layer->numclasses) |
107 | 0 | return MS_FALSE; |
108 | 0 | } |
109 | | |
110 | 0 | if (layer->maxscaledenom <= 0 && layer->minscaledenom <= 0) { |
111 | 0 | if ((layer->maxgeowidth > 0) && |
112 | 0 | ((map->extent.maxx - map->extent.minx) > layer->maxgeowidth)) |
113 | 0 | return MS_FALSE; |
114 | | |
115 | 0 | if ((layer->mingeowidth > 0) && |
116 | 0 | ((map->extent.maxx - map->extent.minx) < layer->mingeowidth)) |
117 | 0 | return MS_FALSE; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | return MS_TRUE; |
121 | 0 | } |
122 | | |
123 | 0 | int msUnionLayerOpen(layerObj *const layer) { |
124 | 0 | if (layer->layerinfo != NULL) { |
125 | 0 | return MS_SUCCESS; /* Nothing to do... layer is already opened */ |
126 | 0 | } |
127 | | |
128 | 0 | if (!layer->connection) { |
129 | 0 | msSetError(MS_MISCERR, |
130 | 0 | "The CONNECTION option is not specified for layer: %s", |
131 | 0 | "msUnionLayerOpen()", layer->name); |
132 | 0 | return MS_FAILURE; |
133 | 0 | } |
134 | | |
135 | 0 | if (!layer->map) { |
136 | 0 | msSetError(MS_MISCERR, "No map assigned to this layer: %s", |
137 | 0 | "msUnionLayerOpen()", layer->name); |
138 | 0 | return MS_FAILURE; |
139 | 0 | } |
140 | | |
141 | 0 | mapObj *map = layer->map; |
142 | |
|
143 | 0 | msUnionLayerInfo *layerinfo = |
144 | 0 | (msUnionLayerInfo *)calloc(1, sizeof(msUnionLayerInfo)); |
145 | 0 | MS_CHECK_ALLOC(layerinfo, sizeof(msUnionLayerInfo), MS_FAILURE); |
146 | |
|
147 | 0 | layer->layerinfo = layerinfo; |
148 | 0 | layerinfo->layerIndex = 0; |
149 | |
|
150 | 0 | layerinfo->classgroup = NULL; |
151 | 0 | layerinfo->nclasses = 0; |
152 | |
|
153 | 0 | layerinfo->layerCount = 0; |
154 | |
|
155 | 0 | layerinfo->classText = NULL; |
156 | 0 | layerinfo->reprojectorCurSrcLayer = -1; |
157 | |
|
158 | 0 | const char *pkey = msLayerGetProcessingKey(layer, "UNION_STATUS_CHECK"); |
159 | 0 | const bool status_check = (pkey && strcasecmp(pkey, "true") == 0); |
160 | |
|
161 | 0 | pkey = msLayerGetProcessingKey(layer, "UNION_SCALE_CHECK"); |
162 | 0 | const bool scale_check = !(pkey && strcasecmp(pkey, "false") == 0); |
163 | |
|
164 | 0 | pkey = msLayerGetProcessingKey(layer, "UNION_SRCLAYER_CLOSE_CONNECTION"); |
165 | |
|
166 | 0 | const auto layerNames = msStringSplit(layer->connection, ','); |
167 | |
|
168 | 0 | if (layerNames.empty()) { |
169 | 0 | msSetError(MS_MISCERR, "No source layers specified in layer: %s", |
170 | 0 | "msUnionLayerOpen()", layer->name); |
171 | 0 | msUnionLayerClose(layer); |
172 | 0 | return MS_FAILURE; |
173 | 0 | } |
174 | | |
175 | 0 | const int layerCount = static_cast<int>(layerNames.size()); |
176 | 0 | layerinfo->layers = (layerObj *)malloc(layerCount * sizeof(layerObj)); |
177 | 0 | MS_CHECK_ALLOC(layerinfo->layers, layerCount * sizeof(layerObj), MS_FAILURE); |
178 | |
|
179 | 0 | layerinfo->status = (int *)malloc(layerCount * sizeof(int)); |
180 | 0 | MS_CHECK_ALLOC(layerinfo->status, layerCount * sizeof(int), MS_FAILURE); |
181 | |
|
182 | 0 | for (int i = 0; i < layerCount; i++) { |
183 | 0 | const char *layerName = layerNames[i].c_str(); |
184 | 0 | const int layerindex = msGetLayerIndex(map, layerName); |
185 | 0 | if (layerindex >= 0 && layerindex < map->numlayers) { |
186 | 0 | const layerObj *const srclayer = map->layers[layerindex]; |
187 | |
|
188 | 0 | if (srclayer->type != layer->type) { |
189 | 0 | msSetError(MS_MISCERR, |
190 | 0 | "The type of the source layer doesn't match with the union " |
191 | 0 | "layer: %s", |
192 | 0 | "msUnionLayerOpen()", srclayer->name); |
193 | 0 | msUnionLayerClose(layer); |
194 | 0 | return MS_FAILURE; |
195 | 0 | } |
196 | | |
197 | | /* we need to create a new layer in order make the single pass query to |
198 | | * work */ |
199 | 0 | layerObj *dstlayer = &layerinfo->layers[i]; |
200 | 0 | if (initLayer(dstlayer, map) == -1) { |
201 | 0 | msSetError(MS_MISCERR, "Cannot initialize source layer: %s", |
202 | 0 | "msUnionLayerOpen()", srclayer->name); |
203 | 0 | msUnionLayerClose(layer); |
204 | 0 | return MS_FAILURE; |
205 | 0 | } |
206 | | |
207 | 0 | ++layerinfo->layerCount; |
208 | |
|
209 | 0 | if (msCopyLayer(dstlayer, srclayer) != MS_SUCCESS) { |
210 | 0 | msSetError(MS_MISCERR, "Cannot copy source layer: %s", |
211 | 0 | "msUnionLayerOpen()", srclayer->name); |
212 | 0 | msUnionLayerClose(layer); |
213 | 0 | return MS_FAILURE; |
214 | 0 | } |
215 | | |
216 | 0 | if (pkey) { |
217 | | /* override connection flag */ |
218 | 0 | msLayerSetProcessingKey(dstlayer, "CLOSE_CONNECTION", pkey); |
219 | 0 | } |
220 | | |
221 | | /* check is we should skip this source (status check) */ |
222 | 0 | if (status_check && dstlayer->status == MS_OFF) { |
223 | 0 | layerinfo->status[i] = MS_DONE; |
224 | 0 | continue; |
225 | 0 | } |
226 | | |
227 | | /* check is we should skip this source (scale check) */ |
228 | 0 | if (scale_check && isScaleInRange(map, dstlayer) == MS_FALSE) { |
229 | 0 | layerinfo->status[i] = MS_DONE; |
230 | 0 | continue; |
231 | 0 | } |
232 | | |
233 | 0 | layerinfo->status[i] = msLayerOpen(dstlayer); |
234 | 0 | if (layerinfo->status[i] != MS_SUCCESS) { |
235 | 0 | msUnionLayerClose(layer); |
236 | 0 | return MS_FAILURE; |
237 | 0 | } |
238 | 0 | } else { |
239 | 0 | msSetError(MS_MISCERR, "Invalid layer: %s", "msUnionLayerOpen()", |
240 | 0 | layerName); |
241 | 0 | msUnionLayerClose(layer); |
242 | 0 | return MS_FAILURE; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | 0 | return MS_SUCCESS; |
247 | 0 | } |
248 | | |
249 | | /* Return MS_TRUE if layer is open, MS_FALSE otherwise. */ |
250 | 0 | int msUnionLayerIsOpen(layerObj *const layer) { |
251 | 0 | if (layer->layerinfo) |
252 | 0 | return (MS_TRUE); |
253 | 0 | else |
254 | 0 | return (MS_FALSE); |
255 | 0 | } |
256 | | |
257 | | /* Free the itemindexes array in a layer. */ |
258 | 0 | void msUnionLayerFreeItemInfo(layerObj *const layer) { |
259 | 0 | int i; |
260 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
261 | |
|
262 | 0 | if (!layerinfo || !layer->map) |
263 | 0 | return; |
264 | | |
265 | 0 | msFree(layer->iteminfo); |
266 | |
|
267 | 0 | layer->iteminfo = NULL; |
268 | |
|
269 | 0 | for (i = 0; i < layerinfo->layerCount; i++) { |
270 | 0 | msLayerFreeItemInfo(&(layerinfo->layers[i])); |
271 | 0 | if (layerinfo->layers[i].items) { |
272 | | /* need to remove the source layer items */ |
273 | 0 | msFreeCharArray(layerinfo->layers[i].items, |
274 | 0 | layerinfo->layers[i].numitems); |
275 | 0 | layerinfo->layers[i].items = NULL; |
276 | 0 | layerinfo->layers[i].numitems = 0; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | | /* clean up expression tokens */ |
282 | 0 | void msUnionLayerFreeExpressionTokens(layerObj *const layer) { |
283 | 0 | int i, j; |
284 | 0 | msFreeExpressionTokens(&(layer->filter)); |
285 | 0 | msFreeExpressionTokens(&(layer->cluster.group)); |
286 | 0 | msFreeExpressionTokens(&(layer->cluster.filter)); |
287 | 0 | for (i = 0; i < layer->numclasses; i++) { |
288 | 0 | msFreeExpressionTokens(&(layer->_class[i]->expression)); |
289 | 0 | msFreeExpressionTokens(&(layer->_class[i]->text)); |
290 | 0 | for (j = 0; j < layer->_class[i]->numstyles; j++) |
291 | 0 | msFreeExpressionTokens(&(layer->_class[i]->styles[j]->_geomtransform)); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | /* allocate the iteminfo index array - same order as the item list */ |
296 | 0 | int msUnionLayerInitItemInfo(layerObj *const layer) { |
297 | 0 | int i, numitems; |
298 | 0 | int *itemindexes; |
299 | 0 | char *itemlist = NULL; |
300 | |
|
301 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
302 | |
|
303 | 0 | if (layer->numitems == 0) { |
304 | 0 | return MS_SUCCESS; |
305 | 0 | } |
306 | | |
307 | 0 | if (!layerinfo || !layer->map) |
308 | 0 | return MS_FAILURE; |
309 | | |
310 | | /* Cleanup any previous item selection */ |
311 | 0 | msUnionLayerFreeItemInfo(layer); |
312 | |
|
313 | 0 | layer->iteminfo = (int *)malloc(sizeof(int) * layer->numitems); |
314 | 0 | MS_CHECK_ALLOC(layer->iteminfo, sizeof(int) * layer->numitems, MS_FAILURE); |
315 | |
|
316 | 0 | itemindexes = (int *)layer->iteminfo; |
317 | | |
318 | | /* check whether we require attributes from the source layers also */ |
319 | 0 | numitems = 0; |
320 | 0 | for (i = 0; i < layer->numitems; i++) { |
321 | 0 | if (EQUAL(layer->items[i], MSUNION_SOURCELAYERNAME)) |
322 | 0 | itemindexes[i] = MSUNION_SOURCELAYERNAMEINDEX; |
323 | 0 | else if (EQUAL(layer->items[i], MSUNION_SOURCELAYERGROUP)) |
324 | 0 | itemindexes[i] = MSUNION_SOURCELAYERGROUPINDEX; |
325 | 0 | else if (EQUAL(layer->items[i], MSUNION_SOURCELAYERVISIBLE)) |
326 | 0 | itemindexes[i] = MSUNION_SOURCELAYERVISIBLEINDEX; |
327 | 0 | else { |
328 | 0 | itemindexes[i] = numitems++; |
329 | 0 | if (itemlist) { |
330 | 0 | itemlist = msStringConcatenate(itemlist, ","); |
331 | 0 | itemlist = msStringConcatenate(itemlist, layer->items[i]); |
332 | 0 | } else { |
333 | 0 | itemlist = msStrdup(layer->items[i]); |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | |
|
338 | 0 | for (i = 0; i < layerinfo->layerCount; i++) { |
339 | 0 | layerObj *const srclayer = &(layerinfo->layers[i]); |
340 | |
|
341 | 0 | if (layerinfo->status[i] != MS_SUCCESS) |
342 | 0 | continue; /* skip empty layers */ |
343 | | |
344 | 0 | msUnionLayerFreeExpressionTokens(srclayer); |
345 | |
|
346 | 0 | if (itemlist) { |
347 | | /* get items requested by the union layer plus the required items */ |
348 | 0 | msLayerSetProcessingKey(srclayer, "ITEMS", itemlist); |
349 | 0 | if (msLayerWhichItems(srclayer, MS_TRUE, NULL) != MS_SUCCESS) { |
350 | 0 | msFree(itemlist); |
351 | 0 | return MS_FAILURE; |
352 | 0 | } |
353 | 0 | } else { |
354 | | /* get only the required items */ |
355 | 0 | if (msLayerWhichItems(srclayer, MS_FALSE, NULL) != MS_SUCCESS) |
356 | 0 | return MS_FAILURE; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 0 | msFree(itemlist); |
361 | 0 | return MS_SUCCESS; |
362 | 0 | } |
363 | | |
364 | | // cppcheck-suppress passedByValue |
365 | 0 | int msUnionLayerWhichShapes(layerObj *const layer, rectObj rect, int isQuery) { |
366 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
367 | |
|
368 | 0 | if (!layerinfo || !layer->map) |
369 | 0 | return MS_FAILURE; |
370 | | |
371 | 0 | for (int i = 0; i < layerinfo->layerCount; i++) { |
372 | 0 | layerObj *const srclayer = &layerinfo->layers[i]; |
373 | |
|
374 | 0 | if (layerinfo->status[i] != MS_SUCCESS) |
375 | 0 | continue; /* skip empty layers */ |
376 | | |
377 | 0 | if (layer->styleitem && layer->numitems == 0) { |
378 | | /* need to initialize items */ |
379 | 0 | msUnionLayerFreeExpressionTokens(srclayer); |
380 | | |
381 | | /* get only the required items */ |
382 | 0 | if (msLayerWhichItems(srclayer, MS_FALSE, NULL) != MS_SUCCESS) |
383 | 0 | return MS_FAILURE; |
384 | 0 | } |
385 | | |
386 | 0 | rectObj srcRect = rect; |
387 | |
|
388 | 0 | if (srclayer->transform == MS_TRUE && srclayer->project && |
389 | 0 | layer->transform == MS_TRUE && layer->project && |
390 | 0 | msProjectionsDiffer(&(srclayer->projection), &(layer->projection))) |
391 | 0 | msProjectRect(&layer->projection, &srclayer->projection, |
392 | 0 | &srcRect); /* project the searchrect to source coords */ |
393 | |
|
394 | 0 | layerinfo->status[i] = msLayerWhichShapes(srclayer, srcRect, isQuery); |
395 | 0 | if (layerinfo->status[i] == MS_FAILURE) |
396 | 0 | return MS_FAILURE; |
397 | 0 | } |
398 | | |
399 | 0 | layerinfo->layerIndex = 0; |
400 | 0 | layerObj *const srclayer = &layerinfo->layers[0]; |
401 | |
|
402 | 0 | msFree(layerinfo->classgroup); |
403 | |
|
404 | 0 | layerinfo->classgroup = NULL; |
405 | 0 | layerinfo->nclasses = 0; |
406 | |
|
407 | 0 | if (srclayer->classgroup && srclayer->numclasses > 0) |
408 | 0 | layerinfo->classgroup = |
409 | 0 | msAllocateValidClassGroups(srclayer, &layerinfo->nclasses); |
410 | |
|
411 | 0 | return MS_SUCCESS; |
412 | 0 | } |
413 | | |
414 | | static int BuildFeatureAttributes(layerObj *const layer, |
415 | 0 | const layerObj *srclayer, shapeObj *shape) { |
416 | 0 | const int *const itemindexes = static_cast<const int *>(layer->iteminfo); |
417 | |
|
418 | 0 | char **values = |
419 | 0 | static_cast<char **>(msSmallMalloc(sizeof(char *) * (layer->numitems))); |
420 | 0 | MS_CHECK_ALLOC(values, layer->numitems * sizeof(char *), MS_FAILURE); |
421 | 0 | ; |
422 | |
|
423 | 0 | for (int i = 0; i < layer->numitems; i++) { |
424 | 0 | if (itemindexes[i] == MSUNION_SOURCELAYERNAMEINDEX) |
425 | 0 | values[i] = msStrdup(srclayer->name); |
426 | 0 | else if (itemindexes[i] == MSUNION_SOURCELAYERGROUPINDEX) |
427 | 0 | values[i] = msStrdup(srclayer->group); |
428 | 0 | else if (itemindexes[i] == MSUNION_SOURCELAYERVISIBLEINDEX) { |
429 | 0 | if (srclayer->status == MS_OFF) |
430 | 0 | values[i] = msStrdup("0"); |
431 | 0 | else |
432 | 0 | values[i] = msStrdup("1"); |
433 | 0 | } else if (shape->values[itemindexes[i]]) |
434 | 0 | values[i] = msStrdup(shape->values[itemindexes[i]]); |
435 | 0 | else |
436 | 0 | values[i] = msStrdup(""); |
437 | 0 | } |
438 | |
|
439 | 0 | if (shape->values) |
440 | 0 | msFreeCharArray(shape->values, shape->numvalues); |
441 | |
|
442 | 0 | shape->values = values; |
443 | 0 | shape->numvalues = layer->numitems; |
444 | |
|
445 | 0 | return MS_SUCCESS; |
446 | 0 | } |
447 | | |
448 | | /* find the next shape with the appropriate shape type */ |
449 | | /* also, load in the attribute data */ |
450 | | /* MS_DONE => no more data */ |
451 | 0 | int msUnionLayerNextShape(layerObj *const layer, shapeObj *shape) { |
452 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
453 | |
|
454 | 0 | if (!layerinfo || !layer->map) |
455 | 0 | return MS_FAILURE; |
456 | | |
457 | 0 | if (layerinfo->layerIndex < 0 || |
458 | 0 | layerinfo->layerIndex >= layerinfo->layerCount) |
459 | 0 | return MS_FAILURE; |
460 | | |
461 | 0 | int rv = MS_DONE; |
462 | |
|
463 | 0 | while (layerinfo->layerIndex < layerinfo->layerCount) { |
464 | 0 | layerObj *srclayer = &layerinfo->layers[layerinfo->layerIndex]; |
465 | 0 | if (layerinfo->status[layerinfo->layerIndex] == MS_SUCCESS) { |
466 | 0 | while ((rv = srclayer->vtable->LayerNextShape(srclayer, shape)) == |
467 | 0 | MS_SUCCESS) { |
468 | 0 | if (layer->styleitem) { |
469 | | /* need to retrieve the source layer classindex if styleitem AUTO is |
470 | | * set */ |
471 | 0 | layerinfo->classIndex = |
472 | 0 | msShapeGetClass(srclayer, layer->map, shape, |
473 | 0 | layerinfo->classgroup, layerinfo->nclasses); |
474 | 0 | if (layerinfo->classIndex < 0 || |
475 | 0 | layerinfo->classIndex >= srclayer->numclasses) { |
476 | | /* this shape is not visible, skip it */ |
477 | 0 | msFreeShape(shape); |
478 | 0 | continue; |
479 | 0 | } |
480 | 0 | if (srclayer->styleitem && |
481 | 0 | strcasecmp(srclayer->styleitem, "AUTO") != 0) { |
482 | | /* Generic feature style handling as per RFC-61 */ |
483 | 0 | msLayerGetFeatureStyle(layer->map, srclayer, |
484 | 0 | srclayer->_class[layerinfo->classIndex], |
485 | 0 | shape); |
486 | 0 | } |
487 | | /* set up annotation */ |
488 | 0 | msFree(layerinfo->classText); |
489 | 0 | layerinfo->classText = NULL; |
490 | 0 | if (srclayer->_class[layerinfo->classIndex]->numlabels > 0) { |
491 | | /* pull text from the first label only */ |
492 | 0 | if (msGetLabelStatus( |
493 | 0 | layer->map, layer, shape, |
494 | 0 | srclayer->_class[layerinfo->classIndex]->labels[0]) == |
495 | 0 | MS_ON) { |
496 | 0 | layerinfo->classText = msShapeGetLabelAnnotation( |
497 | 0 | layer, shape, |
498 | 0 | srclayer->_class[layerinfo->classIndex]->labels[0]); |
499 | 0 | } |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | | /* reproject to the target layer */ |
504 | 0 | if (layerinfo->reprojectorCurSrcLayer != layerinfo->layerIndex) { |
505 | 0 | msProjectDestroyReprojector(layerinfo->reprojectorSrcLayerToLayer); |
506 | 0 | layerinfo->reprojectorSrcLayerToLayer = NULL; |
507 | 0 | layerinfo->reprojectorCurSrcLayer = layerinfo->layerIndex; |
508 | 0 | if (srclayer->project && msProjectionsDiffer(&(srclayer->projection), |
509 | 0 | &(layer->projection))) |
510 | 0 | layerinfo->reprojectorSrcLayerToLayer = msProjectCreateReprojector( |
511 | 0 | &(srclayer->projection), &(layer->projection)); |
512 | 0 | else |
513 | 0 | srclayer->project = MS_FALSE; |
514 | 0 | } |
515 | 0 | if (layerinfo->reprojectorSrcLayerToLayer) |
516 | 0 | msProjectShapeEx(layerinfo->reprojectorSrcLayerToLayer, shape); |
517 | | |
518 | | /* update the layer styles with the bound values */ |
519 | 0 | if (msBindLayerToShape(srclayer, shape, MS_FALSE) != MS_SUCCESS) |
520 | 0 | return MS_FAILURE; |
521 | | |
522 | 0 | shape->tileindex = layerinfo->layerIndex; |
523 | | |
524 | | /* construct the item array */ |
525 | 0 | if (layer->iteminfo) |
526 | 0 | rv = BuildFeatureAttributes(layer, srclayer, shape); |
527 | | |
528 | | /* check the layer filter condition */ |
529 | 0 | if (layer->filter.string != NULL && layer->numitems > 0 && |
530 | 0 | layer->iteminfo) { |
531 | 0 | if (layer->filter.type == MS_EXPRESSION && |
532 | 0 | layer->filter.tokens == NULL) |
533 | 0 | msTokenizeExpression(&(layer->filter), layer->items, |
534 | 0 | &(layer->numitems)); |
535 | |
|
536 | 0 | if (!msEvalExpression(layer, shape, &(layer->filter), |
537 | 0 | layer->filteritemindex)) { |
538 | | /* this shape is filtered */ |
539 | 0 | msFreeShape(shape); |
540 | 0 | continue; |
541 | 0 | } |
542 | 0 | } |
543 | | |
544 | 0 | return rv; |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | 0 | ++layerinfo->layerIndex; |
549 | 0 | if (layerinfo->layerIndex == layerinfo->layerCount) { |
550 | 0 | layerinfo->layerIndex = 0; |
551 | 0 | return MS_DONE; |
552 | 0 | } |
553 | | |
554 | | /* allocate the classgroups for the next layer */ |
555 | 0 | msFree(layerinfo->classgroup); |
556 | |
|
557 | 0 | layerinfo->classgroup = NULL; |
558 | 0 | layerinfo->nclasses = 0; |
559 | |
|
560 | 0 | if (srclayer->classgroup && srclayer->numclasses > 0) |
561 | 0 | layerinfo->classgroup = |
562 | 0 | msAllocateValidClassGroups(srclayer, &layerinfo->nclasses); |
563 | 0 | } |
564 | | |
565 | 0 | return rv; |
566 | 0 | } |
567 | | |
568 | | /* Random access of the feature. */ |
569 | | int msUnionLayerGetShape(layerObj *const layer, shapeObj *shape, |
570 | 0 | resultObj *record) { |
571 | 0 | const long tile = record->tileindex; |
572 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
573 | |
|
574 | 0 | if (!layerinfo || !layer->map) |
575 | 0 | return MS_FAILURE; |
576 | | |
577 | 0 | if (tile < 0 || tile >= layerinfo->layerCount) { |
578 | 0 | msSetError(MS_MISCERR, "Invalid tile index: %s", "msUnionLayerGetShape()", |
579 | 0 | layer->name); |
580 | 0 | return MS_FAILURE; |
581 | 0 | } |
582 | | |
583 | 0 | layerObj *const srclayer = &layerinfo->layers[tile]; |
584 | 0 | record->tileindex = 0; |
585 | 0 | int rv = srclayer->vtable->LayerGetShape(srclayer, shape, record); |
586 | 0 | record->tileindex = tile; |
587 | |
|
588 | 0 | if (rv == MS_SUCCESS) { |
589 | | /* reproject to the target layer */ |
590 | 0 | if (layerinfo->reprojectorCurSrcLayer != tile) { |
591 | 0 | msProjectDestroyReprojector(layerinfo->reprojectorSrcLayerToLayer); |
592 | 0 | layerinfo->reprojectorSrcLayerToLayer = NULL; |
593 | 0 | layerinfo->reprojectorCurSrcLayer = tile; |
594 | 0 | if (srclayer->project && |
595 | 0 | msProjectionsDiffer(&(srclayer->projection), &(layer->projection))) |
596 | 0 | layerinfo->reprojectorSrcLayerToLayer = msProjectCreateReprojector( |
597 | 0 | &(srclayer->projection), &(layer->projection)); |
598 | 0 | else |
599 | 0 | srclayer->project = MS_FALSE; |
600 | 0 | } |
601 | 0 | if (layerinfo->reprojectorSrcLayerToLayer) |
602 | 0 | msProjectShapeEx(layerinfo->reprojectorSrcLayerToLayer, shape); |
603 | |
|
604 | 0 | shape->tileindex = tile; |
605 | | |
606 | | /* construct the item array */ |
607 | 0 | if (layer->iteminfo) |
608 | 0 | rv = BuildFeatureAttributes(layer, srclayer, shape); |
609 | 0 | } |
610 | |
|
611 | 0 | return rv; |
612 | 0 | } |
613 | | |
614 | | /* Query for the items collection */ |
615 | 0 | int msUnionLayerGetItems(layerObj *const layer) { |
616 | | /* we support certain built in attributes */ |
617 | 0 | layer->numitems = 2; |
618 | 0 | layer->items = |
619 | 0 | static_cast<char **>(msSmallMalloc(sizeof(char *) * (layer->numitems))); |
620 | 0 | MS_CHECK_ALLOC(layer->items, layer->numitems * sizeof(char *), MS_FAILURE); |
621 | 0 | layer->items[0] = msStrdup(MSUNION_SOURCELAYERNAME); |
622 | 0 | layer->items[1] = msStrdup(MSUNION_SOURCELAYERGROUP); |
623 | |
|
624 | 0 | return msUnionLayerInitItemInfo(layer); |
625 | 0 | } |
626 | | |
627 | 0 | int msUnionLayerGetNumFeatures(layerObj *const layer) { |
628 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
629 | |
|
630 | 0 | if (!layerinfo || !layer->map) |
631 | 0 | return 0; |
632 | | |
633 | 0 | int numFeatures = 0; |
634 | |
|
635 | 0 | for (int i = 0; i < layerinfo->layerCount; i++) { |
636 | 0 | if (layerinfo->status[i] != MS_SUCCESS) |
637 | 0 | continue; /* skip empty layers */ |
638 | | |
639 | 0 | int c = msLayerGetNumFeatures(&(layerinfo->layers[i])); |
640 | 0 | if (c > 0) |
641 | 0 | numFeatures += c; |
642 | 0 | } |
643 | |
|
644 | 0 | return numFeatures; |
645 | 0 | } |
646 | | |
647 | | static int msUnionLayerGetAutoStyle(mapObj *map, layerObj *const layer, |
648 | 0 | classObj *c, shapeObj *shape) { |
649 | 0 | msUnionLayerInfo *layerinfo = (msUnionLayerInfo *)layer->layerinfo; |
650 | |
|
651 | 0 | if (!layerinfo || !layer->map) |
652 | 0 | return MS_FAILURE; |
653 | | |
654 | 0 | if (shape->tileindex < 0 || shape->tileindex >= layerinfo->layerCount) { |
655 | 0 | msSetError(MS_MISCERR, "Invalid tile index: %s", |
656 | 0 | "msUnionLayerGetAutoStyle()", layer->name); |
657 | 0 | return MS_FAILURE; |
658 | 0 | } |
659 | | |
660 | 0 | layerObj *const srclayer = &layerinfo->layers[shape->tileindex]; |
661 | |
|
662 | 0 | if (srclayer->styleitem && strcasecmp(srclayer->styleitem, "AUTO") == 0) { |
663 | 0 | const int tileindex = shape->tileindex; |
664 | 0 | shape->tileindex = 0; |
665 | 0 | int rv = msLayerGetAutoStyle(map, srclayer, c, shape); |
666 | 0 | shape->tileindex = tileindex; |
667 | 0 | return rv; |
668 | 0 | } else { |
669 | 0 | const classObj *src = srclayer->_class[layerinfo->classIndex]; |
670 | | /* copy the style from the current class index */ |
671 | | /* free any previous styles on the dst layer */ |
672 | |
|
673 | 0 | resetClassStyle(c); |
674 | |
|
675 | 0 | for (int i = 0; i < src->numstyles; i++) { |
676 | 0 | if (msMaybeAllocateClassStyle(c, i)) |
677 | 0 | return MS_FAILURE; |
678 | | |
679 | 0 | if (msCopyStyle(c->styles[i], src->styles[i]) != MS_SUCCESS) { |
680 | 0 | msSetError(MS_MEMERR, "Failed to copy style.", |
681 | 0 | "msUnionLayerGetAutoStyle()"); |
682 | 0 | return MS_FAILURE; |
683 | 0 | } |
684 | | /* remove the bindings on the style */ |
685 | 0 | for (int j = 0; j < MS_STYLE_BINDING_LENGTH; j++) { |
686 | 0 | msFree(c->styles[i]->bindings[j].item); |
687 | 0 | c->styles[i]->bindings[j].item = NULL; |
688 | 0 | } |
689 | 0 | c->styles[i]->numbindings = 0; |
690 | 0 | } |
691 | | |
692 | 0 | for (int i = 0; i < src->numlabels; i++) { |
693 | | // RFC77 TODO: allocation need to be done, but is the right way (from |
694 | | // mapcopy.c)? |
695 | 0 | if (msGrowClassLabels(c) == NULL) |
696 | 0 | return MS_FAILURE; |
697 | 0 | initLabel(c->labels[i]); |
698 | |
|
699 | 0 | if (msCopyLabel(c->labels[i], src->labels[i]) != MS_SUCCESS) { |
700 | 0 | msSetError(MS_MEMERR, "Failed to copy label.", |
701 | 0 | "msUnionLayerGetAutoStyle()"); |
702 | 0 | return MS_FAILURE; |
703 | 0 | } |
704 | | |
705 | | /* remove the bindings on the label */ |
706 | 0 | for (int j = 0; j < MS_LABEL_BINDING_LENGTH; j++) { |
707 | 0 | msFree(c->labels[i]->bindings[j].item); |
708 | 0 | c->labels[i]->bindings[j].item = NULL; |
709 | 0 | } |
710 | 0 | c->labels[i]->numbindings = 0; |
711 | 0 | } |
712 | 0 | c->numlabels = src->numlabels; |
713 | |
|
714 | 0 | c->layer = layer; |
715 | 0 | c->text.string = layerinfo->classText; |
716 | 0 | layerinfo->classText = NULL; |
717 | 0 | } |
718 | 0 | return MS_SUCCESS; |
719 | 0 | } |
720 | | |
721 | 0 | int msUnionLayerCopyVirtualTable(layerVTableObj *vtable) { |
722 | 0 | vtable->LayerInitItemInfo = msUnionLayerInitItemInfo; |
723 | 0 | vtable->LayerFreeItemInfo = msUnionLayerFreeItemInfo; |
724 | 0 | vtable->LayerOpen = msUnionLayerOpen; |
725 | 0 | vtable->LayerIsOpen = msUnionLayerIsOpen; |
726 | 0 | vtable->LayerWhichShapes = msUnionLayerWhichShapes; |
727 | 0 | vtable->LayerNextShape = msUnionLayerNextShape; |
728 | 0 | vtable->LayerGetShape = msUnionLayerGetShape; |
729 | | /* layer->vtable->LayerGetShapeCount, use default */ |
730 | 0 | vtable->LayerClose = msUnionLayerClose; |
731 | |
|
732 | 0 | vtable->LayerGetItems = msUnionLayerGetItems; |
733 | 0 | vtable->LayerCloseConnection = msUnionLayerClose; |
734 | 0 | vtable->LayerGetAutoStyle = msUnionLayerGetAutoStyle; |
735 | |
|
736 | 0 | vtable->LayerGetNumFeatures = msUnionLayerGetNumFeatures; |
737 | |
|
738 | 0 | return MS_SUCCESS; |
739 | 0 | } |
740 | | |
741 | 0 | int msUnionLayerInitializeVirtualTable(layerObj *const layer) { |
742 | 0 | assert(layer != NULL); |
743 | 0 | assert(layer->vtable != NULL); |
744 | | |
745 | 0 | return msUnionLayerCopyVirtualTable(layer->vtable); |
746 | 0 | } |