geo_coder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|