~ubuntu-branches/ubuntu/trusty/postgis/trusty-security

« back to all changes in this revision

Viewing changes to liblwgeom/lwalgorithm.c

  • Committer: Bazaar Package Importer
  • Author(s): Francesco Paolo Lovergine
  • Date: 2009-12-11 13:10:34 UTC
  • mfrom: (1.1.9 upstream) (5.2.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20091211131034-wmsz69wxvt95pe5r
Tags: 1.4.0-2
* Upload to unstable.
* Better parameterized debian/rules against postgis $(VERSION).
* Added dblatex and libcunit1-dev among build-deps.
* Added postgis_comments.sql to contrib/ SQL templates.
* Dropping 8.3 support, no more supported for squeeze.
  (closes: #559587)
* Do not stop on error in postrm if the target dir does not exist.
  (closes: #560409)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**********************************************************************
 
2
 * $Id: lwalgorithm.c 4168 2009-06-11 16:44:03Z pramsey $
 
3
 *
 
4
 * PostGIS - Spatial Types for PostgreSQL
 
5
 * http://postgis.refractions.net
 
6
 * Copyright 2008 Paul Ramsey
 
7
 *
 
8
 * This is free software; you can redistribute and/or modify it under
 
9
 * the terms of the GNU General Public Licence. See the COPYING file.
 
10
 *
 
11
 **********************************************************************/
 
12
 
 
13
#include "lwalgorithm.h"
 
14
 
 
15
 
 
16
/*
 
17
** lw_segment_side()
 
18
**
 
19
** Return < 0.0 if point Q is left of segment P
 
20
** Return > 0.0 if point Q is right of segment P
 
21
** Return = 0.0 if point Q in on segment P
 
22
*/
 
23
double lw_segment_side(POINT2D *p1, POINT2D *p2, POINT2D *q)
 
24
{
 
25
        return ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
 
26
}
 
27
 
 
28
int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
 
29
{
 
30
        double minq=LW_MIN(q1.x,q2.x);
 
31
        double maxq=LW_MAX(q1.x,q2.x);
 
32
        double minp=LW_MIN(p1.x,p2.x);
 
33
        double maxp=LW_MAX(p1.x,p2.x);
 
34
 
 
35
        if (minp>maxq || maxp<minq)
 
36
                return LW_FALSE;
 
37
 
 
38
        minq=LW_MIN(q1.y,q2.y);
 
39
        maxq=LW_MAX(q1.y,q2.y);
 
40
        minp=LW_MIN(p1.y,p2.y);
 
41
        maxp=LW_MAX(p1.y,p2.y);
 
42
 
 
43
        if (minp>maxq || maxp<minq)
 
44
                return LW_FALSE;
 
45
 
 
46
        return LW_TRUE;
 
47
}
 
48
 
 
49
/**
 
50
** @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)
 
51
**      @param p1 start point of first straight linesegment
 
52
**      @param p2 end point of first straight linesegment
 
53
**      @param q1 start point of second line segment
 
54
**      @param q2 end point of second line segment
 
55
**      @return a #CG_SEGMENT_INTERSECTION_TYPE
 
56
**      Returns one of
 
57
**              SEG_ERROR = -1,
 
58
**              SEG_NO_INTERSECTION = 0,
 
59
**              SEG_COLINEAR = 1,
 
60
**              SEG_CROSS_LEFT = 2,
 
61
**              SEG_CROSS_RIGHT = 3,
 
62
**              SEG_TOUCH_LEFT = 4,
 
63
**              SEG_TOUCH_RIGHT = 5
 
64
*/
 
65
int lw_segment_intersects(POINT2D *p1, POINT2D *p2, POINT2D *q1, POINT2D *q2)
 
66
{
 
67
 
 
68
        double pq1, pq2, qp1, qp2;
 
69
 
 
70
        /* No envelope interaction => we are done. */
 
71
        if (!lw_segment_envelope_intersects(*p1, *p2, *q1, *p2))
 
72
        {
 
73
                return SEG_NO_INTERSECTION;
 
74
        }
 
75
 
 
76
        /* Are the start and end points of q on the same side of p? */
 
77
        pq1=lw_segment_side(p1,p2,q1);
 
78
        pq2=lw_segment_side(p1,p2,q2);
 
79
        if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
 
80
        {
 
81
                return SEG_NO_INTERSECTION;
 
82
        }
 
83
 
 
84
        /* Are the start and end points of p on the same side of q? */
 
85
        qp1=lw_segment_side(q1,q2,p1);
 
86
        qp2=lw_segment_side(q1,q2,p2);
 
87
        if ((qp1>0 && qp2>0) || (qp1<0 && qp2<0))
 
88
        {
 
89
                return SEG_NO_INTERSECTION;
 
90
        }
 
91
 
 
92
        /* Nobody is on one side or another? Must be colinear. */
 
93
        if (pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0)
 
94
        {
 
95
                return SEG_COLINEAR;
 
96
        }
 
97
 
 
98
        /*
 
99
        ** When one end-point touches, the sidedness is determined by the
 
100
        ** location of the other end-point.
 
101
        */
 
102
        if ( pq2 == 0.0 )
 
103
        {
 
104
                if ( pq1 < 0.0 )
 
105
                        return SEG_TOUCH_LEFT;
 
106
                else
 
107
                        return SEG_TOUCH_RIGHT;
 
108
        }
 
109
        if ( pq1 == 0.0 )
 
110
        {
 
111
                if ( pq2 < 0.0 )
 
112
                        return SEG_TOUCH_LEFT;
 
113
                else
 
114
                        return SEG_TOUCH_RIGHT;
 
115
        }
 
116
 
 
117
        /* The segments cross, what direction is the crossing? */
 
118
        if ( pq1 < pq2 )
 
119
                return SEG_CROSS_RIGHT;
 
120
        else
 
121
                return SEG_CROSS_LEFT;
 
122
 
 
123
        /* This should never happen! */
 
124
        return SEG_ERROR;
 
125
 
 
126
}
 
127
 
 
128
/**
 
129
** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior  of 2 linestrings
 
130
** @param l1 first line string
 
131
** @param l2 second line string
 
132
** @return a #CG_LINE_CROSS_TYPE
 
133
**   LINE_NO_CROSS = 0
 
134
**   LINE_CROSS_LEFT = -1
 
135
**   LINE_CROSS_RIGHT = 1
 
136
**   LINE_MULTICROSS_END_LEFT = -2
 
137
**   LINE_MULTICROSS_END_RIGHT = 2
 
138
**   LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3
 
139
**   LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
 
140
**
 
141
*/
 
142
int lwline_crossing_direction(LWLINE *l1, LWLINE *l2)
 
143
{
 
144
 
 
145
        int i = 0, j = 0, rv = 0;
 
146
        POINT2D *p1;
 
147
        POINT2D *p2;
 
148
        POINT2D *q1;
 
149
        POINT2D *q2;
 
150
        POINTARRAY *pa1;
 
151
        POINTARRAY *pa2;
 
152
        int cross_left = 0;
 
153
        int cross_right = 0;
 
154
        int first_cross = 0;
 
155
        int final_cross = 0;
 
156
        int this_cross = 0;
 
157
        int vertex_touch = -1;
 
158
        int vertex_touch_type = 0;
 
159
 
 
160
        pa1 = (POINTARRAY*)l1->points;
 
161
        pa2 = (POINTARRAY*)l2->points;
 
162
 
 
163
        p1 = lwalloc(sizeof(POINT2D));
 
164
        p2 = lwalloc(sizeof(POINT2D));
 
165
        q1 = lwalloc(sizeof(POINT2D));
 
166
        q2 = lwalloc(sizeof(POINT2D));
 
167
 
 
168
        /* One-point lines can't intersect (and shouldn't exist). */
 
169
        if ( pa1->npoints < 2 || pa2->npoints < 2 )
 
170
                return LINE_NO_CROSS;
 
171
 
 
172
        LWDEBUGF(4, "lineCrossingDirection: l1 = %s", lwgeom_to_ewkt((LWGEOM*)l1,0));
 
173
        LWDEBUGF(4, "lineCrossingDirection: l2 = %s", lwgeom_to_ewkt((LWGEOM*)l2,0));
 
174
 
 
175
        for ( i = 1; i < pa2->npoints; i++ )
 
176
        {
 
177
 
 
178
                rv = getPoint2d_p(pa2, i-1, q1);
 
179
                rv = getPoint2d_p(pa2, i, q2);
 
180
 
 
181
                for ( j = 1; j < pa1->npoints; j++ )
 
182
                {
 
183
 
 
184
                        rv = getPoint2d_p(pa1, j-1, p1);
 
185
                        rv = getPoint2d_p(pa1, j, p2);
 
186
 
 
187
                        LWDEBUGF(4, "lineCrossingDirection: i=%d, j=%d", i, j);
 
188
 
 
189
                        this_cross = lw_segment_intersects(p1, p2, q1, q2);
 
190
 
 
191
                        if ( ! first_cross && this_cross )
 
192
                                first_cross = this_cross;
 
193
                        if ( this_cross )
 
194
                                final_cross = this_cross;
 
195
 
 
196
                        if ( this_cross == SEG_CROSS_LEFT )
 
197
                        {
 
198
                                cross_left++;
 
199
                                break;
 
200
                        }
 
201
 
 
202
                        if ( this_cross == SEG_CROSS_RIGHT )
 
203
                        {
 
204
                                cross_right++;
 
205
                                break;
 
206
                        }
 
207
 
 
208
                        /*
 
209
                        ** Crossing at a co-linearity can be turned into crossing at
 
210
                        ** a vertex by pulling the original touch point forward along
 
211
                        ** the co-linearity.
 
212
                        */
 
213
                        if ( this_cross == SEG_COLINEAR && vertex_touch == (i-1) )
 
214
                        {
 
215
                                vertex_touch = i;
 
216
                                break;
 
217
                        }
 
218
 
 
219
                        /*
 
220
                        ** Crossing-at-vertex will cause four segment touch interactions, two at
 
221
                        ** j-1 and two at j. We avoid incrementing our cross count by ignoring the
 
222
                        ** second pair.
 
223
                        */
 
224
                        if ( this_cross == SEG_TOUCH_LEFT )
 
225
                        {
 
226
                                if ( vertex_touch == (i-1) && vertex_touch_type == SEG_TOUCH_RIGHT )
 
227
                                {
 
228
                                        cross_left++;
 
229
                                        vertex_touch = -1;
 
230
                                        vertex_touch_type = 0;
 
231
                                }
 
232
                                else
 
233
                                {
 
234
                                        vertex_touch = i;
 
235
                                        vertex_touch_type = this_cross;
 
236
                                }
 
237
                                break;
 
238
                        }
 
239
                        if ( this_cross == SEG_TOUCH_RIGHT )
 
240
                        {
 
241
                                if ( vertex_touch == (i-1) && vertex_touch_type == SEG_TOUCH_LEFT )
 
242
                                {
 
243
                                        cross_right++;
 
244
                                        vertex_touch = -1;
 
245
                                        vertex_touch_type = 0;
 
246
                                }
 
247
                                else
 
248
                                {
 
249
                                        vertex_touch = i;
 
250
                                        vertex_touch_type = this_cross;
 
251
                                }
 
252
                                break;
 
253
                        }
 
254
 
 
255
                        /*
 
256
                        ** TODO Handle co-linear cases.
 
257
                        */
 
258
 
 
259
                        LWDEBUGF(4, "lineCrossingDirection: this_cross=%d, vertex_touch=%d, vertex_touch_type=%d", this_cross, vertex_touch, vertex_touch_type);
 
260
 
 
261
                }
 
262
 
 
263
        }
 
264
 
 
265
        LWDEBUGF(4, "first_cross=%d, final_cross=%d, cross_left=%d, cross_right=%d", first_cross, final_cross, cross_left, cross_right);
 
266
 
 
267
        lwfree(p1);
 
268
        lwfree(p2);
 
269
        lwfree(q1);
 
270
        lwfree(q2);
 
271
 
 
272
        if ( !cross_left && !cross_right )
 
273
                return LINE_NO_CROSS;
 
274
 
 
275
        if ( !cross_left && cross_right == 1 )
 
276
                return LINE_CROSS_RIGHT;
 
277
 
 
278
        if ( !cross_right && cross_left == 1 )
 
279
                return LINE_CROSS_LEFT;
 
280
 
 
281
        if ( cross_left - cross_right == 1 )
 
282
                return LINE_MULTICROSS_END_LEFT;
 
283
 
 
284
        if ( cross_left - cross_right == -1 )
 
285
                return LINE_MULTICROSS_END_RIGHT;
 
286
 
 
287
        if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
 
288
                return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
 
289
 
 
290
        if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
 
291
                return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
 
292
 
 
293
        if ( cross_left - cross_right == 0 && first_cross == SEG_TOUCH_LEFT )
 
294
                return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
 
295
 
 
296
        if ( cross_left - cross_right == 0 && first_cross == SEG_TOUCH_RIGHT )
 
297
                return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
 
298
 
 
299
        return LINE_NO_CROSS;
 
300
 
 
301
}
 
302
 
 
303
/*
 
304
** lwpoint_get_ordinate(point, ordinate) => double
 
305
*/
 
306
double lwpoint_get_ordinate(const POINT4D *p, int ordinate)
 
307
{
 
308
        if ( ! p )
 
309
        {
 
310
                lwerror("Null input geometry.");
 
311
                return 0.0;
 
312
        }
 
313
 
 
314
        if ( ordinate > 3 || ordinate < 0 )
 
315
        {
 
316
                lwerror("Cannot extract ordinate %d.", ordinate);
 
317
                return 0.0;
 
318
        }
 
319
 
 
320
        if ( ordinate == 3 )
 
321
                return p->m;
 
322
        if ( ordinate == 2 )
 
323
                return p->z;
 
324
        if ( ordinate == 1 )
 
325
                return p->y;
 
326
 
 
327
        return p->x;
 
328
 
 
329
}
 
330
void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value)
 
331
{
 
332
        if ( ! p )
 
333
        {
 
334
                lwerror("Null input geometry.");
 
335
                return;
 
336
        }
 
337
 
 
338
        if ( ordinate > 3 || ordinate < 0 )
 
339
        {
 
340
                lwerror("Cannot extract ordinate %d.", ordinate);
 
341
                return;
 
342
        }
 
343
 
 
344
        LWDEBUGF(4, "    setting ordinate %d to %g", ordinate, value);
 
345
 
 
346
        switch ( ordinate )
 
347
        {
 
348
        case 3:
 
349
                p->m = value;
 
350
                return;
 
351
        case 2:
 
352
                p->z = value;
 
353
                return;
 
354
        case 1:
 
355
                p->y = value;
 
356
                return;
 
357
        case 0:
 
358
                p->x = value;
 
359
                return;
 
360
        }
 
361
}
 
362
 
 
363
 
 
364
int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value)
 
365
{
 
366
        double p1_value = lwpoint_get_ordinate(p1, ordinate);
 
367
        double p2_value = lwpoint_get_ordinate(p2, ordinate);
 
368
        double proportion;
 
369
        int i = 0;
 
370
 
 
371
        if ( ordinate < 0 || ordinate >= ndims )
 
372
        {
 
373
                lwerror("Ordinate (%d) is not within ndims (%d).", ordinate, ndims);
 
374
                return 0;
 
375
        }
 
376
 
 
377
        if ( FP_MIN(p1_value, p2_value) > interpolation_value ||
 
378
                FP_MAX(p1_value, p2_value) < interpolation_value )
 
379
        {
 
380
                lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value);
 
381
                return 0;
 
382
        }
 
383
 
 
384
        proportion = fabs((interpolation_value - p1_value) / (p2_value - p1_value));
 
385
 
 
386
        for ( i = 0; i < ndims; i++ )
 
387
        {
 
388
                double newordinate = 0.0;
 
389
                p1_value = lwpoint_get_ordinate(p1, i);
 
390
                p2_value = lwpoint_get_ordinate(p2, i);
 
391
                newordinate = p1_value + proportion * (p2_value - p1_value);
 
392
                lwpoint_set_ordinate(p, i, newordinate);
 
393
                LWDEBUGF(4, "   clip ordinate(%d) p1_value(%g) p2_value(%g) proportion(%g) newordinate(%g) ", i, p1_value, p2_value, proportion, newordinate );
 
394
        }
 
395
 
 
396
        return 1;
 
397
}
 
