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