/src/postgis/liblwgeom/gbox.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * |
6 | | * PostGIS is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation, either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * PostGIS is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with PostGIS. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | ********************************************************************** |
20 | | * |
21 | | * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca> |
22 | | * |
23 | | **********************************************************************/ |
24 | | |
25 | | #include "liblwgeom_internal.h" |
26 | | #include "lwgeodetic.h" |
27 | | #include "lwgeom_log.h" |
28 | | #include <stdlib.h> |
29 | | #include <math.h> |
30 | | |
31 | | |
32 | | GBOX* gbox_new(lwflags_t flags) |
33 | 627 | { |
34 | 627 | GBOX *g = (GBOX*)lwalloc(sizeof(GBOX)); |
35 | 627 | gbox_init(g); |
36 | 627 | g->flags = flags; |
37 | 627 | return g; |
38 | 627 | } |
39 | | |
40 | | void gbox_init(GBOX *gbox) |
41 | 627 | { |
42 | 627 | memset(gbox, 0, sizeof(GBOX)); |
43 | 627 | } |
44 | | |
45 | | GBOX* gbox_clone(const GBOX *gbox) |
46 | 0 | { |
47 | 0 | GBOX *g = lwalloc(sizeof(GBOX)); |
48 | 0 | memcpy(g, gbox, sizeof(GBOX)); |
49 | 0 | return g; |
50 | 0 | } |
51 | | |
52 | | /* TODO to be removed */ |
53 | | BOX3D* box3d_from_gbox(const GBOX *gbox) |
54 | 0 | { |
55 | 0 | BOX3D *b; |
56 | 0 | assert(gbox); |
57 | |
|
58 | 0 | b = lwalloc(sizeof(BOX3D)); |
59 | |
|
60 | 0 | b->xmin = gbox->xmin; |
61 | 0 | b->xmax = gbox->xmax; |
62 | 0 | b->ymin = gbox->ymin; |
63 | 0 | b->ymax = gbox->ymax; |
64 | |
|
65 | 0 | if ( FLAGS_GET_Z(gbox->flags) ) |
66 | 0 | { |
67 | 0 | b->zmin = gbox->zmin; |
68 | 0 | b->zmax = gbox->zmax; |
69 | 0 | } |
70 | 0 | else |
71 | 0 | { |
72 | 0 | b->zmin = b->zmax = 0.0; |
73 | 0 | } |
74 | |
|
75 | 0 | b->srid = SRID_UNKNOWN; |
76 | 0 | return b; |
77 | 0 | } |
78 | | |
79 | | /* TODO to be removed */ |
80 | | GBOX* box3d_to_gbox(const BOX3D *b3d) |
81 | 0 | { |
82 | 0 | GBOX *b; |
83 | 0 | assert(b3d); |
84 | |
|
85 | 0 | b = lwalloc(sizeof(GBOX)); |
86 | |
|
87 | 0 | b->xmin = b3d->xmin; |
88 | 0 | b->xmax = b3d->xmax; |
89 | 0 | b->ymin = b3d->ymin; |
90 | 0 | b->ymax = b3d->ymax; |
91 | 0 | b->zmin = b3d->zmin; |
92 | 0 | b->zmax = b3d->zmax; |
93 | |
|
94 | 0 | return b; |
95 | 0 | } |
96 | | |
97 | | void gbox_expand(GBOX *g, double d) |
98 | 0 | { |
99 | 0 | g->xmin -= d; |
100 | 0 | g->xmax += d; |
101 | 0 | g->ymin -= d; |
102 | 0 | g->ymax += d; |
103 | 0 | if (FLAGS_GET_Z(g->flags) || FLAGS_GET_GEODETIC(g->flags)) |
104 | 0 | { |
105 | 0 | g->zmin -= d; |
106 | 0 | g->zmax += d; |
107 | 0 | } |
108 | 0 | if (FLAGS_GET_M(g->flags)) |
109 | 0 | { |
110 | 0 | g->mmin -= d; |
111 | 0 | g->mmax += d; |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm) |
116 | 0 | { |
117 | 0 | g->xmin -= dx; |
118 | 0 | g->xmax += dx; |
119 | 0 | g->ymin -= dy; |
120 | 0 | g->ymax += dy; |
121 | |
|
122 | 0 | if (FLAGS_GET_Z(g->flags)) |
123 | 0 | { |
124 | 0 | g->zmin -= dz; |
125 | 0 | g->zmax += dz; |
126 | 0 | } |
127 | |
|
128 | 0 | if (FLAGS_GET_M(g->flags)) |
129 | 0 | { |
130 | 0 | g->mmin -= dm; |
131 | 0 | g->mmax += dm; |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout) |
136 | 0 | { |
137 | 0 | if ( ( ! g1 ) && ( ! g2 ) ) |
138 | 0 | return LW_FALSE; |
139 | 0 | else if (!g1) |
140 | 0 | { |
141 | 0 | memcpy(gout, g2, sizeof(GBOX)); |
142 | 0 | return LW_TRUE; |
143 | 0 | } |
144 | 0 | else if (!g2) |
145 | 0 | { |
146 | 0 | memcpy(gout, g1, sizeof(GBOX)); |
147 | 0 | return LW_TRUE; |
148 | 0 | } |
149 | | |
150 | 0 | gout->flags = g1->flags; |
151 | |
|
152 | 0 | gout->xmin = FP_MIN(g1->xmin, g2->xmin); |
153 | 0 | gout->xmax = FP_MAX(g1->xmax, g2->xmax); |
154 | |
|
155 | 0 | gout->ymin = FP_MIN(g1->ymin, g2->ymin); |
156 | 0 | gout->ymax = FP_MAX(g1->ymax, g2->ymax); |
157 | |
|
158 | 0 | gout->zmin = FP_MIN(g1->zmin, g2->zmin); |
159 | 0 | gout->zmax = FP_MAX(g1->zmax, g2->zmax); |
160 | |
|
161 | 0 | return LW_TRUE; |
162 | 0 | } |
163 | | |
164 | | int gbox_same(const GBOX *g1, const GBOX *g2) |
165 | 0 | { |
166 | 0 | if (FLAGS_GET_ZM(g1->flags) != FLAGS_GET_ZM(g2->flags)) |
167 | 0 | return LW_FALSE; |
168 | | |
169 | 0 | if (!gbox_same_2d(g1, g2)) return LW_FALSE; |
170 | | |
171 | 0 | if (FLAGS_GET_Z(g1->flags) && (g1->zmin != g2->zmin || g1->zmax != g2->zmax)) |
172 | 0 | return LW_FALSE; |
173 | 0 | if (FLAGS_GET_M(g1->flags) && (g1->mmin != g2->mmin || g1->mmax != g2->mmax)) |
174 | 0 | return LW_FALSE; |
175 | | |
176 | 0 | return LW_TRUE; |
177 | 0 | } |
178 | | |
179 | | int gbox_same_2d(const GBOX *g1, const GBOX *g2) |
180 | 0 | { |
181 | 0 | if (g1->xmin == g2->xmin && g1->ymin == g2->ymin && |
182 | 0 | g1->xmax == g2->xmax && g1->ymax == g2->ymax) |
183 | 0 | return LW_TRUE; |
184 | 0 | return LW_FALSE; |
185 | 0 | } |
186 | | |
187 | | int gbox_same_2d_float(const GBOX *g1, const GBOX *g2) |
188 | 0 | { |
189 | 0 | if ((g1->xmax == g2->xmax || next_float_up(g1->xmax) == next_float_up(g2->xmax)) && |
190 | 0 | (g1->ymax == g2->ymax || next_float_up(g1->ymax) == next_float_up(g2->ymax)) && |
191 | 0 | (g1->xmin == g2->xmin || next_float_down(g1->xmin) == next_float_down(g1->xmin)) && |
192 | 0 | (g1->ymin == g2->ymin || next_float_down(g2->ymin) == next_float_down(g2->ymin))) |
193 | 0 | return LW_TRUE; |
194 | 0 | return LW_FALSE; |
195 | 0 | } |
196 | | |
197 | | int gbox_is_valid(const GBOX *gbox) |
198 | 0 | { |
199 | | /* X */ |
200 | 0 | if ( ! isfinite(gbox->xmin) || isnan(gbox->xmin) || |
201 | 0 | ! isfinite(gbox->xmax) || isnan(gbox->xmax) ) |
202 | 0 | return LW_FALSE; |
203 | | |
204 | | /* Y */ |
205 | 0 | if ( ! isfinite(gbox->ymin) || isnan(gbox->ymin) || |
206 | 0 | ! isfinite(gbox->ymax) || isnan(gbox->ymax) ) |
207 | 0 | return LW_FALSE; |
208 | | |
209 | | /* Z */ |
210 | 0 | if ( FLAGS_GET_GEODETIC(gbox->flags) || FLAGS_GET_Z(gbox->flags) ) |
211 | 0 | { |
212 | 0 | if ( ! isfinite(gbox->zmin) || isnan(gbox->zmin) || |
213 | 0 | ! isfinite(gbox->zmax) || isnan(gbox->zmax) ) |
214 | 0 | return LW_FALSE; |
215 | 0 | } |
216 | | |
217 | | /* M */ |
218 | 0 | if ( FLAGS_GET_M(gbox->flags) ) |
219 | 0 | { |
220 | 0 | if ( ! isfinite(gbox->mmin) || isnan(gbox->mmin) || |
221 | 0 | ! isfinite(gbox->mmax) || isnan(gbox->mmax) ) |
222 | 0 | return LW_FALSE; |
223 | 0 | } |
224 | | |
225 | 0 | return LW_TRUE; |
226 | 0 | } |
227 | | |
228 | | int gbox_merge_point3d(const POINT3D *p, GBOX *gbox) |
229 | 0 | { |
230 | 0 | if ( gbox->xmin > p->x ) gbox->xmin = p->x; |
231 | 0 | if ( gbox->ymin > p->y ) gbox->ymin = p->y; |
232 | 0 | if ( gbox->zmin > p->z ) gbox->zmin = p->z; |
233 | 0 | if ( gbox->xmax < p->x ) gbox->xmax = p->x; |
234 | 0 | if ( gbox->ymax < p->y ) gbox->ymax = p->y; |
235 | 0 | if ( gbox->zmax < p->z ) gbox->zmax = p->z; |
236 | 0 | return LW_SUCCESS; |
237 | 0 | } |
238 | | |
239 | | int gbox_init_point3d(const POINT3D *p, GBOX *gbox) |
240 | 0 | { |
241 | 0 | gbox->xmin = gbox->xmax = p->x; |
242 | 0 | gbox->ymin = gbox->ymax = p->y; |
243 | 0 | gbox->zmin = gbox->zmax = p->z; |
244 | 0 | return LW_SUCCESS; |
245 | 0 | } |
246 | | |
247 | | int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt) |
248 | 0 | { |
249 | 0 | if ( gbox->xmin > pt->x || gbox->ymin > pt->y || gbox->zmin > pt->z || |
250 | 0 | gbox->xmax < pt->x || gbox->ymax < pt->y || gbox->zmax < pt->z ) |
251 | 0 | { |
252 | 0 | return LW_FALSE; |
253 | 0 | } |
254 | 0 | return LW_TRUE; |
255 | 0 | } |
256 | | |
257 | | int gbox_merge(const GBOX *new_box, GBOX *merge_box) |
258 | 4.24k | { |
259 | 4.24k | assert(merge_box); |
260 | | |
261 | 4.24k | if ( FLAGS_GET_ZM(merge_box->flags) != FLAGS_GET_ZM(new_box->flags) ) |
262 | 0 | return LW_FAILURE; |
263 | | |
264 | 4.24k | if ( new_box->xmin < merge_box->xmin) merge_box->xmin = new_box->xmin; |
265 | 4.24k | if ( new_box->ymin < merge_box->ymin) merge_box->ymin = new_box->ymin; |
266 | 4.24k | if ( new_box->xmax > merge_box->xmax) merge_box->xmax = new_box->xmax; |
267 | 4.24k | if ( new_box->ymax > merge_box->ymax) merge_box->ymax = new_box->ymax; |
268 | | |
269 | 4.24k | if ( FLAGS_GET_Z(merge_box->flags) || FLAGS_GET_GEODETIC(merge_box->flags) ) |
270 | 1.13k | { |
271 | 1.13k | if ( new_box->zmin < merge_box->zmin) merge_box->zmin = new_box->zmin; |
272 | 1.13k | if ( new_box->zmax > merge_box->zmax) merge_box->zmax = new_box->zmax; |
273 | 1.13k | } |
274 | 4.24k | if ( FLAGS_GET_M(merge_box->flags) ) |
275 | 0 | { |
276 | 0 | if ( new_box->mmin < merge_box->mmin) merge_box->mmin = new_box->mmin; |
277 | 0 | if ( new_box->mmax > merge_box->mmax) merge_box->mmax = new_box->mmax; |
278 | 0 | } |
279 | | |
280 | 4.24k | return LW_SUCCESS; |
281 | 4.24k | } |
282 | | |
283 | | int gbox_overlaps(const GBOX *g1, const GBOX *g2) |
284 | 0 | { |
285 | | |
286 | | /* Make sure our boxes are consistent */ |
287 | 0 | if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) ) |
288 | 0 | lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); |
289 | | |
290 | | /* Check X/Y first */ |
291 | 0 | if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || |
292 | 0 | g1->xmin > g2->xmax || g1->ymin > g2->ymax ) |
293 | 0 | return LW_FALSE; |
294 | | |
295 | | /* Deal with the geodetic case special: we only compare the geodetic boxes (x/y/z) */ |
296 | | /* Never the M dimension */ |
297 | 0 | if ( FLAGS_GET_GEODETIC(g1->flags) && FLAGS_GET_GEODETIC(g2->flags) ) |
298 | 0 | { |
299 | 0 | if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) |
300 | 0 | return LW_FALSE; |
301 | 0 | else |
302 | 0 | return LW_TRUE; |
303 | 0 | } |
304 | | |
305 | | /* If both geodetic or both have Z, check Z */ |
306 | 0 | if ( FLAGS_GET_Z(g1->flags) && FLAGS_GET_Z(g2->flags) ) |
307 | 0 | { |
308 | 0 | if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) |
309 | 0 | return LW_FALSE; |
310 | 0 | } |
311 | | |
312 | | /* If both have M, check M */ |
313 | 0 | if ( FLAGS_GET_M(g1->flags) && FLAGS_GET_M(g2->flags) ) |
314 | 0 | { |
315 | 0 | if ( g1->mmax < g2->mmin || g1->mmin > g2->mmax ) |
316 | 0 | return LW_FALSE; |
317 | 0 | } |
318 | | |
319 | 0 | return LW_TRUE; |
320 | 0 | } |
321 | | |
322 | | int |
323 | | gbox_overlaps_2d(const GBOX *g1, const GBOX *g2) |
324 | 0 | { |
325 | | |
326 | | /* Make sure our boxes are consistent */ |
327 | 0 | if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) ) |
328 | 0 | lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); |
329 | | |
330 | | /* Check X/Y first */ |
331 | 0 | if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || |
332 | 0 | g1->xmin > g2->xmax || g1->ymin > g2->ymax ) |
333 | 0 | return LW_FALSE; |
334 | | |
335 | 0 | return LW_TRUE; |
336 | 0 | } |
337 | | |
338 | | int |
339 | | gbox_contains_2d(const GBOX *g1, const GBOX *g2) |
340 | 0 | { |
341 | 0 | if ( ( g2->xmin < g1->xmin ) || ( g2->xmax > g1->xmax ) || |
342 | 0 | ( g2->ymin < g1->ymin ) || ( g2->ymax > g1->ymax ) ) |
343 | 0 | { |
344 | 0 | return LW_FALSE; |
345 | 0 | } |
346 | 0 | return LW_TRUE; |
347 | 0 | } |
348 | | |
349 | | |
350 | | int |
351 | | gbox_within_2d(const GBOX *g1, const GBOX *g2) |
352 | 0 | { |
353 | 0 | if ( ( g1->xmin < g2->xmin ) || ( g1->xmax > g2->xmax ) || |
354 | 0 | ( g1->ymin < g2->ymin ) || ( g1->ymax > g2->ymax ) ) |
355 | 0 | { |
356 | 0 | return LW_FALSE; |
357 | 0 | } |
358 | 0 | return LW_TRUE; |
359 | 0 | } |
360 | | |
361 | | int |
362 | | gbox_contains_point2d(const GBOX *g, const POINT2D *p) |
363 | 0 | { |
364 | 0 | if ( ( g->xmin <= p->x ) && ( g->xmax >= p->x ) && |
365 | 0 | ( g->ymin <= p->y ) && ( g->ymax >= p->y ) ) |
366 | 0 | { |
367 | 0 | return LW_TRUE; |
368 | 0 | } |
369 | 0 | return LW_FALSE; |
370 | 0 | } |
371 | | |
372 | | /** |
373 | | * Warning, this function is only good for x/y/z boxes, used |
374 | | * in unit testing of geodetic box generation. |
375 | | */ |
376 | | GBOX* gbox_from_string(const char *str) |
377 | 0 | { |
378 | 0 | const char *ptr = str; |
379 | 0 | char *nextptr; |
380 | 0 | char *gbox_start = strstr(str, "GBOX(("); |
381 | 0 | GBOX *gbox = gbox_new(lwflags(0,0,1)); |
382 | 0 | if ( ! gbox_start ) return NULL; /* No header found */ |
383 | 0 | ptr += 6; |
384 | 0 | gbox->xmin = strtod(ptr, &nextptr); |
385 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
386 | 0 | ptr = nextptr + 1; |
387 | 0 | gbox->ymin = strtod(ptr, &nextptr); |
388 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
389 | 0 | ptr = nextptr + 1; |
390 | 0 | gbox->zmin = strtod(ptr, &nextptr); |
391 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
392 | 0 | ptr = nextptr + 3; |
393 | 0 | gbox->xmax = strtod(ptr, &nextptr); |
394 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
395 | 0 | ptr = nextptr + 1; |
396 | 0 | gbox->ymax = strtod(ptr, &nextptr); |
397 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
398 | 0 | ptr = nextptr + 1; |
399 | 0 | gbox->zmax = strtod(ptr, &nextptr); |
400 | 0 | if ( ptr == nextptr ) return NULL; /* No double found */ |
401 | 0 | return gbox; |
402 | 0 | } |
403 | | |
404 | | char* gbox_to_string(const GBOX *gbox) |
405 | 0 | { |
406 | 0 | const size_t sz = 138; |
407 | 0 | char *str = NULL; |
408 | |
|
409 | 0 | if ( ! gbox ) |
410 | 0 | return lwstrdup("NULL POINTER"); |
411 | | |
412 | 0 | str = (char*)lwalloc(sz); |
413 | |
|
414 | 0 | if ( FLAGS_GET_GEODETIC(gbox->flags) ) |
415 | 0 | { |
416 | 0 | snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); |
417 | 0 | return str; |
418 | 0 | } |
419 | 0 | if ( FLAGS_GET_Z(gbox->flags) && FLAGS_GET_M(gbox->flags) ) |
420 | 0 | { |
421 | 0 | snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->zmax, gbox->mmax); |
422 | 0 | return str; |
423 | 0 | } |
424 | 0 | if ( FLAGS_GET_Z(gbox->flags) ) |
425 | 0 | { |
426 | 0 | snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); |
427 | 0 | return str; |
428 | 0 | } |
429 | 0 | if ( FLAGS_GET_M(gbox->flags) ) |
430 | 0 | { |
431 | 0 | snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->mmax); |
432 | 0 | return str; |
433 | 0 | } |
434 | 0 | snprintf(str, sz, "GBOX((%.8g,%.8g),(%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->xmax, gbox->ymax); |
435 | 0 | return str; |
436 | 0 | } |
437 | | |
438 | | GBOX* gbox_copy(const GBOX *box) |
439 | 0 | { |
440 | 0 | GBOX *copy = (GBOX*)lwalloc(sizeof(GBOX)); |
441 | 0 | memcpy(copy, box, sizeof(GBOX)); |
442 | 0 | return copy; |
443 | 0 | } |
444 | | |
445 | | void gbox_duplicate(const GBOX *original, GBOX *duplicate) |
446 | 1.75k | { |
447 | 1.75k | assert(duplicate); |
448 | 1.75k | assert(original); |
449 | 1.75k | memcpy(duplicate, original, sizeof(GBOX)); |
450 | 1.75k | } |
451 | | |
452 | | size_t gbox_serialized_size(lwflags_t flags) |
453 | 0 | { |
454 | 0 | if (FLAGS_GET_GEODETIC(flags)) |
455 | 0 | return 6 * sizeof(float); |
456 | 0 | else |
457 | 0 | return 2 * FLAGS_NDIMS(flags) * sizeof(float); |
458 | 0 | } |
459 | | |
460 | | |
461 | | /* ******************************************************************************** |
462 | | ** Compute cartesian bounding GBOX boxes from LWGEOM. |
463 | | */ |
464 | | |
465 | | int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox) |
466 | 0 | { |
467 | 0 | POINT2D xmin, ymin, xmax, ymax; |
468 | 0 | POINT2D C; |
469 | 0 | int A2_side; |
470 | 0 | double radius_A; |
471 | |
|
472 | 0 | LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called."); |
473 | |
|
474 | 0 | radius_A = lw_arc_center(A1, A2, A3, &C); |
475 | | |
476 | | /* Negative radius signals straight line, p1/p2/p3 are collinear */ |
477 | 0 | if (radius_A < 0.0) |
478 | 0 | { |
479 | 0 | gbox->xmin = FP_MIN(A1->x, A3->x); |
480 | 0 | gbox->ymin = FP_MIN(A1->y, A3->y); |
481 | 0 | gbox->xmax = FP_MAX(A1->x, A3->x); |
482 | 0 | gbox->ymax = FP_MAX(A1->y, A3->y); |
483 | 0 | return LW_SUCCESS; |
484 | 0 | } |
485 | | |
486 | | /* Matched start/end points imply circle */ |
487 | 0 | if ( A1->x == A3->x && A1->y == A3->y ) |
488 | 0 | { |
489 | 0 | gbox->xmin = C.x - radius_A; |
490 | 0 | gbox->ymin = C.y - radius_A; |
491 | 0 | gbox->xmax = C.x + radius_A; |
492 | 0 | gbox->ymax = C.y + radius_A; |
493 | 0 | return LW_SUCCESS; |
494 | 0 | } |
495 | | |
496 | | /* First approximation, bounds of start/end points */ |
497 | 0 | gbox->xmin = FP_MIN(A1->x, A3->x); |
498 | 0 | gbox->ymin = FP_MIN(A1->y, A3->y); |
499 | 0 | gbox->xmax = FP_MAX(A1->x, A3->x); |
500 | 0 | gbox->ymax = FP_MAX(A1->y, A3->y); |
501 | | |
502 | | /* Create points for the possible extrema */ |
503 | 0 | xmin.x = C.x - radius_A; |
504 | 0 | xmin.y = C.y; |
505 | 0 | ymin.x = C.x; |
506 | 0 | ymin.y = C.y - radius_A; |
507 | 0 | xmax.x = C.x + radius_A; |
508 | 0 | xmax.y = C.y; |
509 | 0 | ymax.x = C.x; |
510 | 0 | ymax.y = C.y + radius_A; |
511 | | |
512 | | /* Divide the circle into two parts, one on each side of a line |
513 | | joining p1 and p3. The circle extrema on the same side of that line |
514 | | as p2 is on, are also the extrema of the bbox. */ |
515 | |
|
516 | 0 | A2_side = lw_segment_side(A1, A3, A2); |
517 | |
|
518 | 0 | if ( A2_side == lw_segment_side(A1, A3, &xmin) ) |
519 | 0 | gbox->xmin = xmin.x; |
520 | |
|
521 | 0 | if ( A2_side == lw_segment_side(A1, A3, &ymin) ) |
522 | 0 | gbox->ymin = ymin.y; |
523 | |
|
524 | 0 | if ( A2_side == lw_segment_side(A1, A3, &xmax) ) |
525 | 0 | gbox->xmax = xmax.x; |
526 | |
|
527 | 0 | if ( A2_side == lw_segment_side(A1, A3, &ymax) ) |
528 | 0 | gbox->ymax = ymax.y; |
529 | |
|
530 | 0 | return LW_SUCCESS; |
531 | 0 | } |
532 | | |
533 | | |
534 | | static int lw_arc_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox) |
535 | 0 | { |
536 | 0 | int rv; |
537 | |
|
538 | 0 | LWDEBUG(2, "lw_arc_calculate_gbox_cartesian called."); |
539 | |
|
540 | 0 | rv = lw_arc_calculate_gbox_cartesian_2d((POINT2D*)p1, (POINT2D*)p2, (POINT2D*)p3, gbox); |
541 | 0 | gbox->zmin = FP_MIN(p1->z, p3->z); |
542 | 0 | gbox->mmin = FP_MIN(p1->m, p3->m); |
543 | 0 | gbox->zmax = FP_MAX(p1->z, p3->z); |
544 | 0 | gbox->mmax = FP_MAX(p1->m, p3->m); |
545 | 0 | return rv; |
546 | 0 | } |
547 | | |
548 | | static void |
549 | | ptarray_calculate_gbox_cartesian_2d(const POINTARRAY *pa, GBOX *gbox) |
550 | 3.58k | { |
551 | 3.58k | const POINT2D *p = getPoint2d_cp(pa, 0); |
552 | | |
553 | 3.58k | gbox->xmax = gbox->xmin = p->x; |
554 | 3.58k | gbox->ymax = gbox->ymin = p->y; |
555 | | |
556 | 112k | for (uint32_t i = 1; i < pa->npoints; i++) |
557 | 109k | { |
558 | 109k | p = getPoint2d_cp(pa, i); |
559 | 109k | gbox->xmin = FP_MIN(gbox->xmin, p->x); |
560 | 109k | gbox->xmax = FP_MAX(gbox->xmax, p->x); |
561 | 109k | gbox->ymin = FP_MIN(gbox->ymin, p->y); |
562 | 109k | gbox->ymax = FP_MAX(gbox->ymax, p->y); |
563 | 109k | } |
564 | 3.58k | } |
565 | | |
566 | | /* Works with X/Y/Z. Needs to be adjusted after if X/Y/M was required */ |
567 | | static void |
568 | | ptarray_calculate_gbox_cartesian_3d(const POINTARRAY *pa, GBOX *gbox) |
569 | 1.28k | { |
570 | 1.28k | const POINT3D *p = getPoint3d_cp(pa, 0); |
571 | | |
572 | 1.28k | gbox->xmax = gbox->xmin = p->x; |
573 | 1.28k | gbox->ymax = gbox->ymin = p->y; |
574 | 1.28k | gbox->zmax = gbox->zmin = p->z; |
575 | | |
576 | 29.6k | for (uint32_t i = 1; i < pa->npoints; i++) |
577 | 28.3k | { |
578 | 28.3k | p = getPoint3d_cp(pa, i); |
579 | 28.3k | gbox->xmin = FP_MIN(gbox->xmin, p->x); |
580 | 28.3k | gbox->xmax = FP_MAX(gbox->xmax, p->x); |
581 | 28.3k | gbox->ymin = FP_MIN(gbox->ymin, p->y); |
582 | 28.3k | gbox->ymax = FP_MAX(gbox->ymax, p->y); |
583 | 28.3k | gbox->zmin = FP_MIN(gbox->zmin, p->z); |
584 | 28.3k | gbox->zmax = FP_MAX(gbox->zmax, p->z); |
585 | 28.3k | } |
586 | 1.28k | } |
587 | | |
588 | | static void |
589 | | ptarray_calculate_gbox_cartesian_4d(const POINTARRAY *pa, GBOX *gbox) |
590 | 0 | { |
591 | 0 | const POINT4D *p = getPoint4d_cp(pa, 0); |
592 | |
|
593 | 0 | gbox->xmax = gbox->xmin = p->x; |
594 | 0 | gbox->ymax = gbox->ymin = p->y; |
595 | 0 | gbox->zmax = gbox->zmin = p->z; |
596 | 0 | gbox->mmax = gbox->mmin = p->m; |
597 | |
|
598 | 0 | for (uint32_t i = 1; i < pa->npoints; i++) |
599 | 0 | { |
600 | 0 | p = getPoint4d_cp(pa, i); |
601 | 0 | gbox->xmin = FP_MIN(gbox->xmin, p->x); |
602 | 0 | gbox->xmax = FP_MAX(gbox->xmax, p->x); |
603 | 0 | gbox->ymin = FP_MIN(gbox->ymin, p->y); |
604 | 0 | gbox->ymax = FP_MAX(gbox->ymax, p->y); |
605 | 0 | gbox->zmin = FP_MIN(gbox->zmin, p->z); |
606 | 0 | gbox->zmax = FP_MAX(gbox->zmax, p->z); |
607 | 0 | gbox->mmin = FP_MIN(gbox->mmin, p->m); |
608 | 0 | gbox->mmax = FP_MAX(gbox->mmax, p->m); |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | int |
613 | | ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox) |
614 | 154k | { |
615 | 154k | if (!pa || pa->npoints == 0) |
616 | 149k | return LW_FAILURE; |
617 | 4.87k | if (!gbox) |
618 | 0 | return LW_FAILURE; |
619 | | |
620 | 4.87k | int has_z = FLAGS_GET_Z(pa->flags); |
621 | 4.87k | int has_m = FLAGS_GET_M(pa->flags); |
622 | 4.87k | gbox->flags = lwflags(has_z, has_m, 0); |
623 | 4.87k | LWDEBUGF(4, "ptarray_calculate_gbox Z: %d M: %d", has_z, has_m); |
624 | 4.87k | int coordinates = 2 + has_z + has_m; |
625 | | |
626 | 4.87k | switch (coordinates) |
627 | 4.87k | { |
628 | 3.58k | case 2: |
629 | 3.58k | { |
630 | 3.58k | ptarray_calculate_gbox_cartesian_2d(pa, gbox); |
631 | 3.58k | break; |
632 | 0 | } |
633 | 1.28k | case 3: |
634 | 1.28k | { |
635 | 1.28k | if (has_z) |
636 | 1.28k | { |
637 | 1.28k | ptarray_calculate_gbox_cartesian_3d(pa, gbox); |
638 | 1.28k | } |
639 | 0 | else |
640 | 0 | { |
641 | 0 | double zmin = gbox->zmin; |
642 | 0 | double zmax = gbox->zmax; |
643 | 0 | ptarray_calculate_gbox_cartesian_3d(pa, gbox); |
644 | 0 | gbox->mmin = gbox->zmin; |
645 | 0 | gbox->mmax = gbox->zmax; |
646 | 0 | gbox->zmin = zmin; |
647 | 0 | gbox->zmax = zmax; |
648 | 0 | } |
649 | 1.28k | break; |
650 | 0 | } |
651 | 0 | default: |
652 | 0 | { |
653 | 0 | ptarray_calculate_gbox_cartesian_4d(pa, gbox); |
654 | 0 | break; |
655 | 0 | } |
656 | 4.87k | } |
657 | 4.87k | return LW_SUCCESS; |
658 | 4.87k | } |
659 | | |
660 | | static int lwcircstring_calculate_gbox_cartesian(LWCIRCSTRING *curve, GBOX *gbox) |
661 | 0 | { |
662 | 0 | GBOX tmp = {0}; |
663 | 0 | POINT4D p1, p2, p3; |
664 | 0 | uint32_t i; |
665 | |
|
666 | 0 | if (!curve) return LW_FAILURE; |
667 | 0 | if (curve->points->npoints < 3) return LW_FAILURE; |
668 | | |
669 | 0 | tmp.flags = |
670 | 0 | lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0); |
671 | | |
672 | | /* Initialize */ |
673 | 0 | gbox->xmin = gbox->ymin = gbox->zmin = gbox->mmin = FLT_MAX; |
674 | 0 | gbox->xmax = gbox->ymax = gbox->zmax = gbox->mmax = -1*FLT_MAX; |
675 | |
|
676 | 0 | for ( i = 2; i < curve->points->npoints; i += 2 ) |
677 | 0 | { |
678 | 0 | getPoint4d_p(curve->points, i-2, &p1); |
679 | 0 | getPoint4d_p(curve->points, i-1, &p2); |
680 | 0 | getPoint4d_p(curve->points, i, &p3); |
681 | |
|
682 | 0 | if (lw_arc_calculate_gbox_cartesian(&p1, &p2, &p3, &tmp) == LW_FAILURE) |
683 | 0 | continue; |
684 | | |
685 | 0 | gbox_merge(&tmp, gbox); |
686 | 0 | } |
687 | |
|
688 | 0 | return LW_SUCCESS; |
689 | 0 | } |
690 | | |
691 | | static int lwpoint_calculate_gbox_cartesian(LWPOINT *point, GBOX *gbox) |
692 | 135k | { |
693 | 135k | if ( ! point ) return LW_FAILURE; |
694 | 135k | return ptarray_calculate_gbox_cartesian( point->point, gbox ); |
695 | 135k | } |
696 | | |
697 | | static int lwline_calculate_gbox_cartesian(LWLINE *line, GBOX *gbox) |
698 | 17.8k | { |
699 | 17.8k | if ( ! line ) return LW_FAILURE; |
700 | 17.8k | return ptarray_calculate_gbox_cartesian( line->points, gbox ); |
701 | 17.8k | } |
702 | | |
703 | | static int lwtriangle_calculate_gbox_cartesian(LWTRIANGLE *triangle, GBOX *gbox) |
704 | 0 | { |
705 | 0 | if ( ! triangle ) return LW_FAILURE; |
706 | 0 | return ptarray_calculate_gbox_cartesian( triangle->points, gbox ); |
707 | 0 | } |
708 | | |
709 | | static int lwpoly_calculate_gbox_cartesian(LWPOLY *poly, GBOX *gbox) |
710 | 1.75k | { |
711 | 1.75k | if ( ! poly ) return LW_FAILURE; |
712 | 1.75k | if ( poly->nrings == 0 ) return LW_FAILURE; |
713 | | /* Just need to check outer ring */ |
714 | 893 | return ptarray_calculate_gbox_cartesian( poly->rings[0], gbox ); |
715 | 1.75k | } |
716 | | |
717 | | static int lwcollection_calculate_gbox_cartesian(LWCOLLECTION *coll, GBOX *gbox) |
718 | 3.96k | { |
719 | 3.96k | GBOX subbox = {0}; |
720 | 3.96k | uint32_t i; |
721 | 3.96k | int result = LW_FAILURE; |
722 | 3.96k | int first = LW_TRUE; |
723 | 3.96k | assert(coll); |
724 | 3.96k | if ( (coll->ngeoms == 0) || !gbox) |
725 | 2.01k | return LW_FAILURE; |
726 | | |
727 | 1.95k | subbox.flags = coll->flags; |
728 | | |
729 | 160k | for ( i = 0; i < coll->ngeoms; i++ ) |
730 | 158k | { |
731 | 158k | if ( lwgeom_calculate_gbox_cartesian((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS ) |
732 | 6.00k | { |
733 | | /* Keep a copy of the sub-bounding box for later |
734 | | if ( coll->geoms[i]->bbox ) |
735 | | lwfree(coll->geoms[i]->bbox); |
736 | | coll->geoms[i]->bbox = gbox_copy(&subbox); */ |
737 | 6.00k | if ( first ) |
738 | 1.75k | { |
739 | 1.75k | gbox_duplicate(&subbox, gbox); |
740 | 1.75k | first = LW_FALSE; |
741 | 1.75k | } |
742 | 4.24k | else |
743 | 4.24k | { |
744 | 4.24k | gbox_merge(&subbox, gbox); |
745 | 4.24k | } |
746 | 6.00k | result = LW_SUCCESS; |
747 | 6.00k | } |
748 | 158k | } |
749 | 1.95k | return result; |
750 | 3.96k | } |
751 | | |
752 | | int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox) |
753 | 159k | { |
754 | 159k | if ( ! lwgeom ) return LW_FAILURE; |
755 | 159k | LWDEBUGF(4, "lwgeom_calculate_gbox got type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type)); |
756 | | |
757 | 159k | switch (lwgeom->type) |
758 | 159k | { |
759 | 135k | case POINTTYPE: |
760 | 135k | return lwpoint_calculate_gbox_cartesian((LWPOINT *)lwgeom, gbox); |
761 | 17.8k | case LINETYPE: |
762 | 17.8k | return lwline_calculate_gbox_cartesian((LWLINE *)lwgeom, gbox); |
763 | 0 | case CIRCSTRINGTYPE: |
764 | 0 | return lwcircstring_calculate_gbox_cartesian((LWCIRCSTRING *)lwgeom, gbox); |
765 | 1.75k | case POLYGONTYPE: |
766 | 1.75k | return lwpoly_calculate_gbox_cartesian((LWPOLY *)lwgeom, gbox); |
767 | 0 | case TRIANGLETYPE: |
768 | 0 | return lwtriangle_calculate_gbox_cartesian((LWTRIANGLE *)lwgeom, gbox); |
769 | 0 | case COMPOUNDTYPE: |
770 | 0 | case CURVEPOLYTYPE: |
771 | 2.08k | case MULTIPOINTTYPE: |
772 | 2.57k | case MULTILINETYPE: |
773 | 2.57k | case MULTICURVETYPE: |
774 | 3.11k | case MULTIPOLYGONTYPE: |
775 | 3.11k | case MULTISURFACETYPE: |
776 | 3.11k | case POLYHEDRALSURFACETYPE: |
777 | 3.11k | case TINTYPE: |
778 | 3.96k | case COLLECTIONTYPE: |
779 | 3.96k | return lwcollection_calculate_gbox_cartesian((LWCOLLECTION *)lwgeom, gbox); |
780 | 159k | } |
781 | | /* Never get here, please. */ |
782 | 0 | lwerror("unsupported type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type)); |
783 | 0 | return LW_FAILURE; |
784 | 159k | } |
785 | | |
786 | | void gbox_float_round(GBOX *gbox) |
787 | 0 | { |
788 | 0 | gbox->xmin = next_float_down(gbox->xmin); |
789 | 0 | gbox->xmax = next_float_up(gbox->xmax); |
790 | |
|
791 | 0 | gbox->ymin = next_float_down(gbox->ymin); |
792 | 0 | gbox->ymax = next_float_up(gbox->ymax); |
793 | |
|
794 | 0 | if ( FLAGS_GET_M(gbox->flags) ) |
795 | 0 | { |
796 | 0 | gbox->mmin = next_float_down(gbox->mmin); |
797 | 0 | gbox->mmax = next_float_up(gbox->mmax); |
798 | 0 | } |
799 | |
|
800 | 0 | if ( FLAGS_GET_Z(gbox->flags) ) |
801 | 0 | { |
802 | 0 | gbox->zmin = next_float_down(gbox->zmin); |
803 | 0 | gbox->zmax = next_float_up(gbox->zmax); |
804 | 0 | } |
805 | 0 | } |
806 | | |
807 | | uint64_t |
808 | | gbox_get_sortable_hash(const GBOX *g, const int32_t srid) |
809 | 0 | { |
810 | 0 | union floatuint { |
811 | 0 | uint32_t u; |
812 | 0 | float f; |
813 | 0 | }; |
814 | |
|
815 | 0 | union floatuint x, y; |
816 | | |
817 | | /* |
818 | | * Since in theory the bitwise representation of an IEEE |
819 | | * float is sortable (exponents come before mantissa, etc) |
820 | | * we just copy the bits directly into an int and then |
821 | | * interleave those ints. |
822 | | */ |
823 | 0 | if (FLAGS_GET_GEODETIC(g->flags)) |
824 | 0 | { |
825 | 0 | GEOGRAPHIC_POINT gpt; |
826 | 0 | POINT3D p; |
827 | 0 | p.x = (g->xmax + g->xmin) / 2.0; |
828 | 0 | p.y = (g->ymax + g->ymin) / 2.0; |
829 | 0 | p.z = (g->zmax + g->zmin) / 2.0; |
830 | 0 | normalize(&p); |
831 | 0 | cart2geog(&p, &gpt); |
832 | | /* We know range for geography, so build the curve taking it into account */ |
833 | 0 | x.f = 1.5 + gpt.lon / 512.0; |
834 | 0 | y.f = 1.5 + gpt.lat / 256.0; |
835 | 0 | } |
836 | 0 | else |
837 | 0 | { |
838 | 0 | x.f = (g->xmax + g->xmin) / 2; |
839 | 0 | y.f = (g->ymax + g->ymin) / 2; |
840 | | /* |
841 | | * Tweak for popular SRID values: push floating point values into 1..2 range, |
842 | | * a region where exponent is constant and thus Hilbert curve |
843 | | * doesn't have compression artifact when X or Y value is close to 0. |
844 | | * If someone has out of bounds value it will still expose the arifact but not crash. |
845 | | * TODO: reconsider when we will have machinery to properly get bounds by SRID. |
846 | | */ |
847 | 0 | if (srid == 3857 || srid == 3395) |
848 | 0 | { |
849 | 0 | x.f = 1.5 + x.f / 67108864.0; |
850 | 0 | y.f = 1.5 + y.f / 67108864.0; |
851 | 0 | } |
852 | 0 | else if (srid == 4326) |
853 | 0 | { |
854 | 0 | x.f = 1.5 + x.f / 512.0; |
855 | 0 | y.f = 1.5 + y.f / 256.0; |
856 | 0 | } |
857 | 0 | } |
858 | |
|
859 | 0 | return uint32_hilbert(y.u, x.u); |
860 | 0 | } |