398
 
 
399
LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to)
 
400
{
 
401
        LWCOLLECTION *lwgeom_out = NULL;
 
402
 
 
403
        if ( ! mline )
 
404
        {
 
405
                lwerror("Null input geometry.");
 
406
                return NULL;
 
407
        }
 
408
 
 
409
        if ( mline->ngeoms == 1)
 
410
        {
 
411
                lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to);
 
412
        }
 
413
        else
 
414
        {
 
415
                LWCOLLECTION *col;
 
416
                char hasz = TYPE_HASZ(mline->type);
 
417
                char hasm = TYPE_HASM(mline->type);
 
418
                char hassrid = TYPE_HASSRID(mline->type);
 
419
                int i, j;
 
420
                char homogeneous = 1;
 
421
                size_t geoms_size = 0;
 
422
                lwgeom_out = lwcollection_construct_empty(mline->SRID, hasz, hasm);
 
423
                lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, MULTILINETYPE);
 
424
                for ( i = 0; i < mline->ngeoms; i ++ )
 
425
                {
 
426
                        col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to);
 
427
                        if ( col )
 
428
                        { /* Something was left after the clip. */
 
429
                                if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size )
 
430
                                {
 
431
                                        geoms_size += 16;
 
432
                                        if ( lwgeom_out->geoms )
 
433
                                        {
 
434
                                                lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*));
 
