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