tinyows 1.2.2
wfs_get_feature.c
Go to the documentation of this file.
1/*
2 Copyright (c) <2007-2012> <Barbara Philippot - Olivier Courtin>
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 IN THE SOFTWARE.
21*/
22
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <assert.h>
27#include <float.h>
28#include <math.h>
29#include <string.h>
30
31#include "../ows/ows.h"
32
33
34
35/*
36 * Return the boundaries of the features returned by the request
37 */
38static void wfs_gml_bounded_by(ows * o, wfs_request * wr, double xmin, double ymin, double xmax, double ymax, ows_srs * srs)
39{
40 int precision;
41
42 assert(o);
43 assert(wr);
44 assert(srs);
45
46 if (srs->is_geographic) precision = o->degree_precision;
47 else precision = o->meter_precision;
48
49 if (!srs->srid || srs->srid == -1 || (xmin == ymin && xmax == ymax && xmin == xmax)) {
50 if (ows_version_get(o->request->version) == 100)
51 fprintf(o->output, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>\n");
52 else return; /* No Null boundedBy in WFS 1.1.0 SF-0 */
53
54 } else {
55 fprintf(o->output, "<gml:boundedBy>\n");
56
57 if (wr->format == WFS_GML212) {
58 fprintf(o->output, " <gml:Box srsName=\"");
59 if (strcmp(srs->auth_name->buf, "EPSG")) fprintf(o->output, "%s:", srs->auth_name->buf);
60 else if (srs->is_long) fprintf(o->output, "urn:ogc:def:crs:EPSG::");
61 else fprintf(o->output, "EPSG:");
62 fprintf(o->output, "%d\">", srs->srid);
63
64 if (fabs(xmin) > OWS_MAX_DOUBLE || fabs(ymin) > OWS_MAX_DOUBLE ||
65 fabs(xmax) > OWS_MAX_DOUBLE || fabs(ymax) > OWS_MAX_DOUBLE)
66 fprintf(o->output, "<gml:coordinates decimal=\".\" cs=\",\" ts=\" \">%g,%g %g,%g</gml:coordinates>",
67 xmin, ymin, xmax, ymax);
68 else fprintf(o->output, "<gml:coordinates decimal=\".\" cs=\",\" ts=\" \">%.*f,%.*f %.*f,%.*f</gml:coordinates>",
69 precision, xmin, precision, ymin, precision, xmax, precision, ymax);
70 fprintf(o->output, "</gml:Box>\n");
71
72 } else if (wr->format == WFS_GML311) {
73 fprintf(o->output, " <gml:Envelope srsName=\"");
74 if (strcmp(srs->auth_name->buf, "EPSG")) fprintf(o->output, "%s:", srs->auth_name->buf);
75 else if (srs->is_long) fprintf(o->output, "urn:ogc:def:crs:EPSG::");
76 else fprintf(o->output, "EPSG:");
77 fprintf(o->output, "%d\">", srs->srid);
78
80 if (fabs(xmin) > OWS_MAX_DOUBLE || fabs(ymin) > OWS_MAX_DOUBLE ||
81 fabs(xmax) > OWS_MAX_DOUBLE || fabs(ymax) > OWS_MAX_DOUBLE) {
82 fprintf(o->output, "<gml:lowerCorner>%g %g</gml:lowerCorner>", ymin, xmin);
83 fprintf(o->output, "<gml:upperCorner>%g %g</gml:upperCorner>", ymax, xmax);
84 } else {
85 fprintf(o->output, "<gml:lowerCorner>%.*f %.*f</gml:lowerCorner>", precision, ymin, precision, xmin);
86 fprintf(o->output, "<gml:upperCorner>%.*f %.*f</gml:upperCorner>", precision, ymax, precision, xmax);
87 }
88 } else {
89 if (fabs(xmin) > OWS_MAX_DOUBLE || fabs(ymin) > OWS_MAX_DOUBLE ||
90 fabs(xmax) > OWS_MAX_DOUBLE || fabs(ymax) > OWS_MAX_DOUBLE) {
91 fprintf(o->output, "<gml:lowerCorner>%g %g</gml:lowerCorner>", xmin, ymin);
92 fprintf(o->output, "<gml:upperCorner>%g %g</gml:upperCorner>", xmax, ymax);
93 } else {
94 fprintf(o->output, "<gml:lowerCorner>%.*f %.*f</gml:lowerCorner>", precision, xmin, precision, ymin);
95 fprintf(o->output, "<gml:upperCorner>%.*f %.*f</gml:upperCorner>", precision, xmax, precision, ymax);
96 }
97 }
98 fprintf(o->output, "</gml:Envelope>\n");
99 }
100
101 fprintf(o->output, "</gml:boundedBy>\n");
102 }
103}
104
105
106/*
107 * Display the properties of one feature
108 */
110 buffer * layer_name, buffer * prefix,
111 char * prop_name, buffer * prop_type, char * value)
112{
113 buffer *time, *pkey, *value_encoded, *prop_name_buffer;
114 bool gml_ns = false;
115 assert(layer_name && prop_name);
116 assert(prop_type);
117 assert(prefix);
118 assert(value);
119 assert(layer_name && prop_name && prop_type && prefix && value && wr && o);
120
121 pkey = ows_psql_id_column(o, layer_name); /* CAUTION: pkey could be NULL ! */
122
123 /* No Pkey display in GML (default behaviour) */
124 if (pkey && pkey->buf && !strcmp(prop_name, pkey->buf) && !o->expose_pk) return;
125
126 /* Avoid to expose elements from gml_exclude_items */
127 prop_name_buffer = buffer_from_str(prop_name);
128
129 if ((ows_layer_get(o->layers, layer_name))->exclude_items
130 && in_list(ows_layer_get(o->layers, layer_name)->exclude_items, prop_name_buffer)) return;
131
132 buffer_free(prop_name_buffer);
133
134 if (strlen(value) == 0) return; /* Don't display empty property */
135
136 /* We have to check if we use gml ns or not */
137 if ((ows_layer_get(o->layers, layer_name))->gml_ns
138 && (in_list_str((ows_layer_get(o->layers, layer_name))->gml_ns, prop_name))) gml_ns = true;
139
140 if (gml_ns) fprintf(o->output, " <gml:%s>", prop_name);
141 else fprintf(o->output, " <%s:%s>", prefix->buf, prop_name);
142
143
144 /* PSQL date must be transformed into GML format */
145 if ( buffer_cmp(prop_type, "timestamptz")
146 || buffer_cmp(prop_type, "timestamp")
147 || buffer_cmp(prop_type, "datetime")
148 || buffer_cmp(prop_type, "date")) {
149 time = ows_psql_timestamp_to_xml_time(value);
150 fprintf(o->output, "%s", time->buf);
151 buffer_free(time);
152
153 /* PSQL boolean must be transformed into GML format */
154 } else if (buffer_cmp(prop_type, "bool")) {
155 if (!strcmp(value, "t")) fprintf(o->output, "true");
156 if (!strcmp(value, "f")) fprintf(o->output, "false");
157
158 } else if ( buffer_cmp(prop_type, "text")
159 || buffer_cmp(prop_type, "hstore")
160 || buffer_ncmp(prop_type, "char", 4)
161 || buffer_ncmp(prop_type, "varchar", 7)) {
162
163 value_encoded = buffer_encode_xml_entities_str(value);
164 fprintf(o->output, "%s", value_encoded->buf);
165 buffer_free(value_encoded);
166
167 } else fprintf(o->output, "%s", value);
168
169 if (gml_ns) fprintf(o->output, "</gml:%s>\n", prop_name);
170 else fprintf(o->output, "</%s:%s>\n", prefix->buf, prop_name);
171}
172
173
174/*
175 * Display in GML all feature members returned by the request
176 */
177void wfs_gml_feature_member(ows * o, wfs_request * wr, buffer * layer_name, list * properties, PGresult * res)
178{
179 int i, j, number, end, nb_fields;
180 buffer *id_name, *ns_prefix, *prop_type, *layer;
181 array * describe;
182 assert(o && wr && res && layer_name);
183
184 /* CAUTION: Properties could be NULL ! */
185
186 number = -1;
187 id_name = ows_psql_id_column(o, layer_name);
188
189 /* CAUTION: We could imagine layer without PK ! */
190 if (id_name && id_name->use) number = PQfnumber(res, id_name->buf);
191
192 ns_prefix = ows_layer_ns_prefix(o->layers, ows_layer_uri_to_prefix(o->layers, layer_name));
193 describe = ows_psql_describe_table(o, layer_name);
194
195 /* display the results in gml */
196 for (i = 0, end = PQntuples(res); i < end; i++) {
197 fprintf(o->output, " <gml:featureMember>\n");
198
199 layer = ows_layer_no_uri(o->layers, layer_name);
200
201 /* print layer's name and id according to GML version */
202 if (id_name && id_name->use) {
203 if (wr->format == WFS_GML311)
204 fprintf(o->output, " <%s gml:id=\"%s.%s\">\n",
205 ows_layer_uri_to_prefix(o->layers, layer_name)->buf,
206 layer->buf, PQgetvalue(res, i, number));
207 else fprintf(o->output, " <%s fid=\"%s.%s\">\n",
208 ows_layer_uri_to_prefix(o->layers, layer_name)->buf,
209 layer->buf, PQgetvalue(res, i, number));
210 } else fprintf(o->output, " <%s>\n", ows_layer_uri_to_prefix(o->layers, layer_name)->buf);
211
212 /* print properties */
213 for (j = 0, nb_fields = PQnfields(res) ; j < nb_fields ; j++) {
214 if ( !properties
215 || in_list_str(properties, PQfname(res, j))
216 || buffer_cmp(properties->first->value, "*")
217 || (ows_psql_not_null_properties(o, layer_name)
218 && in_list_str(ows_psql_not_null_properties(o, layer_name), PQfname(res, j)))
219 || ((ows_layer_get(o->layers, layer_name))->gml_ns
220 && in_list_str((ows_layer_get(o->layers, layer_name))->gml_ns, PQfname(res, j)))) {
221 prop_type = array_get(describe, PQfname(res, j));
222 wfs_gml_display_feature(o, wr, layer_name, ns_prefix, PQfname(res,j), prop_type, PQgetvalue(res, i ,j));
223 }
224 }
225
226 fprintf(o->output, " </%s>\n", ows_layer_uri_to_prefix(o->layers, layer_name)->buf);
227 fprintf(o->output, " </gml:featureMember>\n");
228 }
229}
230
231
232/*
233 * Display in GML the fisrt node and the required namespaces
234 */
236{
237 array *namespaces;
238 array_node *an;
239 list_node *ln;
240
241 assert(o);
242 assert(wr);
243
244 namespaces = ows_layer_list_namespaces(o->layers);
245 assert(namespaces);
246
247 if (wr->format == WFS_GML212)
248 fprintf(o->output, "Content-Type: text/xml; subtype=gml/2.1.2\n\n");
249 else if (wr->format == WFS_GML311)
250 fprintf(o->output, "Content-Type: text/xml; subtype=gml/3.1.1\n\n");
251
252 fprintf(o->output, "<?xml version='1.0' encoding='%s'?>\n", o->encoding->buf);
253 fprintf(o->output, "<wfs:FeatureCollection\n");
254
255 for (an = namespaces->first; an != NULL; an = an->next)
256 fprintf(o->output, " xmlns:%s='%s'\n", an->key->buf, an->value->buf);
257
258 fprintf(o->output, " xmlns:wfs='http://www.opengis.net/wfs'\n");
259 fprintf(o->output, " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n");
260 fprintf(o->output, " xmlns:gml='http://www.opengis.net/gml'\n");
261 fprintf(o->output, " xmlns:xsd='http://www.w3.org/2001/XMLSchema'\n");
262 fprintf(o->output, " xmlns:ogc='http://www.opengis.net/ogc'\n");
263 fprintf(o->output, " xmlns:xlink='http://www.w3.org/1999/xlink'\n");
264 fprintf(o->output, " xmlns:ows='http://www.opengis.net/ows'\n");
265
266 fprintf(o->output, " xsi:schemaLocation='");
267 if (ows_version_get(o->request->version) == 100)
268 fprintf(o->output, "%s\n %s?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType",
269 namespaces->first->value->buf, o->online_resource->buf);
270 else
271 fprintf(o->output, "%s\n %s?service=WFS&amp;version=1.1.0&amp;request=DescribeFeatureType",
272 namespaces->first->value->buf, o->online_resource->buf);
273
274 /* FeatureId request could be without Typename parameter */
275 if (wr->typename) {
276 fprintf(o->output, "&amp;Typename=");
277 for (ln = wr->typename->first ; ln ; ln = ln->next) {
278 fprintf(o->output, "%s", ln->value->buf);
279 if (ln->next) fprintf(o->output, ",");
280 }
281 }
282
283 if (ows_version_get(o->request->version) == 100) {
284 fprintf(o->output, " http://www.opengis.net/wfs\n");
285 fprintf(o->output, " http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd\n");
286 } else {
287 fprintf(o->output, " http://www.opengis.net/wfs\n");
288 fprintf(o->output, " http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\n");
289 }
290
291 if (wr->format == WFS_GML212) {
292 fprintf(o->output, " http://www.opengis.net/gml\n");
293 fprintf(o->output, " http://schemas.opengis.net/gml/2.1.2/feature.xsd'\n");
294 } else {
295 fprintf(o->output, " http://www.opengis.net/gml\n");
296 fprintf(o->output, " http://schemas.opengis.net/gml/3.1.1/base/gml.xsd'\n");
297 }
298
299 array_free(namespaces);
300}
301
302
303/*
304 * Diplay in GML result of a GetFeature hits request
305 */
306static void wfs_gml_display_hits(ows * o, wfs_request * wr, mlist * request_list)
307{
308 list_node * ln;
309 PGresult *res;
310 buffer * date;
311 int hits = 0;
312
313 assert(o);
314 assert(wr);
315 assert(request_list);
316
318
319 /* Just count the number of features */
320 for (ln = request_list->first->value->first ; ln ; ln = ln->next) {
321 buffer_add_head_str(ln->value, "SELECT count(*) FROM (");
322 buffer_add_str(ln->value, ") AS foo");
323
324 res = ows_psql_exec(o, ln->value->buf);
325 if (PQresultStatus(res) == PGRES_TUPLES_OK)
326 hits = hits + atoi(PQgetvalue(res, 0, 0));
327 PQclear(res);
328 }
329
330 /* Render GML hits output */
331 res = ows_psql_exec(o, "SELECT localtimestamp");
332 date = ows_psql_timestamp_to_xml_time(PQgetvalue(res, 0, 0));
333 fprintf(o->output, " timeStamp='%s' numberOfFeatures='%d' />\n", date->buf, hits);
334 buffer_free(date);
335 PQclear(res);
336}
337
338/*
339 * Diplay in GML result of a GetFeature request
340 */
341static void wfs_gml_display_results(ows * o, wfs_request * wr, mlist * request_list)
342{
343 mlist_node *mln_property, *mln_fid;
344 list_node *ln, *ln_typename;
345 buffer *layer_name, *layer_uri;
346 list *fe;
347 PGresult *res;
348 ows_bbox *outer_b;
349
350 assert(o && wr && request_list);
351
352 ln = ln_typename = NULL;
353 mln_property = mln_fid = NULL;
354
355 /* Display the first node and namespaces */
357 fprintf(o->output, ">\n");
358
359 /* Display only if we really asked the bbox of the features retrieved. Overhead could be signifiant ! */
360 if (o->display_bbox) {
361 outer_b = ows_bbox_boundaries(o, request_list->first->next->value, request_list->last->value, wr->srs);
362 wfs_gml_bounded_by(o, wr, outer_b->xmin, outer_b->ymin, outer_b->xmax, outer_b->ymax, outer_b->srs);
363 ows_bbox_free(outer_b);
364 }
365
366 /* Initialize the nodes to run through requests */
367 if (wr->typename) ln_typename = wr->typename->first;
368 if (wr->featureid) mln_fid = wr->featureid->first;
369 if (wr->propertyname) mln_property = wr->propertyname->first;
370
371 for (ln = request_list->first->value->first ; ln ; ln = ln->next) {
372
373 res = ows_psql_exec(o, ln->value->buf);
374
375 if (PQresultStatus(res) != PGRES_TUPLES_OK) {
376 PQclear(res);
377
378 /* Increments the nodes */
379 if (wr->typename) ln_typename = ln_typename->next;
380 if (wr->featureid) mln_fid = mln_fid->next;
381 if (wr->propertyname) mln_property = mln_property->next;
382
383 break;
384 }
385
386 /* Define a layer_name which match typename or featureid */
387
388 if (wr->typename) {
389 layer_name = buffer_init();
390 buffer_copy(layer_name, ln_typename->value);
391 layer_uri = ows_layer_prefix_to_uri(o->layers, layer_name);
392 buffer_free(layer_name);
393 } else {
394 fe = list_explode('.', mln_fid->value->first->value);
395 layer_uri = ows_layer_no_uri_to_uri(o->layers, fe->first->value);
396 list_free(fe);
397 }
398
399 /* Display each feature member */
400 if (wr->propertyname) wfs_gml_feature_member(o, wr, layer_uri, mln_property->value, res);
401 else wfs_gml_feature_member(o, wr, layer_uri, NULL, res); /* PropertyNames not mandatory */
402
403 PQclear(res);
404
405 /* Increments the nodes */
406 if (wr->featureid) mln_fid = mln_fid->next;
407 if (wr->propertyname) mln_property = mln_property->next;
408 if (wr->typename) ln_typename = ln_typename->next;
409 }
410
411 fprintf(o->output, "</wfs:FeatureCollection>\n");
412}
413
414
415/*
416 * Transform part of GetFeature request into SELECT statement of a SQL request
417 */
419{
420 int gml_opt;
421 buffer *select;
422 array *prop_table;
423 array_node *an;
424 bool gml_boundedby;
425
426 assert(o && wr);
427
428 select = buffer_init();
429 buffer_add_str(select, "SELECT ");
430
431 prop_table = ows_psql_describe_table(o, layer_name);
432
433 for (an = prop_table->first ; an ; an = an->next) {
434
435 if (!strcmp(an->key->buf, "boundedBy")
436 && (ows_layer_get(o->layers, layer_name))->gml_ns
437 && (in_list_str((ows_layer_get(o->layers, layer_name))->gml_ns, an->key->buf))) gml_boundedby = true;
438 else gml_boundedby = false;
439
440 /* geometry columns must be returned in GML */
441 if (ows_psql_is_geometry_column(o, layer_name, an->key)) {
442
443 if (wr->format == WFS_GML212) {
444 buffer_add_str(select, "ST_AsGML(");
445
446 /* Geometry Reprojection on the fly step if asked */
447 if (wr->srs) {
448 buffer_add_str(select, "ST_Transform(");
449 buffer_add(select, '"');
450 buffer_copy(select, an->key);
451 buffer_add_str(select, "\"::geometry,");
452 buffer_add_int(select, wr->srs->srid);
453 buffer_add_str(select, "),");
454 } else {
455 buffer_add(select, '"');
456 buffer_copy(select, an->key);
457 buffer_add_str(select, "\",");
458 }
459
460 if ((wr->srs && !wr->srs->is_geographic) ||
461 (!wr->srs && ows_srs_meter_units(o, layer_name)))
463 else
465
466 gml_opt = 0; /* Short SRS */
467 if (wr->srs && wr->srs->is_long) gml_opt += 1;
468 /* This will be actually without effect since PostGIS only honours flag = 16 for GML 3 */
469 if (wr->srs &&
470 wr->srs->honours_authority_axis_order &&
471 !wr->srs->is_axis_order_gis_friendly ) gml_opt += 16;
472 if (gml_boundedby) gml_opt += 32;
473
474 buffer_add_str(select, ",");
475 buffer_add_int(select, gml_opt);
476 buffer_add_str(select, ") AS \"");
477 buffer_copy(select, an->key);
478 buffer_add_str(select, "\" ");
479 }
480 /* GML3 */
481 else if (wr->format == WFS_GML311) {
482 buffer_add_str(select, "ST_AsGML(3, ");
483
484 /* Geometry Reprojection on the fly step if asked */
485 if (wr->srs) {
486 buffer_add_str(select, "ST_Transform(");
487 buffer_add(select, '"');
488 buffer_copy(select, an->key);
489 buffer_add_str(select, "\"::geometry,");
490 buffer_add_int(select, wr->srs->srid);
491 buffer_add_str(select, "),");
492 } else {
493 buffer_add(select, '"');
494 buffer_copy(select, an->key);
495 buffer_add_str(select, "\",");
496 }
497
498 gml_opt = 6; /* no srsDimension (CITE Compliant) and use LineString rather than curve */
499 if (wr->srs && wr->srs->is_long) gml_opt += 1; /* Long SRS */
500 if (gml_boundedby) gml_opt += 32;
501
502 if ((wr->srs && !wr->srs->is_geographic) || (!wr->srs && ows_srs_meter_units(o, layer_name))) {
504 } else {
506 }
507 if (wr->srs &&
508 wr->srs->honours_authority_axis_order &&
509 !wr->srs->is_axis_order_gis_friendly ) gml_opt += 16;
510
511 buffer_add_str(select, ", ");
512 buffer_add_int(select, gml_opt);
513 buffer_add_str(select, ") AS \"");
514 buffer_copy(select, an->key);
515 buffer_add_str(select, "\" ");
516 } else if (wr->format == WFS_GEOJSON || wr->format == WFS_JSONP) {
517 buffer_add_str(select, "ST_AsGeoJSON(");
518
519 /* Geometry Reprojection on the fly step if asked */
520 if (wr->srs) {
521 buffer_add_str(select, "ST_Transform(");
522 buffer_add(select, '"');
523 buffer_copy(select, an->key);
524 buffer_add_str(select, "\"::geometry,");
525 buffer_add_int(select, wr->srs->srid);
526 buffer_add_str(select, "),");
527 } else {
528 buffer_add(select, '"');
529 buffer_copy(select, an->key);
530 buffer_add_str(select, "\",");
531 }
532
533 if ((wr->srs && !wr->srs->is_geographic) ||
534 (!wr->srs && ows_srs_meter_units(o, layer_name)))
536 else
538
539 buffer_add_str(select, ", 1) AS \""); /* Bbox */
540
541 buffer_copy(select, an->key);
542 buffer_add_str(select, "\" ");
543 }
544
545 }
546 /* Columns are written in quotation marks */
547 else {
548 buffer_add_str(select, "\"");
549 buffer_copy(select, an->key);
550 buffer_add_str(select, "\"");
551 }
552
553 if (an->next) buffer_add_str(select, ",");
554 }
555
556 return select;
557}
558
559
560/*
561 * Retrieve a list of SQL requests from the GetFeature parameters
562 */
564{
565 mlist *requests;
566 mlist_node *mln_fid;
567 list *fid, *sql_req, *from_list, *where_list;
568 list_node *ln_typename, *ln_filter;
569 buffer *geom, *sql, *where, *layer_name, *layer_uri, *sql_count;
570 int srid, size, cpt, features, max_features;
571 filter_encoding *fe;
572 ows_bbox *bbox;
573 char *escaped;
574 PGresult * res;
575
576 assert(o && wr);
577
578 mln_fid = NULL;
579 ln_typename = ln_filter = NULL;
580 where = geom = NULL;
581 size = features = 0;
582
583 /* Initialize the nodes to run through typename and fid */
584 if (wr->typename) {
585 size = wr->typename->size;
586 ln_typename = wr->typename->first;
587 }
588
589 if (wr->filter) ln_filter = wr->filter->first;
590
591 if (wr->featureid) {
592 size = wr->featureid->size;
593 mln_fid = wr->featureid->first;
594 }
595
596 /* Will contain list of SQL requests */
597 sql_req = list_init();
598 /* The 'from_list' and 'where_list' will contain layer names
599 and SQL WHERE statement used later to compute the boundaries */
600 from_list = list_init();
601 where_list = list_init();
602
603 /* Fill a SQL request for each typename */
604 for (cpt = 0; cpt < size; cpt++) {
605 layer_name = buffer_init();
606
607 /* Defines a layer_name which match typename or featureid */
608 if (wr->typename) {
609 buffer_copy(layer_name, ln_typename->value);
610 layer_uri = ows_layer_prefix_to_uri(o->layers, layer_name);
611 } else {
612 fid = list_explode('.', mln_fid->value->first->value);
613 layer_uri = ows_layer_no_uri_to_uri(o->layers, fid->first->value);
614 buffer_copy(layer_name, ows_layer_uri_to_prefix(o->layers, layer_uri));
615 list_free(fid);
616 }
617
618
619 /* SELECT */
620 sql = wfs_retrieve_sql_request_select(o, wr, layer_uri);
621
622 /* FROM : match layer_name (typename or featureid) */
623 buffer_add_str(sql, " FROM \"");
624 buffer_copy(sql, ows_psql_schema_name(o, layer_uri));
625 buffer_add_str(sql, "\".\"");
626 buffer_copy(sql, ows_psql_table_name(o, layer_uri));
627 buffer_add_str(sql, "\"");
628
629 /* WHERE : match featureid, bbox or filter */
630
631 /* FeatureId */
632 if (wr->featureid) {
633 where = fe_kvp_featureid(o, wr, layer_uri, mln_fid->value);
634
635 if (where->use == 0) {
636 buffer_free(where);
637 buffer_free(sql);
638 buffer_free(layer_name);
639 list_free(sql_req);
640 list_free(from_list);
641 list_free(where_list);
642 wfs_error(o, wr, WFS_ERROR_NO_MATCHING, "error : an id_column is required to use featureid", "GetFeature");
643 return NULL;
644 }
645 }
646
647 /* BBOX */
648 else if (wr->bbox) where = fe_kvp_bbox(o, wr, layer_uri, wr->bbox);
649
650 /* Filter */
651 else if (wr->filter) {
652 if (ln_filter->value->use != 0) {
653 where = buffer_init();
654 buffer_add_str(where, " WHERE ");
655
657 fe = fe_filter(o, fe, layer_name, ln_filter->value);
658
659 if (fe->error_code != FE_NO_ERROR) {
660 buffer_free(where);
661 buffer_free(sql);
662 buffer_free(layer_name);
663 list_free(sql_req);
664 list_free(from_list);
665 list_free(where_list);
666 fe_error(o, fe);
667 return NULL;
668 }
669
670 buffer_copy(where, fe->sql);
672 }
673 } else where = buffer_init();
674
675 buffer_free(layer_name);
676
677 if (o->max_geobbox && where->use != 0) buffer_add_str(where, " AND ");
678 else if (o->max_geobbox && where->use == 0) buffer_add_str(where, " WHERE ");
679
680 /* geobbox's limits of ows */
681 if (o->max_geobbox) {
682
683 bbox = ows_bbox_init();
685
686 buffer_add_str(where, "NOT (ST_Disjoint(");
687 buffer_copy(where, geom);
688 buffer_add_str(where, ",ST_Transform(");
689 ows_bbox_to_query(o, bbox, where);
690 buffer_add_str(where, ",");
691 srid = ows_srs_get_srid_from_layer(o, layer_uri);
692 buffer_add_int(where, srid);
693 buffer_add_str(where, ")))");
694
695 ows_bbox_free(bbox);
696 }
697
698 /* sortby parameter */
699 if (wr->sortby) {
700 buffer_add_str(where, " ORDER BY ");
701 escaped = ows_psql_escape_string(o, wr->sortby->buf);
702 if (escaped) {
703 buffer_add_str(where, escaped);
704 free(escaped);
705 }
706 }
707
708 /* maxfeatures parameter, or max_features ows limits, limits the number of results */
709 max_features = -1;
710 if (wr->maxfeatures > 0 && o->max_features > 0 && wr->maxfeatures > o->max_features)
711 max_features = o->max_features;
712 else if (wr->maxfeatures > 0)
713 max_features = wr->maxfeatures;
714 else if (o->max_features > 0)
715 max_features = o->max_features;
716
717 if (max_features > 0 && wr->typename->size == 1) {
718 buffer_add_str(where, " LIMIT ");
719 buffer_add_int(where, max_features);
720 } else if (max_features > 0 && wr->typename->size > 1) {
721 /* We have to compute LIMIT for each layer in this case ! */
722 sql_count = buffer_init();
723 buffer_add_str(sql_count, "SELECT count(*) FROM (");
724 buffer_copy(sql_count, sql);
725 buffer_copy(sql_count, where);
726 buffer_add_str(sql_count, " LIMIT ");
727 buffer_add_int(sql_count, max_features - features );
728 buffer_add_str(sql_count, ") AS c");
729 res = ows_psql_exec(o, sql_count->buf);
730 if (PQresultStatus(res) == PGRES_TUPLES_OK) {
731 if (features + atoi(PQgetvalue(res, 0, 0)) <= max_features) {
732 buffer_add_str(where, " LIMIT ");
733 if ((max_features - features) > 0)
734 buffer_add_int(where, max_features - features);
735 else buffer_add_int(where, 0);
736 features += atoi(PQgetvalue(res, 0, 0));
737 }
738 }
739 PQclear(res);
740 buffer_free(sql_count);
741 }
742
743 buffer_copy(sql, where);
744
745 /* fprintf(stderr, "sql = %s\n", sql->buf); */
746
747 list_add(sql_req, sql);
748 list_add(where_list, where);
749 list_add_by_copy(from_list, layer_uri);
750
751 /* incrementation of the nodes */
752 if (wr->featureid) mln_fid = mln_fid->next;
753 if (wr->typename) ln_typename = ln_typename->next;
754 if (wr->filter) ln_filter = ln_filter->next;
755 }
756
757 /* requests multiple list contains three lists : sql requests, from list and where list */
758 requests = mlist_init();
759 mlist_add(requests, sql_req);
760 mlist_add(requests, from_list);
761 mlist_add(requests, where_list);
762
763 return requests;
764}
765
766
767/*
768 * Diplay in GeoJSON result of a GetFeature request
769 */
770static void wfs_geojson_display_results(ows * o, wfs_request * wr, mlist * request_list)
771{
772 PGresult *res;
773 list_node *ln, *ll;
774 array *prop_table;
775 array_node *an;
776 buffer *prop, *value_enc, *geom, *id_name;
777 bool first_row, first_col;
778 int i,j;
779 int geoms;
780 int number;
781
782 assert(o);
783 assert(wr);
784 assert(request_list);
785
786 ln = ll= NULL;
787
788 ll = request_list->first->next->value->first;
789 geom = buffer_init();
790 prop = buffer_init();
791 id_name = buffer_init();
792
793 if (wr->format == WFS_JSONP)
794 {
795 assert(wr->callback);
796
797 fprintf(o->output, "Content-Type: application/javascript\n\n");
798 fprintf(o->output, "%s", wr->callback->buf);
799 fprintf(o->output, "(");
800
801 } else fprintf(o->output, "Content-Type: application/json\n\n");
802
803
804 fprintf(o->output, "{\"type\": \"FeatureCollection\", \"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"");
805 if (ows_version_get(o->request->version) == 100)
806 fprintf(o->output, "EPSG:%i", wr->srs->srid);
807 else
808 fprintf(o->output, "urn:ogc:def:crs:EPSG::%i", wr->srs->srid);
809
810 fprintf(o->output, "\"}}, \"features\": [");
811
812 for (ln = request_list->first->value->first ; ln ; ln = ln->next) {
813
814 res = ows_psql_exec(o, ln->value->buf);
815 if (PQresultStatus(res) != PGRES_TUPLES_OK) {
816 PQclear(res);
817 ll = ll->next;
818 break;
819 }
820
821 prop_table = ows_psql_describe_table(o, ll->value);
822 first_row = true;
823 if(ows_psql_id_column(o, ll->value)) /* CAUTION: pkey could be NULL ! */
824 buffer_copy(id_name, ows_psql_id_column(o, ll->value));
825 number = -1;
826 if (id_name && id_name->use)
827 number = PQfnumber(res, id_name->buf);
828 buffer_empty(id_name);
829
830 for (i=0 ; i < PQntuples(res) ; i++) {
831
832 first_col = true;
833 geoms = 0;
834
835 if (first_row) first_row = false;
836 else fprintf(o->output, ",");
837
838 if ( number >= 0 ) {
839 buffer_add_str(id_name, "\"id\": \"");
840 buffer_copy(id_name, ows_layer_no_uri(o->layers, ll->value));
841 buffer_add_str(id_name, ".");
842 buffer_add_str(id_name, PQgetvalue(res, i, number));
843 buffer_add_str(id_name, "\", ");
844 }
845 for (an = prop_table->first, j=0 ; an ; an = an->next, j++) {
846
847 if (ows_psql_is_geometry_column(o, ll->value, an->key)) {
848 buffer_add_str(geom, PQgetvalue(res, i, j));
849 geoms++;
850 } else {
851
852 if (first_col) first_col = false;
853 else buffer_add_str(prop, ", \"");
854
855 buffer_copy(prop, an->key);
856 buffer_add_str(prop, "\": \"");
857 value_enc = buffer_encode_json_str(PQgetvalue(res, i, j));
858 buffer_copy(prop, value_enc);
859 buffer_free(value_enc);
860 buffer_add(prop, '"');
861 }
862 }
863
864 if (geoms == 0) {
865 fprintf(o->output,
866 "{\"type\":\"Feature\", %s\"properties\":{\"%s}}\n",
867 id_name->buf, prop->buf);
868 } else if (geoms == 1) {
869 fprintf(o->output,
870 "{\"type\":\"Feature\", %s\"properties\":{\"%s}, \"geometry\":%s}\n",
871 id_name->buf, prop->buf, geom->buf);
872 } else if (geoms > 1) {
873 fprintf(o->output,
874 "{\"type\":\"Feature\", %s\"properties\":{\"%s}, \"geometry\":%s%s]}}\n",
875 id_name->buf,
876 prop->buf, "{ \"type\": \"GeometryCollection\", \"geometries\": [",
877 geom->buf);
878 }
879 buffer_empty(prop);
880 buffer_empty(geom);
881 buffer_empty(id_name);
882 }
883
884 PQclear(res);
885 ll = ll->next;
886 }
887
888 fprintf(o->output, "]}");
889 if (wr->format == WFS_JSONP) fprintf(o->output, ");");
890
891 buffer_free(geom);
892 buffer_free(prop);
893}
894
895
896/*
897 * Execute the GetFeature request
898 */
900{
901 mlist *request_list;
902 assert(o && wr);
903
904 /* Retrieve a list of SQL requests from the GetFeature parameters */
905 request_list = wfs_retrieve_sql_request_list(o, wr);
906 if (!request_list) return;
907
908 if (wr->format == WFS_GML212 || wr->format == WFS_GML311) {
909 /* Display result of the GetFeature request in GML */
910 if (buffer_cmp(wr->resulttype, "hits"))
911 wfs_gml_display_hits(o, wr, request_list);
912 else
913 wfs_gml_display_results(o, wr, request_list);
914
915 } else if (wr->format == WFS_GEOJSON || wr->format == WFS_JSONP)
916 wfs_geojson_display_results(o, wr, request_list);
917
918 /* Add here other functions to display GetFeature response in other formats */
919
920 mlist_free(request_list);
921}
buffer * buffer_encode_xml_entities_str(const char *str)
Definition buffer.c:501
list * ows_psql_not_null_properties(ows *o, buffer *layer_name)
Definition ows_psql.c:185
void ows_bbox_free(ows_bbox *b)
Definition ows_bbox.c:58
void buffer_add(buffer *buf, char c)
Definition buffer.c:123
int ows_version_get(ows_version *v)
void list_add_by_copy(list *l, buffer *value)
Definition list.c:187
void buffer_empty(buffer *buf)
Definition buffer.c:100
filter_encoding * fe_filter(ows *o, filter_encoding *fe, buffer *typename, buffer *xmlchar)
Definition fe_filter.c:353
void mlist_add(mlist *ml, list *value)
Definition mlist.c:71
ows_bbox * ows_bbox_init()
Definition ows_bbox.c:37
PGresult * ows_psql_exec(ows *o, const char *sql)
Definition ows_psql.c:56
buffer * buffer_encode_json_str(const char *str)
Definition buffer.c:544
void buffer_copy(buffer *dest, const buffer *src)
Definition buffer.c:350
bool ows_bbox_set_from_geobbox(ows *o, ows_bbox *bb, ows_geobbox *geo)
Definition ows_bbox.c:258
void fe_error(ows *o, filter_encoding *fe)
Definition fe_error.c:34
ows_bbox * ows_bbox_boundaries(ows *o, list *from, list *where, ows_srs *srs)
Definition ows_bbox.c:154
bool ows_srs_meter_units(ows *o, buffer *layer_name)
Definition ows_srs.c:361
filter_encoding * filter_encoding_init()
Definition fe_filter.c:35
bool buffer_cmp(const buffer *buf, const char *str)
Definition buffer.c:290
bool in_list(const list *l, const buffer *value)
Definition list.c:259
void list_free(list *l)
Definition list.c:54
void buffer_add_str(buffer *buf, const char *str)
Definition buffer.c:254
list * list_init()
Definition list.c:36
char * ows_psql_escape_string(ows *o, const char *content)
Definition ows_psql.c:840
array * ows_layer_list_namespaces(ows_layer_list *ll)
Definition ows_layer.c:228
void ows_bbox_to_query(ows *o, ows_bbox *bbox, buffer *query)
Definition ows_bbox.c:285
buffer * array_get(const array *a, const char *key)
Definition array.c:147
buffer * ows_psql_id_column(ows *o, buffer *layer_name)
Definition ows_psql.c:36
void wfs_error(ows *o, wfs_request *wf, enum wfs_error_code code, char *message, char *locator)
Definition wfs_error.c:124
bool in_list_str(const list *l, const char *value)
Definition list.c:278
void array_free(array *a)
Definition array.c:53
bool buffer_ncmp(const buffer *buf, const char *str, size_t n)
Definition buffer.c:310
void list_add(list *l, buffer *value)
Definition list.c:71
void filter_encoding_free(filter_encoding *fe)
Definition fe_filter.c:54
void buffer_add_head_str(buffer *buf, char *str)
Definition buffer.c:239
mlist * mlist_init()
Definition mlist.c:36
buffer * ows_layer_no_uri_to_uri(const ows_layer_list *ll, buffer *name_no_uri)
Definition ows_layer.c:392
array * ows_psql_describe_table(ows *o, buffer *layer_name)
Definition ows_psql.c:373
buffer * buffer_from_str(const char *str)
Definition buffer.c:202
void buffer_free(buffer *buf)
Definition buffer.c:83
buffer * ows_layer_uri_to_prefix(ows_layer_list *ll, buffer *layer_name)
Definition ows_layer.c:327
buffer * ows_psql_timestamp_to_xml_time(char *timestamp)
Definition ows_psql.c:489
void buffer_add_int(buffer *buf, int i)
Definition buffer.c:173
buffer * ows_layer_ns_prefix(ows_layer_list *ll, buffer *layer_name_prefix)
Definition ows_layer.c:408
buffer * ows_layer_no_uri(ows_layer_list *ll, buffer *layer_name)
Definition ows_layer.c:376
ows_layer * ows_layer_get(const ows_layer_list *ll, const buffer *name)
Definition ows_layer.c:66
int ows_srs_get_srid_from_layer(ows *o, buffer *layer_name)
Definition ows_srs.c:380
bool ows_psql_is_geometry_column(ows *o, buffer *layer_name, buffer *column)
Definition ows_psql.c:164
buffer * buffer_init()
Definition buffer.c:61
buffer * ows_psql_table_name(ows *o, buffer *layer_name)
Definition ows_psql.c:116
list * list_explode(char separator, const buffer *value)
Definition list.c:296
buffer * fe_kvp_featureid(ows *o, wfs_request *wr, buffer *layer_name, list *fid)
Definition fe_filter.c:494
buffer * fe_kvp_bbox(ows *o, wfs_request *wr, buffer *layer_name, ows_bbox *bbox)
Definition fe_filter.c:420
buffer * ows_layer_prefix_to_uri(ows_layer_list *ll, buffer *layer_name_prefix)
Definition ows_layer.c:343
buffer * ows_psql_schema_name(ows *o, buffer *layer_name)
Definition ows_psql.c:96
void mlist_free(mlist *ml)
Definition mlist.c:54
#define OWS_MAX_DOUBLE
Definition ows_struct.h:364
@ WFS_GEOJSON
Definition ows_struct.h:283
@ WFS_GML311
Definition ows_struct.h:281
@ WFS_GML212
Definition ows_struct.h:280
@ WFS_JSONP
Definition ows_struct.h:284
@ FE_NO_ERROR
Definition ows_struct.h:328
@ WFS_ERROR_NO_MATCHING
Definition ows_struct.h:264
wfs_request
Definition ows_struct.h:269
buffer * value
Definition ows_struct.h:83
buffer * key
Definition ows_struct.h:82
struct Array_node * next
Definition ows_struct.h:84
array_node * first
Definition ows_struct.h:88
char * buf
size to next realloc
Definition ows_struct.h:39
size_t use
Definition ows_struct.h:36
enum fe_error_code error_code
Definition ows_struct.h:346
struct List_node * next
Definition ows_struct.h:45
buffer * value
Definition ows_struct.h:44
list_node * first
Definition ows_struct.h:50
struct Mlist_node * next
Definition ows_struct.h:58
list * value
Definition ows_struct.h:57
mlist_node * last
Definition ows_struct.h:64
mlist_node * first
Definition ows_struct.h:63
ows_srs * srs
Definition ows_struct.h:139
double xmax
Definition ows_struct.h:137
double ymin
Definition ows_struct.h:136
double ymax
Definition ows_struct.h:138
double xmin
Definition ows_struct.h:135
list * exclude_items
Definition ows_struct.h:187
ows_version * version
Definition ows_struct.h:353
bool honours_authority_axis_order
Definition ows_struct.h:126
bool is_long
Definition ows_struct.h:129
buffer * auth_name
Definition ows_struct.h:112
bool is_geographic
Definition ows_struct.h:114
bool is_axis_order_gis_friendly
Definition ows_struct.h:118
ows_request * request
Definition ows_struct.h:403
int degree_precision
Definition ows_struct.h:387
buffer * encoding
Definition ows_struct.h:375
int meter_precision
Definition ows_struct.h:388
bool display_bbox
Definition ows_struct.h:393
bool expose_pk
Definition ows_struct.h:394
ows_geobbox * max_geobbox
Definition ows_struct.h:391
buffer * online_resource
Definition ows_struct.h:373
int max_features
Definition ows_struct.h:390
ows_layer_list * layers
Definition ows_struct.h:402
FILE * output
Definition ows_struct.h:382
static void wfs_gml_display_namespaces(ows *o, wfs_request *wr)
static void wfs_gml_display_hits(ows *o, wfs_request *wr, mlist *request_list)
static void wfs_gml_display_results(ows *o, wfs_request *wr, mlist *request_list)
static void wfs_geojson_display_results(ows *o, wfs_request *wr, mlist *request_list)
void wfs_gml_feature_member(ows *o, wfs_request *wr, buffer *layer_name, list *properties, PGresult *res)
void wfs_get_feature(ows *o, wfs_request *wr)
static buffer * wfs_retrieve_sql_request_select(ows *o, wfs_request *wr, buffer *layer_name)
static void wfs_gml_bounded_by(ows *o, wfs_request *wr, double xmin, double ymin, double xmax, double ymax, ows_srs *srs)
static mlist * wfs_retrieve_sql_request_list(ows *o, wfs_request *wr)
void wfs_gml_display_feature(ows *o, wfs_request *wr, buffer *layer_name, buffer *prefix, char *prop_name, buffer *prop_type, char *value)

Generated for tinyows by doxygen 1.12.0