435
                                        }
 
436
                                        else
 
437
                                        {
 
438
                                                lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*));
 
439
                                        }
 
440
                                }
 
441
                                for ( j = 0; j < col->ngeoms; j++ )
 
442
                                {
 
443
                                        lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j];
 
444
                                        lwgeom_out->ngeoms++;
 
445
                                }
 
446
                                if ( TYPE_GETTYPE(col->type) != TYPE_GETTYPE(mline->type) )
 
447
                                {
 
448
                                        homogeneous = 0;
 
449
                                }
 
450
                                /* Shallow free the struct, leaving the geoms behind. */
 
451
                                if ( col->bbox ) lwfree(col->bbox);
 
452
                                lwfree(col);
 
453
                        }
 
454
                }
 
455
                lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
 
456
                lwgeom_add_bbox((LWGEOM*)lwgeom_out);
 
457
                if ( ! homogeneous )
 
458
                {
 
459
                        lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
 
460
                }
 
461
        }
 
462
 
 
463
        if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */
 
464
        {
 
465
                return NULL;
 
466
        }
 
467
 
 
468
        return lwgeom_out;
 
469
 
 
470
}
 
471
 
 
472
 
 
473
/*
 
474
** lwline_clip_to_ordinate_range(line, ordinate, from, to) => lwmline
 
475
**
 
476
** Take in a LINESTRING and return a MULTILINESTRING of those portions of the
 
477
** LINESTRING between the from/to range for the specified ordinate (XYZM)
 
478
*/
 
