/src/postgis/liblwgeom/lwalgorithm.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 2008 Paul Ramsey |
22 | | * |
23 | | **********************************************************************/ |
24 | | |
25 | | |
26 | | #include "liblwgeom_internal.h" |
27 | | #include "lwgeom_log.h" |
28 | | #include <ctype.h> /* for tolower */ |
29 | | #include <stdbool.h> |
30 | | |
31 | | int |
32 | | p4d_same(const POINT4D *p1, const POINT4D *p2) |
33 | 0 | { |
34 | 0 | return p1->x == p2->x |
35 | 0 | && p1->y == p2->y |
36 | 0 | && p1->z == p2->z |
37 | 0 | && p1->m == p2->m; |
38 | 0 | } |
39 | | |
40 | | int |
41 | | p3d_same(const POINT3D *p1, const POINT3D *p2) |
42 | 1.10M | { |
43 | 1.10M | return p1->x == p2->x |
44 | 1.10M | && p1->y == p2->y |
45 | 1.10M | && p1->z == p2->z; |
46 | 1.10M | } |
47 | | |
48 | | int |
49 | | p3dz_same(const POINT3DZ *p1, const POINT3DZ *p2) |
50 | 0 | { |
51 | 0 | return p1->x == p2->x |
52 | 0 | && p1->y == p2->y |
53 | 0 | && p1->z == p2->z; |
54 | 0 | } |
55 | | |
56 | | int |
57 | | p2d_same(const POINT2D *p1, const POINT2D *p2) |
58 | 0 | { |
59 | 0 | return p1->x == p2->x |
60 | 0 | && p1->y == p2->y; |
61 | 0 | } |
62 | | |
63 | | /** |
64 | | * lw_segment_side() |
65 | | * |
66 | | * Return -1 if point Q is left of segment P |
67 | | * Return 1 if point Q is right of segment P |
68 | | * Return 0 if point Q in on segment P |
69 | | */ |
70 | | int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q) |
71 | 152k | { |
72 | 152k | double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) ); |
73 | 152k | return SIGNUM(side); |
74 | 152k | } |
75 | | |
76 | | /** |
77 | | * Returns the length of a linear segment |
78 | | */ |
79 | | double |
80 | | lw_seg_length(const POINT2D *A1, const POINT2D *A2) |
81 | 0 | { |
82 | 0 | return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y)); |
83 | 0 | } |
84 | | |
85 | | /** |
86 | | * Returns true if P is on the same side of the plane partition |
87 | | * defined by A1/A3 as A2 is. Only makes sense if P has already been |
88 | | * determined to be on the circle defined by A1/A2/A3. |
89 | | */ |
90 | | int |
91 | | lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) |
92 | 0 | { |
93 | 0 | int pt_side = lw_segment_side(A1, A3, P); |
94 | 0 | int arc_side = lw_segment_side(A1, A3, A2); |
95 | 0 | return pt_side == 0 || pt_side == arc_side; |
96 | 0 | } |
97 | | |
98 | | /** |
99 | | * Returns true if P is between A1/A2. Only makes sense if P has already been |
100 | | * determined to be on the line defined by A1/A2. |
101 | | */ |
102 | | int |
103 | | lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2) |
104 | 0 | { |
105 | 0 | return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) || |
106 | 0 | ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y)); |
107 | 0 | } |
108 | | |
109 | | int |
110 | | lw_pt_on_segment(const POINT2D* A1, const POINT2D* A2, const POINT2D* P) |
111 | 0 | { |
112 | 0 | int side = lw_segment_side(A1, A2, P); |
113 | 0 | if (side != 0) return LW_FALSE; |
114 | 0 | return lw_pt_in_seg(P, A1, A2); |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * Returns true if arc A is actually a point (all vertices are the same) . |
119 | | */ |
120 | | int |
121 | | lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) |
122 | 0 | { |
123 | 0 | if ( A1->x == A2->x && A2->x == A3->x && |
124 | 0 | A1->y == A2->y && A2->y == A3->y ) |
125 | 0 | return LW_TRUE; |
126 | 0 | else |
127 | 0 | return LW_FALSE; |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * Returns the length of a circular arc segment |
132 | | */ |
133 | | double |
134 | | lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) |
135 | 0 | { |
136 | 0 | POINT2D C; |
137 | 0 | double radius_A, circumference_A; |
138 | 0 | int a2_side, clockwise; |
139 | 0 | double a1, a3; |
140 | 0 | double angle; |
141 | |
|
142 | 0 | if ( lw_arc_is_pt(A1, A2, A3) ) |
143 | 0 | return 0.0; |
144 | | |
145 | 0 | radius_A = lw_arc_center(A1, A2, A3, &C); |
146 | | |
147 | | /* Co-linear! Return linear distance! */ |
148 | 0 | if ( radius_A < 0 ) |
149 | 0 | { |
150 | 0 | double dx = A1->x - A3->x; |
151 | 0 | double dy = A1->y - A3->y; |
152 | 0 | return sqrt(dx*dx + dy*dy); |
153 | 0 | } |
154 | | |
155 | | /* Closed circle! Return the circumference! */ |
156 | 0 | circumference_A = M_PI * 2 * radius_A; |
157 | 0 | if ( p2d_same(A1, A3) ) |
158 | 0 | return circumference_A; |
159 | | |
160 | | /* Determine the orientation of the arc */ |
161 | 0 | a2_side = lw_segment_side(A1, A3, A2); |
162 | | |
163 | | /* The side of the A1/A3 line that A2 falls on dictates the sweep |
164 | | direction from A1 to A3. */ |
165 | 0 | if ( a2_side == -1 ) |
166 | 0 | clockwise = LW_TRUE; |
167 | 0 | else |
168 | 0 | clockwise = LW_FALSE; |
169 | | |
170 | | /* Angles of each point that defines the arc section */ |
171 | 0 | a1 = atan2(A1->y - C.y, A1->x - C.x); |
172 | 0 | a3 = atan2(A3->y - C.y, A3->x - C.x); |
173 | | |
174 | | /* What's the sweep from A1 to A3? */ |
175 | 0 | if ( clockwise ) |
176 | 0 | { |
177 | 0 | if ( a1 > a3 ) |
178 | 0 | angle = a1 - a3; |
179 | 0 | else |
180 | 0 | angle = 2*M_PI + a1 - a3; |
181 | 0 | } |
182 | 0 | else |
183 | 0 | { |
184 | 0 | if ( a3 > a1 ) |
185 | 0 | angle = a3 - a1; |
186 | 0 | else |
187 | 0 | angle = 2*M_PI + a3 - a1; |
188 | 0 | } |
189 | | |
190 | | /* Length as proportion of circumference */ |
191 | 0 | return circumference_A * (angle / (2*M_PI)); |
192 | 0 | } |
193 | | |
194 | | int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q) |
195 | 0 | { |
196 | 0 | POINT2D C; |
197 | 0 | double radius_A; |
198 | 0 | double side_Q, side_A2; |
199 | 0 | double d; |
200 | |
|
201 | 0 | side_Q = lw_segment_side(A1, A3, Q); |
202 | 0 | radius_A = lw_arc_center(A1, A2, A3, &C); |
203 | 0 | side_A2 = lw_segment_side(A1, A3, A2); |
204 | | |
205 | | /* Linear case */ |
206 | 0 | if ( radius_A < 0 ) |
207 | 0 | return side_Q; |
208 | | |
209 | 0 | d = distance2d_pt_pt(Q, &C); |
210 | | |
211 | | /* Q is on the arc boundary */ |
212 | 0 | if ( d == radius_A && side_Q == side_A2 ) |
213 | 0 | { |
214 | 0 | return 0; |
215 | 0 | } |
216 | | |
217 | | /* Q on A1-A3 line, so its on opposite side to A2 */ |
218 | 0 | if ( side_Q == 0 ) |
219 | 0 | { |
220 | 0 | return -1 * side_A2; |
221 | 0 | } |
222 | | |
223 | | /* |
224 | | * Q is inside the arc boundary, so it's not on the side we |
225 | | * might think from examining only the end points |
226 | | */ |
227 | 0 | if ( d < radius_A && side_Q == side_A2 ) |
228 | 0 | { |
229 | 0 | side_Q *= -1; |
230 | 0 | } |
231 | |
|
232 | 0 | return side_Q; |
233 | 0 | } |
234 | | |
235 | | /** |
236 | | * Determines the center of the circle defined by the three given points. |
237 | | * In the event the circle is complete, the midpoint of the segment defined |
238 | | * by the first and second points is returned. If the points are collinear, |
239 | | * as determined by equal slopes, then -1.0 is returned. If the interior |
240 | | * point is coincident with either end point, they are taken as collinear. |
241 | | * For non-collinear cases, arc radious is returned. |
242 | | */ |
243 | | double |
244 | | lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result) |
245 | 3.61M | { |
246 | 3.61M | POINT2D c; |
247 | 3.61M | double cx, cy, cr; |
248 | 3.61M | double dx21, dy21, dx31, dy31, h21, h31, d; |
249 | | |
250 | 3.61M | c.x = c.y = 0.0; |
251 | | |
252 | 3.61M | LWDEBUGF(2, "lw_arc_center called (%.16f,%.16f), (%.16f,%.16f), (%.16f,%.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y); |
253 | | |
254 | | /* Closed circle */ |
255 | 3.61M | if (fabs(p1->x - p3->x) < EPSILON_SQLMM && |
256 | 3.60M | fabs(p1->y - p3->y) < EPSILON_SQLMM) |
257 | 3.59M | { |
258 | 3.59M | cx = p1->x + (p2->x - p1->x) / 2.0; |
259 | 3.59M | cy = p1->y + (p2->y - p1->y) / 2.0; |
260 | 3.59M | c.x = cx; |
261 | 3.59M | c.y = cy; |
262 | 3.59M | *result = c; |
263 | 3.59M | cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0)); |
264 | 3.59M | return cr; |
265 | 3.59M | } |
266 | | |
267 | | /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */ |
268 | 16.2k | dx21 = p2->x - p1->x; |
269 | 16.2k | dy21 = p2->y - p1->y; |
270 | 16.2k | dx31 = p3->x - p1->x; |
271 | 16.2k | dy31 = p3->y - p1->y; |
272 | | |
273 | 16.2k | h21 = pow(dx21, 2.0) + pow(dy21, 2.0); |
274 | 16.2k | h31 = pow(dx31, 2.0) + pow(dy31, 2.0); |
275 | | |
276 | | /* 2 * |Cross product|, d<0 means clockwise and d>0 counterclockwise sweeping angle */ |
277 | 16.2k | d = 2 * (dx21 * dy31 - dx31 * dy21); |
278 | | |
279 | | /* Check colinearity, |Cross product| = 0 */ |
280 | 16.2k | if (fabs(d) < EPSILON_SQLMM) |
281 | 6.45k | return -1.0; |
282 | | |
283 | | /* Calculate centroid coordinates and radius */ |
284 | 9.83k | cx = p1->x + (h21 * dy31 - h31 * dy21) / d; |
285 | 9.83k | cy = p1->y - (h21 * dx31 - h31 * dx21) / d; |
286 | 9.83k | c.x = cx; |
287 | 9.83k | c.y = cy; |
288 | 9.83k | *result = c; |
289 | 9.83k | cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2)); |
290 | | |
291 | 9.83k | LWDEBUGF(2, "lw_arc_center center is (%.16f,%.16f)", result->x, result->y); |
292 | | |
293 | 9.83k | return cr; |
294 | 16.2k | } |
295 | | |
296 | | static int |
297 | | lw_seg_interact(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2) |
298 | 0 | { |
299 | 0 | double minq=FP_MIN(q1->x,q2->x); |
300 | 0 | double maxq=FP_MAX(q1->x,q2->x); |
301 | 0 | double minp=FP_MIN(p1->x,p2->x); |
302 | 0 | double maxp=FP_MAX(p1->x,p2->x); |
303 | |
|
304 | 0 | if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) |
305 | 0 | return LW_FALSE; |
306 | | |
307 | 0 | minq=FP_MIN(q1->y,q2->y); |
308 | 0 | maxq=FP_MAX(q1->y,q2->y); |
309 | 0 | minp=FP_MIN(p1->y,p2->y); |
310 | 0 | maxp=FP_MAX(p1->y,p2->y); |
311 | |
|
312 | 0 | if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) |
313 | 0 | return LW_FALSE; |
314 | | |
315 | 0 | return LW_TRUE; |
316 | 0 | } |
317 | | |
318 | | /** |
319 | | ** @brief returns the kind of #CG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2) |
320 | | ** @param p1 start point of first straight linesegment |
321 | | ** @param p2 end point of first straight linesegment |
322 | | ** @param q1 start point of second line segment |
323 | | ** @param q2 end point of second line segment |
324 | | ** @return a #CG_SEGMENT_INTERSECTION_TYPE |
325 | | ** Returns one of |
326 | | ** SEG_ERROR = -1, |
327 | | ** SEG_NO_INTERSECTION = 0, |
328 | | ** SEG_COLINEAR = 1, |
329 | | ** SEG_CROSS_LEFT = 2, |
330 | | ** SEG_CROSS_RIGHT = 3, |
331 | | */ |
332 | | int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2) |
333 | 0 | { |
334 | |
|
335 | 0 | int pq1, pq2, qp1, qp2; |
336 | | |
337 | | /* No envelope interaction => we are done. */ |
338 | 0 | if (!lw_seg_interact(p1, p2, q1, p2)) |
339 | 0 | { |
340 | 0 | return SEG_NO_INTERSECTION; |
341 | 0 | } |
342 | | |
343 | | /* Are the start and end points of q on the same side of p? */ |
344 | 0 | pq1=lw_segment_side(p1,p2,q1); |
345 | 0 | pq2=lw_segment_side(p1,p2,q2); |
346 | 0 | if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0)) |
347 | 0 | { |
348 | 0 | return SEG_NO_INTERSECTION; |
349 | 0 | } |
350 | | |
351 | | /* Are the start and end points of p on the same side of q? */ |
352 | 0 | qp1=lw_segment_side(q1,q2,p1); |
353 | 0 | qp2=lw_segment_side(q1,q2,p2); |
354 | 0 | if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) ) |
355 | 0 | { |
356 | 0 | return SEG_NO_INTERSECTION; |
357 | 0 | } |
358 | | |
359 | | /* Nobody is on one side or another? Must be colinear. */ |
360 | 0 | if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 ) |
361 | 0 | { |
362 | 0 | return SEG_COLINEAR; |
363 | 0 | } |
364 | | |
365 | | /* |
366 | | ** When one end-point touches, the sidedness is determined by the |
367 | | ** location of the other end-point. Only touches by the first point |
368 | | ** will be considered "real" to avoid double counting. |
369 | | */ |
370 | 0 | LWDEBUGF(4, "pq1=%d pq2=%d", pq1, pq2); |
371 | 0 | LWDEBUGF(4, "qp1=%d qp2=%d", qp1, qp2); |
372 | | |
373 | | /* Second point of p or q touches, it's not a crossing. */ |
374 | 0 | if ( pq2 == 0 || qp2 == 0 ) |
375 | 0 | { |
376 | 0 | return SEG_NO_INTERSECTION; |
377 | 0 | } |
378 | | |
379 | | /* First point of p touches, it's a "crossing". */ |
380 | 0 | if ( pq1 == 0 ) |
381 | 0 | { |
382 | 0 | if ( pq2 > 0 ) |
383 | 0 | return SEG_CROSS_RIGHT; |
384 | 0 | else |
385 | 0 | return SEG_CROSS_LEFT; |
386 | 0 | } |
387 | | |
388 | | /* First point of q touches, it's a crossing. */ |
389 | 0 | if ( qp1 == 0 ) |
390 | 0 | { |
391 | 0 | if ( pq1 < pq2 ) |
392 | 0 | return SEG_CROSS_RIGHT; |
393 | 0 | else |
394 | 0 | return SEG_CROSS_LEFT; |
395 | 0 | } |
396 | | |
397 | | /* The segments cross, what direction is the crossing? */ |
398 | 0 | if ( pq1 < pq2 ) |
399 | 0 | return SEG_CROSS_RIGHT; |
400 | 0 | else |
401 | 0 | return SEG_CROSS_LEFT; |
402 | | |
403 | | /* This should never happen! */ |
404 | 0 | return SEG_ERROR; |
405 | 0 | } |
406 | | |
407 | | /** |
408 | | ** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior of 2 linestrings |
409 | | ** @param l1 first line string |
410 | | ** @param l2 second line string |
411 | | ** @return a #CG_LINE_CROSS_TYPE |
412 | | ** LINE_NO_CROSS = 0 |
413 | | ** LINE_CROSS_LEFT = -1 |
414 | | ** LINE_CROSS_RIGHT = 1 |
415 | | ** LINE_MULTICROSS_END_LEFT = -2 |
416 | | ** LINE_MULTICROSS_END_RIGHT = 2 |
417 | | ** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3 |
418 | | ** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 |
419 | | ** |
420 | | */ |
421 | | int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2) |
422 | 0 | { |
423 | 0 | uint32_t i = 0, j = 0; |
424 | 0 | const POINT2D *p1, *p2, *q1, *q2; |
425 | 0 | POINTARRAY *pa1 = NULL, *pa2 = NULL; |
426 | 0 | int cross_left = 0; |
427 | 0 | int cross_right = 0; |
428 | 0 | int first_cross = 0; |
429 | 0 | int this_cross = 0; |
430 | | #if POSTGIS_DEBUG_LEVEL >= 4 |
431 | | char *geom_ewkt; |
432 | | #endif |
433 | |
|
434 | 0 | pa1 = (POINTARRAY*)l1->points; |
435 | 0 | pa2 = (POINTARRAY*)l2->points; |
436 | | |
437 | | /* One-point lines can't intersect (and shouldn't exist). */ |
438 | 0 | if ( pa1->npoints < 2 || pa2->npoints < 2 ) |
439 | 0 | return LINE_NO_CROSS; |
440 | | |
441 | | /* Zero length lines don't have a side. */ |
442 | 0 | if ( ptarray_length_2d(pa1) == 0 || ptarray_length_2d(pa2) == 0 ) |
443 | 0 | return LINE_NO_CROSS; |
444 | | |
445 | | |
446 | | #if POSTGIS_DEBUG_LEVEL >= 4 |
447 | | geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l1); |
448 | | LWDEBUGF(4, "l1 = %s", geom_ewkt); |
449 | | lwfree(geom_ewkt); |
450 | | geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l2); |
451 | | LWDEBUGF(4, "l2 = %s", geom_ewkt); |
452 | | lwfree(geom_ewkt); |
453 | | #endif |
454 | | |
455 | | /* Initialize first point of q */ |
456 | 0 | q1 = getPoint2d_cp(pa2, 0); |
457 | |
|
458 | 0 | for ( i = 1; i < pa2->npoints; i++ ) |
459 | 0 | { |
460 | | |
461 | | /* Update second point of q to next value */ |
462 | 0 | q2 = getPoint2d_cp(pa2, i); |
463 | | |
464 | | /* Initialize first point of p */ |
465 | 0 | p1 = getPoint2d_cp(pa1, 0); |
466 | |
|
467 | 0 | for ( j = 1; j < pa1->npoints; j++ ) |
468 | 0 | { |
469 | | |
470 | | /* Update second point of p to next value */ |
471 | 0 | p2 = getPoint2d_cp(pa1, j); |
472 | |
|
473 | 0 | this_cross = lw_segment_intersects(p1, p2, q1, q2); |
474 | |
|
475 | 0 | LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", i, j, p1->x, p1->y, p2->x, p2->y); |
476 | |
|
477 | 0 | if ( this_cross == SEG_CROSS_LEFT ) |
478 | 0 | { |
479 | 0 | LWDEBUG(4,"this_cross == SEG_CROSS_LEFT"); |
480 | 0 | cross_left++; |
481 | 0 | if ( ! first_cross ) |
482 | 0 | first_cross = SEG_CROSS_LEFT; |
483 | 0 | } |
484 | |
|
485 | 0 | if ( this_cross == SEG_CROSS_RIGHT ) |
486 | 0 | { |
487 | 0 | LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT"); |
488 | 0 | cross_right++; |
489 | 0 | if ( ! first_cross ) |
490 | 0 | first_cross = SEG_CROSS_RIGHT; |
491 | 0 | } |
492 | | |
493 | | /* |
494 | | ** Crossing at a co-linearity can be turned handled by extending |
495 | | ** segment to next vertex and seeing if the end points straddle |
496 | | ** the co-linear segment. |
497 | | */ |
498 | 0 | if ( this_cross == SEG_COLINEAR ) |
499 | 0 | { |
500 | 0 | LWDEBUG(4,"this_cross == SEG_COLINEAR"); |
501 | | /* TODO: Add logic here and in segment_intersects() |
502 | | continue; |
503 | | */ |
504 | 0 | } |
505 | |
|
506 | 0 | LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION"); |
507 | | |
508 | | /* Turn second point of p into first point */ |
509 | 0 | p1 = p2; |
510 | |
|
511 | 0 | } |
512 | | |
513 | | /* Turn second point of q into first point */ |
514 | 0 | q1 = q2; |
515 | |
|
516 | 0 | } |
517 | |
|
518 | 0 | LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right); |
519 | |
|
520 | 0 | if ( !cross_left && !cross_right ) |
521 | 0 | return LINE_NO_CROSS; |
522 | | |
523 | 0 | if ( !cross_left && cross_right == 1 ) |
524 | 0 | return LINE_CROSS_RIGHT; |
525 | | |
526 | 0 | if ( !cross_right && cross_left == 1 ) |
527 | 0 | return LINE_CROSS_LEFT; |
528 | | |
529 | 0 | if ( cross_left - cross_right == 1 ) |
530 | 0 | return LINE_MULTICROSS_END_LEFT; |
531 | | |
532 | 0 | if ( cross_left - cross_right == -1 ) |
533 | 0 | return LINE_MULTICROSS_END_RIGHT; |
534 | | |
535 | 0 | if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT ) |
536 | 0 | return LINE_MULTICROSS_END_SAME_FIRST_LEFT; |
537 | | |
538 | 0 | if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT ) |
539 | 0 | return LINE_MULTICROSS_END_SAME_FIRST_RIGHT; |
540 | | |
541 | 0 | return LINE_NO_CROSS; |
542 | |
|
543 | 0 | } |
544 | | |
545 | | |
546 | | |
547 | | |
548 | | |
549 | | static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; |
550 | | |
551 | 0 | #define GEOHASH_MAX_DOUBLE_PRECISION_CHARS 20 |
552 | | |
553 | | /* |
554 | | ** Calculate the geohash, iterating downwards and gaining precision. |
555 | | ** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com> |
556 | | ** Released under the MIT License. |
557 | | */ |
558 | | lwvarlena_t * |
559 | | geohash_point(double longitude, double latitude, int precision) |
560 | 0 | { |
561 | 0 | int is_even=1, i=0; |
562 | 0 | double lat[2], lon[2], mid; |
563 | 0 | char bits[] = {16,8,4,2,1}; |
564 | 0 | int bit=0, ch=0; |
565 | 0 | lwvarlena_t *v = lwalloc(precision + LWVARHDRSZ); |
566 | 0 | LWSIZE_SET(v->size, precision + LWVARHDRSZ); |
567 | 0 | char *geohash = v->data; |
568 | |
|
569 | 0 | lat[0] = -90.0; |
570 | 0 | lat[1] = 90.0; |
571 | 0 | lon[0] = -180.0; |
572 | 0 | lon[1] = 180.0; |
573 | |
|
574 | 0 | while (i < precision) |
575 | 0 | { |
576 | 0 | if (is_even) |
577 | 0 | { |
578 | 0 | mid = (lon[0] + lon[1]) / 2; |
579 | 0 | if (longitude >= mid) |
580 | 0 | { |
581 | 0 | ch |= bits[bit]; |
582 | 0 | lon[0] = mid; |
583 | 0 | } |
584 | 0 | else |
585 | 0 | { |
586 | 0 | lon[1] = mid; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | else |
590 | 0 | { |
591 | 0 | mid = (lat[0] + lat[1]) / 2; |
592 | 0 | if (latitude >= mid) |
593 | 0 | { |
594 | 0 | ch |= bits[bit]; |
595 | 0 | lat[0] = mid; |
596 | 0 | } |
597 | 0 | else |
598 | 0 | { |
599 | 0 | lat[1] = mid; |
600 | 0 | } |
601 | 0 | } |
602 | |
|
603 | 0 | is_even = !is_even; |
604 | 0 | if (bit < 4) |
605 | 0 | { |
606 | 0 | bit++; |
607 | 0 | } |
608 | 0 | else |
609 | 0 | { |
610 | 0 | geohash[i++] = base32[ch]; |
611 | 0 | bit = 0; |
612 | 0 | ch = 0; |
613 | 0 | } |
614 | 0 | } |
615 | |
|
616 | 0 | return v; |
617 | 0 | } |
618 | | |
619 | | |
620 | | /* |
621 | | ** Calculate the geohash, iterating downwards and gaining precision. |
622 | | ** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com> |
623 | | ** Released under the MIT License. |
624 | | */ |
625 | | unsigned int geohash_point_as_int(POINT2D *pt) |
626 | 0 | { |
627 | 0 | int is_even=1; |
628 | 0 | double lat[2], lon[2], mid; |
629 | 0 | int bit=32; |
630 | 0 | unsigned int ch = 0; |
631 | |
|
632 | 0 | double longitude = pt->x; |
633 | 0 | double latitude = pt->y; |
634 | |
|
635 | 0 | lat[0] = -90.0; |
636 | 0 | lat[1] = 90.0; |
637 | 0 | lon[0] = -180.0; |
638 | 0 | lon[1] = 180.0; |
639 | |
|
640 | 0 | while (--bit >= 0) |
641 | 0 | { |
642 | 0 | if (is_even) |
643 | 0 | { |
644 | 0 | mid = (lon[0] + lon[1]) / 2; |
645 | 0 | if (longitude > mid) |
646 | 0 | { |
647 | 0 | ch |= 0x0001u << bit; |
648 | 0 | lon[0] = mid; |
649 | 0 | } |
650 | 0 | else |
651 | 0 | { |
652 | 0 | lon[1] = mid; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | else |
656 | 0 | { |
657 | 0 | mid = (lat[0] + lat[1]) / 2; |
658 | 0 | if (latitude > mid) |
659 | 0 | { |
660 | 0 | ch |= 0x0001 << bit; |
661 | 0 | lat[0] = mid; |
662 | 0 | } |
663 | 0 | else |
664 | 0 | { |
665 | 0 | lat[1] = mid; |
666 | 0 | } |
667 | 0 | } |
668 | |
|
669 | 0 | is_even = !is_even; |
670 | 0 | } |
671 | 0 | return ch; |
672 | 0 | } |
673 | | |
674 | | /* |
675 | | ** Decode a GeoHash into a bounding box. The lat and lon arguments should |
676 | | ** both be passed as double arrays of length 2 at a minimum where the values |
677 | | ** set in them will be the southwest and northeast coordinates of the bounding |
678 | | ** box accordingly. A precision less than 0 indicates that the entire length |
679 | | ** of the GeoHash should be used. |
680 | | ** It will call `lwerror` if an invalid character is found |
681 | | */ |
682 | | void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision) |
683 | 67 | { |
684 | 67 | bool is_even = 1; |
685 | | |
686 | 67 | lat[0] = -90.0; |
687 | 67 | lat[1] = 90.0; |
688 | 67 | lon[0] = -180.0; |
689 | 67 | lon[1] = 180.0; |
690 | | |
691 | 67 | size_t hashlen = strlen(geohash); |
692 | 67 | if (precision < 0 || (size_t)precision > hashlen) |
693 | 40 | { |
694 | 40 | precision = (int)hashlen; |
695 | 40 | } |
696 | | |
697 | 2.53k | for (int i = 0; i < precision; i++) |
698 | 2.49k | { |
699 | 2.49k | char c = tolower(geohash[i]); |
700 | | |
701 | | /* Valid characters are all digits in base32 */ |
702 | 2.49k | char *base32_pos = strchr(base32, c); |
703 | 2.49k | if (!base32_pos) |
704 | 31 | { |
705 | 31 | lwerror("%s: Invalid character '%c'", __func__, geohash[i]); |
706 | 31 | return; |
707 | 31 | } |
708 | 2.46k | char cd = base32_pos - base32; |
709 | | |
710 | 14.7k | for (size_t j = 0; j < 5; j++) |
711 | 12.3k | { |
712 | 12.3k | const char bits[] = {16, 8, 4, 2, 1}; |
713 | 12.3k | char mask = bits[j]; |
714 | 12.3k | if (is_even) |
715 | 6.17k | { |
716 | 6.17k | lon[!(cd & mask)] = (lon[0] + lon[1]) / 2; |
717 | 6.17k | } |
718 | 6.14k | else |
719 | 6.14k | { |
720 | 6.14k | lat[!(cd & mask)] = (lat[0] + lat[1]) / 2; |
721 | 6.14k | } |
722 | 12.3k | is_even = !is_even; |
723 | 12.3k | } |
724 | 2.46k | } |
725 | 67 | } |
726 | | |
727 | | int lwgeom_geohash_precision(GBOX bbox, GBOX *bounds) |
728 | 0 | { |
729 | 0 | double minx, miny, maxx, maxy; |
730 | 0 | double latmax, latmin, lonmax, lonmin; |
731 | 0 | double lonwidth, latwidth; |
732 | 0 | double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust; |
733 | 0 | int precision = 0; |
734 | | |
735 | | /* Get the bounding box, return error if things don't work out. */ |
736 | 0 | minx = bbox.xmin; |
737 | 0 | miny = bbox.ymin; |
738 | 0 | maxx = bbox.xmax; |
739 | 0 | maxy = bbox.ymax; |
740 | |
|
741 | 0 | if ( minx == maxx && miny == maxy ) |
742 | 0 | { |
743 | | /* It's a point. Doubles have 51 bits of precision. |
744 | | ** 2 * 51 / 5 == 20 */ |
745 | 0 | return GEOHASH_MAX_DOUBLE_PRECISION_CHARS; |
746 | 0 | } |
747 | | |
748 | 0 | lonmin = -180.0; |
749 | 0 | latmin = -90.0; |
750 | 0 | lonmax = 180.0; |
751 | 0 | latmax = 90.0; |
752 | | |
753 | | /* Shrink a world bounding box until one of the edges interferes with the |
754 | | ** bounds of our rectangle. */ |
755 | 0 | while ( 1 ) |
756 | 0 | { |
757 | 0 | double old_lonmin = lonmin; |
758 | 0 | double old_lonmax = lonmax; |
759 | 0 | double old_latmin = latmin; |
760 | 0 | double old_latmax = latmax; |
761 | |
|
762 | 0 | lonwidth = lonmax - lonmin; |
763 | 0 | latwidth = latmax - latmin; |
764 | 0 | latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0; |
765 | |
|
766 | 0 | if ( minx >= lonmin + lonwidth / 2.0 ) |
767 | 0 | { |
768 | 0 | lonminadjust = lonwidth / 2.0; |
769 | 0 | } |
770 | 0 | else if ( maxx <= lonmax - lonwidth / 2.0 ) |
771 | 0 | { |
772 | 0 | lonmaxadjust = -1 * lonwidth / 2.0; |
773 | 0 | } |
774 | 0 | if ( lonminadjust || lonmaxadjust ) |
775 | 0 | { |
776 | 0 | lonmin += lonminadjust; |
777 | 0 | lonmax += lonmaxadjust; |
778 | | /* Stop before counting bits that no longer move the double bounds. */ |
779 | 0 | if (lonmin == old_lonmin && lonmax == old_lonmax) |
780 | 0 | break; |
781 | | /* Each adjustment cycle corresponds to 2 bits of storage in the |
782 | | ** geohash. */ |
783 | 0 | precision++; |
784 | 0 | } |
785 | 0 | else |
786 | 0 | { |
787 | 0 | break; |
788 | 0 | } |
789 | | |
790 | 0 | if ( miny >= latmin + latwidth / 2.0 ) |
791 | 0 | { |
792 | 0 | latminadjust = latwidth / 2.0; |
793 | 0 | } |
794 | 0 | else if (maxy <= latmax - latwidth / 2.0 ) |
795 | 0 | { |
796 | 0 | latmaxadjust = -1 * latwidth / 2.0; |
797 | 0 | } |
798 | | /* Only adjust if adjustments are legal (we haven't crossed any edges). */ |
799 | 0 | if ( latminadjust || latmaxadjust ) |
800 | 0 | { |
801 | 0 | latmin += latminadjust; |
802 | 0 | latmax += latmaxadjust; |
803 | | /* Stop before counting bits that no longer move the double bounds. */ |
804 | 0 | if (latmin == old_latmin && latmax == old_latmax) |
805 | 0 | break; |
806 | | /* Each adjustment cycle corresponds to 2 bits of storage in the |
807 | | ** geohash. */ |
808 | 0 | precision++; |
809 | 0 | } |
810 | 0 | else |
811 | 0 | { |
812 | 0 | break; |
813 | 0 | } |
814 | 0 | } |
815 | | |
816 | | /* Save the edges of our bounds, in case someone cares later. */ |
817 | 0 | bounds->xmin = lonmin; |
818 | 0 | bounds->xmax = lonmax; |
819 | 0 | bounds->ymin = latmin; |
820 | 0 | bounds->ymax = latmax; |
821 | | |
822 | | /* Each geohash character (base32) can contain 5 bits of information. |
823 | | ** We are returning the precision in characters, so here we divide. */ |
824 | 0 | precision /= 5; |
825 | 0 | return precision > GEOHASH_MAX_DOUBLE_PRECISION_CHARS ? GEOHASH_MAX_DOUBLE_PRECISION_CHARS : precision; |
826 | 0 | } |
827 | | |
828 | | |
829 | | /* |
830 | | ** Return a geohash string for the geometry. <http://geohash.org> |
831 | | ** Where the precision is non-positive, calculate a precision based on the |
832 | | ** bounds of the feature. Big features have loose precision. |
833 | | ** Small features have tight precision. |
834 | | */ |
835 | | lwvarlena_t * |
836 | | lwgeom_geohash(const LWGEOM *lwgeom, int precision) |
837 | 0 | { |
838 | 0 | GBOX gbox = {0}; |
839 | 0 | GBOX gbox_bounds = {0}; |
840 | 0 | double lat, lon; |
841 | 0 | int result; |
842 | |
|
843 | 0 | gbox_init(&gbox); |
844 | 0 | gbox_init(&gbox_bounds); |
845 | |
|
846 | 0 | result = lwgeom_calculate_gbox_cartesian(lwgeom, &gbox); |
847 | 0 | if ( result == LW_FAILURE ) return NULL; |
848 | | |
849 | | /* Return error if we are being fed something outside our working bounds */ |
850 | 0 | if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 ) |
851 | 0 | { |
852 | 0 | lwerror("Geohash requires inputs in decimal degrees, got (%g %g, %g %g).", |
853 | 0 | gbox.xmin, gbox.ymin, |
854 | 0 | gbox.xmax, gbox.ymax); |
855 | 0 | return NULL; |
856 | 0 | } |
857 | | |
858 | | /* What is the center of our geometry bounds? We'll use that to |
859 | | ** approximate location. */ |
860 | 0 | lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2; |
861 | 0 | lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2; |
862 | |
|
863 | 0 | if ( precision <= 0 ) |
864 | 0 | { |
865 | 0 | precision = lwgeom_geohash_precision(gbox, &gbox_bounds); |
866 | 0 | } |
867 | | |
868 | | /* |
869 | | ** Return the geohash of the center, with a precision determined by the |
870 | | ** extent of the bounds. |
871 | | ** Possible change: return the point at the center of the precision bounds? |
872 | | */ |
873 | 0 | return geohash_point(lon, lat, precision); |
874 | 0 | } |