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 <string.h>
29 #include <math.h>
30
31 #include "ows.h"
32
33
34 /*
35 * Initialize a bbox structure
36 */
ows_bbox_init()37 ows_bbox *ows_bbox_init()
38 {
39 ows_bbox *b;
40
41 b = malloc(sizeof(ows_bbox));
42 assert(b);
43
44 b->xmin = -DBL_MAX + 1;
45 b->ymin = -DBL_MAX + 1;
46 b->xmax = DBL_MAX - 1;
47 b->ymax = DBL_MAX - 1;
48
49 b->srs = ows_srs_init();
50
51 return b;
52 }
53
54
55 /*
56 * Free a bbox structure
57 */
ows_bbox_free(ows_bbox * b)58 void ows_bbox_free(ows_bbox * b)
59 {
60 assert(b);
61
62 ows_srs_free(b->srs);
63 free(b);
64 b = NULL;
65 }
66
67
68 /*
69 * Set a given bbox with null area test
70 */
ows_bbox_set(ows * o,ows_bbox * b,double xmin,double ymin,double xmax,double ymax,int srid)71 bool ows_bbox_set(ows * o, ows_bbox * b, double xmin, double ymin, double xmax, double ymax, int srid)
72 {
73 assert(o && b);
74
75 if (xmin >= xmax || ymin >= ymax) return false;
76
77 if ( fabs(xmin - DBL_MAX) < DBL_EPSILON
78 || fabs(ymin - DBL_MAX) < DBL_EPSILON
79 || fabs(xmax - DBL_MAX) < DBL_EPSILON
80 || fabs(ymax - DBL_MAX) < DBL_EPSILON) return false;
81
82 b->xmin = xmin;
83 b->xmax = xmax;
84 b->ymin = ymin;
85 b->ymax = ymax;
86
87 if (srid == 4326) return ows_srs_set_geobbox(o, b->srs);
88
89 return ows_srs_set_from_srid(o, b->srs, srid);
90 }
91
92
93 /*
94 * Set a given bbox from a string like 'xmin,ymin,xmax,ymax'
95 */
ows_bbox_set_from_str(ows * o,ows_bbox * bb,const char * str,int srid)96 bool ows_bbox_set_from_str(ows * o, ows_bbox * bb, const char *str, int srid)
97 {
98 double xmin, ymin, xmax, ymax;
99 ows_srs *s;
100 buffer *b;
101 list *l;
102 int srs = srid;
103 bool ret_srs = true;
104
105 assert(o && bb && str);
106
107 b = buffer_init();
108 buffer_add_str(b, str);
109 l = list_explode(',', b);
110 buffer_free(b);
111
112 /* srs is optional since WFS 1.1 */
113 if (l->size < 4 || l->size > 5) {
114 list_free(l);
115 return false;
116 }
117
118 xmin = strtod(l->first->value->buf, NULL);
119 ymin = strtod(l->first->next->value->buf, NULL);
120 xmax = strtod(l->first->next->next->value->buf, NULL);
121 ymax = strtod(l->first->next->next->next->value->buf, NULL);
122 /* FIXME error handling */
123
124 /* srs is optional since WFS 1.1 */
125 if (l->size == 5) {
126 s = ows_srs_init();
127 ret_srs = ows_srs_set_from_srsname(o, s, l->last->value->buf);
128 srs = s->srid;
129 ows_srs_free(s);
130 }
131
132 list_free(l);
133 if(!ret_srs) return false;
134
135 return ows_bbox_set(o, bb, xmin, ymin, xmax, ymax, srs);
136 }
137
138
139 /*
140 * Set a given bbox matching a feature collection's outerboundaries
141 * or a simple feature's outerboundaries
142 * Bbox is set from a list containing one or several layer names
143 * and optionnaly one or several WHERE SQL statement following each layer name
144 */
ows_bbox_boundaries(ows * o,list * from,list * where,ows_srs * srs)145 ows_bbox *ows_bbox_boundaries(ows * o, list * from, list * where, ows_srs * srs)
146 {
147 ows_bbox *bb;
148 buffer *sql;
149 list *geom;
150 list_node *ln_from, *ln_where, *ln_geom;
151 PGresult *res;
152
153 assert(o && from && where && srs);
154
155 bb = ows_bbox_init();
156
157 if (from->size != where->size) return bb;
158
159 sql = buffer_init();
160 /* Put into a buffer the SQL request calculating an extent */
161 buffer_add_str(sql, "SELECT ST_xmin(g.extent), ST_ymin(g.extent), ST_xmax(g.extent), ST_ymax(g.extent) FROM ");
162 buffer_add_str(sql, "(SELECT ST_Extent(foo.the_geom) as extent FROM ( ");
163
164 /* For each layer name or each geometry column, make an union between retrieved features */
165 for (ln_from = from->first, ln_where = where->first;
166 ln_from;
167 ln_from = ln_from->next, ln_where = ln_where->next) {
168
169 geom = ows_psql_geometry_column(o, ln_from->value);
170
171 for (ln_geom = geom->first ; ln_geom ; ln_geom = ln_geom->next) {
172 buffer_add_str(sql, " (SELECT ST_Transform(\"");
173 buffer_copy(sql, ln_geom->value);
174 buffer_add_str(sql, "\"::geometry, ");
175 buffer_add_int(sql, srs->srid);
176 buffer_add_str(sql, ") AS \"the_geom\" FROM \"");
177 buffer_copy(sql, ows_psql_schema_name(o, ln_from->value));
178 buffer_add_str(sql, "\".\"");
179 buffer_copy(sql, ows_psql_table_name(o, ln_from->value));
180 buffer_add_str(sql, "\" ");
181 buffer_copy(sql, ln_where->value);
182 buffer_add_str(sql, ")");
183
184 if (ln_geom->next) buffer_add_str(sql, " UNION ALL ");
185 }
186
187 if (ln_from->next) buffer_add_str(sql, " UNION ALL ");
188 }
189
190 buffer_add_str(sql, " ) AS foo) AS g");
191
192 res = ows_psql_exec(o, sql->buf);
193 buffer_free(sql);
194
195 if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 4) {
196 PQclear(res);
197 return bb;
198 }
199
200 bb->xmin = strtod(PQgetvalue(res, 0, 0), NULL);
201 bb->ymin = strtod(PQgetvalue(res, 0, 1), NULL);
202 bb->xmax = strtod(PQgetvalue(res, 0, 2), NULL);
203 bb->ymax = strtod(PQgetvalue(res, 0, 3), NULL);
204 /* FIXME Error handling */
205
206 ows_srs_copy(bb->srs, srs);
207
208 PQclear(res);
209 return bb;
210 }
211
212
213 /*
214 * Transform a bbox from initial srid to another srid passed in parameter
215 */
ows_bbox_transform(ows * o,ows_bbox * bb,int srid)216 bool ows_bbox_transform(ows * o, ows_bbox * bb, int srid)
217 {
218 buffer *sql;
219 PGresult *res;
220
221 assert(o && bb);
222
223 sql = buffer_init();
224 buffer_add_str(sql, "SELECT xmin(g), ymin(g), xmax(g), ymax(g) FROM (SELECT ST_Transform(");
225 ows_bbox_to_query(o, bb, sql);
226 buffer_add_str(sql, ")) AS g ) AS foo");
227
228 res = ows_psql_exec(o, sql->buf);
229 buffer_free(sql);
230
231 if (PQresultStatus(res) != PGRES_TUPLES_OK) {
232 PQclear(res);
233 return false;
234 }
235
236 bb->xmin = strtod(PQgetvalue(res, 0, 0), NULL);
237 bb->ymin = strtod(PQgetvalue(res, 0, 1), NULL);
238 bb->xmax = strtod(PQgetvalue(res, 0, 2), NULL);
239 bb->ymax = strtod(PQgetvalue(res, 0, 3), NULL);
240 /* FIXME Error Handling */
241
242 return ows_srs_set_from_srid(o, bb->srs, srid);
243 }
244
245
246 /*
247 * Transform a geobbox into a bbox
248 */
ows_bbox_set_from_geobbox(ows * o,ows_bbox * bb,ows_geobbox * geo)249 bool ows_bbox_set_from_geobbox(ows * o, ows_bbox * bb, ows_geobbox * geo)
250 {
251 assert(bb && geo);
252
253 if (geo->west < geo->east) {
254 bb->xmin = geo->west;
255 bb->xmax = geo->east;
256 } else {
257 bb->xmax = geo->west;
258 bb->xmin = geo->east;
259 }
260
261 if (geo->north < geo->south) {
262 bb->ymin = geo->north;
263 bb->ymax = geo->south;
264 } else {
265 bb->ymax = geo->north;
266 bb->ymin = geo->south;
267 }
268
269 return ows_srs_set_geobbox(o, bb->srs);
270 }
271
272
273 /*
274 * Convert a bbox to PostGIS query Polygon
275 */
ows_bbox_to_query(ows * o,ows_bbox * bbox,buffer * query)276 void ows_bbox_to_query(ows *o, ows_bbox *bbox, buffer *query)
277 {
278 double x1, y1, x2, y2;
279
280 assert(o && bbox && query);
281
282 if (bbox->srs->is_reverse_axis) {
283 x1 = bbox->ymin;
284 y1 = bbox->xmin;
285 x2 = bbox->ymax;
286 y2 = bbox->xmax;
287 } else {
288 x1 = bbox->xmin;
289 y1 = bbox->ymin;
290 x2 = bbox->xmax;
291 y2 = bbox->ymax;
292 }
293
294 /* We use explicit POLYGON geometry rather than BBOX
295 related to precision handle (Float4 vs Double) */
296 buffer_add_str(query, "'SRID=");
297 buffer_add_int(query, bbox->srs->srid);
298 buffer_add_str(query, ";POLYGON((");
299 buffer_add_double(query, x1);
300 buffer_add_str(query, " ");
301 buffer_add_double(query, y1);
302 buffer_add_str(query, ",");
303 buffer_add_double(query, x1);
304 buffer_add_str(query, " ");
305 buffer_add_double(query, y2);
306 buffer_add_str(query, ",");
307 buffer_add_double(query, x2);
308 buffer_add_str(query, " ");
309 buffer_add_double(query, y2);
310 buffer_add_str(query, ",");
311 buffer_add_double(query, x2);
312 buffer_add_str(query, " ");
313 buffer_add_double(query, y1);
314 buffer_add_str(query, ",");
315 buffer_add_double(query, x1);
316 buffer_add_str(query, " ");
317 buffer_add_double(query, y1);
318 buffer_add_str(query, "))'::geometry");
319
320 /* FIXME what about geography ? */
321 }
322
323
324 #ifdef OWS_DEBUG
325 /*
326 * Flush bbox value to a file (mainly to debug purpose)
327 */
ows_bbox_flush(const ows_bbox * b,FILE * output)328 void ows_bbox_flush(const ows_bbox * b, FILE * output)
329 {
330 assert(b);
331
332 fprintf(output, "[%g,%g,%g,%g]\n", b->xmin, b->ymin, b->xmax, b->ymax);
333 ows_srs_flush(b->srs, output);
334 }
335 #endif
336