479
LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to)
 
480
{
 
481
 
 
482
        POINTARRAY *pa_in = NULL;
 
483
        LWCOLLECTION *lwgeom_out = NULL;
 
484
        POINTARRAY *pa_out = NULL;
 
485
        DYNPTARRAY *dp = NULL;
 
486
        int i, rv;
 
487
        int added_last_point = 0;
 
488
        POINT4D *p = NULL, *q = NULL, *r = NULL;
 
489
        double ordinate_value_p = 0.0, ordinate_value_q = 0.0;
 
490
        char hasz = TYPE_HASZ(line->type);
 
491
        char hasm = TYPE_HASM(line->type);
 
492
        char dims = TYPE_NDIMS(line->type);
 
493
        char hassrid = TYPE_HASSRID(line->type);
 
494
 
 
495
        LWDEBUGF(4, "hassrid = %d", hassrid);
 
496
 
 
497
        /* Null input, nothing we can do. */
 
498
        if ( ! line )
 
499
        {
 
500
                lwerror("Null input geometry.");
 
501
                return NULL;
 
502
        }
 
503
 
 
504
        /* Ensure 'from' is less than 'to'. */
 
505
        if ( to < from )
 
506
        {
 
507
                double t = from;
 
508
                from = to;
 
509
                to = t;
 
510
        }
 
511
 
 
512
        LWDEBUGF(4, "from = %g, to = %g, ordinate = %d", from, to, ordinate);
 
513
        LWDEBUGF(4, "%s", lwgeom_to_ewkt((LWGEOM*)line, PARSER_CHECK_NONE));
 
514
 
 
515
        /* Asking for an ordinate we don't have. Error. */
 
516
        if ( ordinate >= dims )
 
517
        {
 
518
                lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims);
 
519
                return NULL;
 
520
        }
 
521
 
 
522
        /* Prepare our working point objects. */
 
523
        p = lwalloc(sizeof(POINT4D));
 
524
        q = lwalloc(sizeof(POINT4D));
 
525
        r = lwalloc(sizeof(POINT4D));
 
526
 
 
527
        /* Construct a collection to hold our outputs. */
 
528
        lwgeom_out = lwalloc(sizeof(LWCOLLECTION));
 
529
        lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, MULTILINETYPE);
 
530
        if (hassrid)
 
531
                lwgeom_out->SRID = line->SRID;
 
532
        else
 
533
                lwgeom_out->SRID = -1;
 
534
        lwgeom_out->bbox = NULL;
 
535
        lwgeom_out->ngeoms = 0;
 
536
        lwgeom_out->geoms = NULL;
 
537
 
 
538
        pa_in = (POINTARRAY*)line->points;
 
539
 
 
540
        for ( i = 0; i < pa_in->npoints; i++ )
 
541
        {
 
542
                LWDEBUGF(4, "Point #%d", i);
 
543
                if ( i > 0 )
 
544
                {
 
545
                        q->x = p->x;
 
546
                        q->y = p->y;
 
547
                        q->z = p->z;
 
548
                        q->m = p->m;
 
549
                        ordinate_value_q = ordinate_value_p;
 
550
                }
 
551
                rv = getPoint4d_p(pa_in, i, p);
 
552
                ordinate_value_p = lwpoint_get_ordinate(p, ordinate);
 
553
                LWDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p);
 
554
                LWDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q);
 
555
 
 
556
                /* Is this point inside the ordinate range? Yes. */
 
557
                if ( ordinate_value_p >= from && ordinate_value_p <= to )
 
558
                {
 
559
                        LWDEBUGF(4, " inside ordinate range (%g, %g)", from, to);
 
560
 
 
561
                        if ( ! added_last_point )
 
562
                        {
 
563
                                LWDEBUG(4,"  new ptarray required");
 
564
                                /* We didn't add the previous point, so this is a new segment.
 
565
                                *  Make a new point array. */
 
566
                                if ( dp ) lwfree(dp);
 
567
                                dp = dynptarray_create(64, line->type);
 
568
 
 
569
                                /* We're transiting into the range so add an interpolated
 
570
                                *  point at the range boundary.
 
571
                                *  If we're on a boundary and crossing from the far side,
 
572
                                *  we also need an interpolated point. */
 
573
                                if ( i > 0 && ( /* Don't try to interpolate if this is the first point */
 
574
                                            ( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */
 
575
                                            ( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */
 
576
                                            ( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */
 
577
                                {
 
578
                                        double interpolation_value;
 
579
                                        (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from);
 
580
                                        rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
 
581
                                        rv = dynptarray_addPoint4d(dp, r, 0);
 
582
                                        LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
 
583
                                }
 
584
                        }
 
585
                        /* Add the current vertex to the point array. */
 
586
                        rv = dynptarray_addPoint4d(dp, p, 0);
 
587
                        if ( ordinate_value_p == from || ordinate_value_p == to )
 
588
                        {
 
589
                                added_last_point = 2; /* Added on boundary. */
 
590
                        }
 
591
                        else
 
592
                        {
 
593
                                added_last_point = 1; /* Added inside range. */
 
594
                        }
 
595
                }
 
596
                /* Is this point inside the ordinate range? No. */
 
597
                else
 
598
                {
 
599
                        LWDEBUGF(4, "  added_last_point (%d)", added_last_point);
 
600
                        if ( added_last_point == 1 )
 
601
                        {
 
602
                                /* We're transiting out of the range, so add an interpolated point
 
603
                                *  to the point array at the range boundary. */
 
604
                                double interpolation_value;
 
605
                                (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
 
606
                                rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
 
607
                                rv = dynptarray_addPoint4d(dp, r, 0);
 
608
                                LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
 
609
                        }
 
610
                        else if ( added_last_point == 2 )
 
611
                        {
 
612
                                /* We're out and the last point was on the boundary.
 
613
                                *  If the last point was the near boundary, nothing to do.
 
614
                                *  If it was the far boundary, we need an interpolated point. */
 
615
                                if ( from != to && (
 
616
                                            (ordinate_value_q == from && ordinate_value_p > from) ||
 
617
                                            (ordinate_value_q == to && ordinate_value_p < to) ) )
 
618
                                {
 
619
                                        double interpolation_value;
 
620
                                        (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
 
621
                                        rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
 
622
                                        rv = dynptarray_addPoint4d(dp, r, 0);
 
623
                                        LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
 
624
                                }
 
625
                        }
 
626
                        else if ( i && ordinate_value_q < from && ordinate_value_p > to )
 
627
                        {
 
628
                                /* We just hopped over the whole range, from bottom to top,
 
629
                                *  so we need to add *two* interpolated points! */
 
630
                                pa_out = ptarray_construct(hasz, hasm, 2);
 
631
                                /* Interpolate lower point. */
 
632
                                rv = lwpoint_interpolate(p, q, r, dims, ordinate, from);
 
633
                                setPoint4d(pa_out, 0, r);
 
634
                                /* Interpolate upper point. */
 
635
                                rv = lwpoint_interpolate(p, q, r, dims, ordinate, to);
 
636
                                setPoint4d(pa_out, 1, r);
 
637
                        }
 
638
                        else if ( i && ordinate_value_q > to && ordinate_value_p < from )
 
639
                        {
 
640
                                /* We just hopped over the whole range, from top to bottom,
 
641
                                *  so we need to add *two* interpolated points! */
 
642
                                pa_out = ptarray_construct(hasz, hasm, 2);
 
643
                                /* Interpolate upper point. */
 
644
                                rv = lwpoint_interpolate(p, q, r, dims, ordinate, to);
 
645
                                setPoint4d(pa_out, 0, r);
 
646
                                /* Interpolate lower point. */
 
647
                                rv = lwpoint_interpolate(p, q, r, dims, ordinate, from);
 
648
                                setPoint4d(pa_out, 1, r);
 
649
                        }
 
650
                        /* We have an extant point-array, save it out to a multi-line. */
 
651
                        if ( dp || pa_out )
 
652
                        {
 
653
                                LWGEOM *oline;
 
654
                                LWDEBUG(4, "saving pointarray to multi-line (1)");
 
655
                                if ( dp )
 
656
                                {
 
657
                                        /* Only one point, so we have to make an lwpoint to hold this
 
658
                                        *  and set the overall output type to a generic collection. */
 
659
                                        if ( dp->pa->npoints == 1 )
 
660
                                        {
 
661
                                                oline = (LWGEOM*)lwpoint_construct(line->SRID, NULL, dp->pa);
 
662
                                                oline->type = lwgeom_makeType(hasz, hasm, hassrid, POINTTYPE);
 
663
                                                lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
 
664
                                        }
 
665
                                        else
 
666
                                        {
 
667
                                                oline = (LWGEOM*)lwline_construct(line->SRID, NULL, dp->pa);
 
668
                                                oline->type = lwgeom_makeType(hasz, hasm, hassrid, LINETYPE);
 
669
                                        }
 
670
                                }
 
671
                                else
 
672
                                {
 
673
                                        oline = (LWGEOM*)lwline_construct(line->SRID, NULL, pa_out);
 
674
                                }
 
675
                                lwgeom_out->ngeoms++;
 
676
                                if ( lwgeom_out->geoms ) /* We can't just realloc, since repalloc chokes on a starting null ptr. */
 
677
                                {
 
678
                                        lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, sizeof(LWGEOM*) * lwgeom_out->ngeoms);
 
679
                                }
 
680
                                else
 
681
                                {
 
682
                                        lwgeom_out->geoms = lwalloc(sizeof(LWGEOM*) * lwgeom_out->ngeoms);
 
683
                                }
 
684
                                lwgeom_out->geoms[lwgeom_out->ngeoms - 1] = oline;
 
685
                                lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
 
686
                                lwgeom_add_bbox((LWGEOM*)lwgeom_out);
 
687
                                if ( dp ) lwfree(dp);
 
688
                                dp = NULL;
 
689
                                if ( pa_out ) pa_out = NULL;
 
690
                        }
 
691
                        added_last_point = 0;
 
692
 
 
693
                }
 
694
        }
 
695
 
 
696
        /* Still some points left to be saved out. */
 
697
        if ( dp && dp->pa->npoints > 0 )
 
698
        {
 
699
                LWGEOM *oline;
 
700
                LWDEBUG(4, "saving pointarray to multi-line (2)");
 
701
                LWDEBUGF(4, "dp->pa->npoints == %d", dp->pa->npoints);
 
702
                LWDEBUGF(4, "lwgeom_out->ngeoms == %d", lwgeom_out->ngeoms);
 
703
 
 
704
                if ( dp->pa->npoints == 1 )
 
705
                {
 
706
                        oline = (LWGEOM*)lwpoint_construct(line->SRID, NULL, dp->pa);
 
707
                        oline->type = lwgeom_makeType(hasz, hasm, hassrid, POINTTYPE);
 
708
                        lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
 
709
                }
 
710
                else
 
711
                {
 
712
                        oline = (LWGEOM*)lwline_construct(line->SRID, NULL, dp->pa);
 
713
                        oline->type = lwgeom_makeType(hasz, hasm, hassrid, LINETYPE);
 
714
                }
 
715
 
 
716
                lwgeom_out->ngeoms++;
 
717
                if ( lwgeom_out->geoms ) /* We can't just realloc, since repalloc chokes on a starting null ptr. */
 
718
                {
 
719
                        lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, sizeof(LWGEOM*) * lwgeom_out->ngeoms);
 
720
                }
 
721
                else
 
722
                {
 
723
                        lwgeom_out->geoms = lwalloc(sizeof(LWGEOM*) * lwgeom_out->ngeoms);
 
724
                }
 
725
                lwgeom_out->geoms[lwgeom_out->ngeoms - 1] = oline;
 
726
                lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
 
727
                lwgeom_add_bbox((LWGEOM*)lwgeom_out);
 
728
                if ( dp ) lwfree(dp);
 
729
                dp = NULL;
 
730
        }
 
731
 
 
732
        lwfree(p);
 
733
        lwfree(q);
 
734
        lwfree(r);
 
735
 
 
736
        if ( lwgeom_out->ngeoms > 0 )
 
737
                return lwgeom_out;
 
738
 
 
739
        return NULL;
 
740
 
 
741
}
 
742
 
 
743
static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
 
744
 
 
745
/*
 
746
** Calculate the geohash, iterating downwards and gaining precision.
 
747
** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com>
 
748
** Released under the MIT License.
 
749
*/
 
750
char *geohash_point(double longitude, double latitude, int precision)
 
751
{
 
752
        int is_even=1, i=0;
 
753
        double lat[2], lon[2], mid;
 
754
        char bits[] = {16,8,4,2,1};
 
755
        int bit=0, ch=0;
 
756
        char *geohash = NULL;
 
757
 
 
758
        geohash = lwalloc(precision + 1);
 
759
 
 
760
        lat[0] = -90.0;
 
761
        lat[1] = 90.0;
 
762
        lon[0] = -180.0;
 
763
        lon[1] = 180.0;
 
764
 
 
765
        while (i < precision)
 
766
        {
 
767
                if (is_even)
 
768
                {
 
769
                        mid = (lon[0] + lon[1]) / 2;
 
770
                        if (longitude > mid)
 
771
                        {
 
772
                                ch |= bits[bit];
 
773
                                lon[0] = mid;
 
774
                        }
 
775
                        else
 
776
                        {
 
777
                                lon[1] = mid;
 
778
                        }
 
779
                }
 
780
                else
 
781
                {
 
782
                        mid = (lat[0] + lat[1]) / 2;
 
783
                        if (latitude > mid)
 
784
                        {
 
785
                                ch |= bits[bit];
 
786
                                lat[0] = mid;
 
787
                        }
 
788
                        else
 
789
                        {
 
790
                                lat[1] = mid;
 
791
                        }
 
792
                }
 
793
 
 
794
                is_even = !is_even;
 
795
                if (bit < 4)
 
796
                {
 
797
                        bit++;
 
798
                }
 
799
                else
 
800
                {
 
801
                        geohash[i++] = base32[ch];
 
802
                        bit = 0;
 
803
                        ch = 0;
 
804
                }
 
805
        }
 
806
        geohash[i] = 0;
 
807
        return geohash;
 
808
}
 
809
 
 
810
int lwgeom_geohash_precision(BOX3D bbox, BOX3D *bounds)
 
811
{
 
812
        double minx, miny, maxx, maxy;
 
813
        double latmax, latmin, lonmax, lonmin;
 
814
        double lonwidth, latwidth;
 
815
        double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
 
816
        int precision = 0;
 
817
 
 
818
        /* Get the bounding box, return error if things don't work out. */
 
819
        minx = bbox.xmin;
 
820
        miny = bbox.ymin;
 
821
        maxx = bbox.xmax;
 
822
        maxy = bbox.ymax;
 
823
 
 
824
        if ( minx == maxx && miny == maxy )
 
825
        {
 
826
                /* It's a point. Doubles have 51 bits of precision.
 
827
                ** 2 * 51 / 5 == 20 */
 
828
                return 20;
 
829
        }
 
830
 
 
831
        lonmin = -180.0;
 
832
        latmin = -90.0;
 
833
        lonmax = 180.0;
 
834
        latmax = 90.0;
 
835
 
 
836
        /* Shrink a world bounding box until one of the edges interferes with the
 
837
        ** bounds of our rectangle. */
 
838
        while ( 1 )
 
839
        {
 
840
                lonwidth = lonmax - lonmin;
 
841
                latwidth = latmax - latmin;
 
842
                latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
 
843
 
 
844
                if ( minx > lonmin + lonwidth / 2.0 )
 
845
                {
 
846
                        lonminadjust = lonwidth / 2.0;
 
847
                }
 
848
                else if ( maxx < lonmax - lonwidth / 2.0 )
 
849
                {
 
850
                        lonmaxadjust = -1 * lonwidth / 2.0;
 
851
                }
 
852
                if ( miny > latmin + latwidth / 2.0 )
 
853
                {
 
854
                        latminadjust = latwidth / 2.0;
 
855
                }
 
856
                else if (maxy < latmax - latwidth / 2.0 )
 
857
                {
 
858
                        latmaxadjust = -1 * latwidth / 2.0;
 
859
                }
 
860
                /* Only adjust if adjustments are legal (we haven't crossed any edges). */
 
861
                if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) )
 
862
                {
 
863
                        latmin += latminadjust;
 
864
                        lonmin += lonminadjust;
 
865
                        latmax += latmaxadjust;
 
866
                        lonmax += lonmaxadjust;
 
867
                        /* Each adjustment cycle corresponds to 2 bits of storage in the
 
868
                        ** geohash.     */
 
869
                        precision += 2;
 
870
                }
 
871
                else
 
872
                {
 
873
                        break;
 
874
                }
 
875
        }
 
876
 
 
877
        /* Save the edges of our bounds, in case someone cares later. */
 
878
        bounds->xmin = lonmin;
 
879
        bounds->xmax = lonmax;
 
880
        bounds->ymin = latmin;
 
881
        bounds->ymax = latmax;
 
882
 
 
883
        /* Each geohash character (base32) can contain 5 bits of information.
 
884
        ** We are returning the precision in characters, so here we divide. */
 
885
        return precision / 5;
 
886
}
 
887
 
 
888
 
 
889
/*
 
890
** Return a geohash string for the geometry. <http://geohash.org>
 
891
** Where the precision is non-positive, calculate a precision based on the
 
892
** bounds of the feature. Big features have loose precision.
 
893
** Small features have tight precision.
 
894
*/
 
895
char *lwgeom_geohash(const LWGEOM *lwgeom, int precision)
 
896
{
 
897
        BOX3D *bbox = NULL;
 
898
        BOX3D precision_bounds;
 
899
        double lat, lon;
 
900
 
 
901
        bbox = lwgeom_compute_box3d(lwgeom);
 
902
        if ( ! bbox ) return NULL;
 
903
 
 
904
        /* Return error if we are being fed something outside our working bounds */
 
905
        if ( bbox->xmin < -180 || bbox->ymin < -90 || bbox->xmax > 180 || bbox->ymax > 90 )
 
906
        {
 
907
                lwerror("Geohash requires inputs in decimal degrees.");
 
908
                lwfree(bbox);
 
909
                return NULL;
 
910
        }
 
911
 
 
912
        /* What is the center of our geometry bounds? We'll use that to
 
913
        ** approximate location. */
 
914
        lon = bbox->xmin + (bbox->xmax - bbox->xmin) / 2;
 
915
        lat = bbox->ymin + (bbox->ymax - bbox->ymin) / 2;
 
916
 
 
917
        if ( precision <= 0 )
 
918
        {
 
919
                precision = lwgeom_geohash_precision(*bbox, &precision_bounds);
 
920
        }
 
921
 
 
922
        lwfree(bbox);
 
923
 
 
924
        /*
 
925
        ** Return the geohash of the center, with a precision determined by the
 
926
        ** extent of the bounds.
 
927
        ** Possible change: return the point at the center of the precision bounds?
 
928
        */
 
929
        return geohash_point(lon, lat, precision);
 
930
}
 
931
 
 
932
 
 
933
 
 
934
 
 
935
 
 
936
 
 
937
 
 
938
 
 
939
 
 
940
 
 
941
 
 
942
 
 
943
 
 
944
 
 
945
 
 
946
 
 
947
 
 
948
 
 
949
 
 
950
 
 
951
 
 
952
 
 
953