forked from hans/Nominatim
Compare commits
1182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
282bd4a67e | ||
|
|
51f6db2e9c | ||
|
|
e4ecbef61e | ||
|
|
23dd49a5a2 | ||
|
|
0c85f88be8 | ||
|
|
7829a05002 | ||
|
|
233e064f0b | ||
|
|
203e210d3a | ||
|
|
ff1c78fef5 | ||
|
|
d3a731dae4 | ||
|
|
73a4433d8e | ||
|
|
3b4ffea690 | ||
|
|
05d7f91392 | ||
|
|
e3e9f69654 | ||
|
|
34a4a9b08f | ||
|
|
e0836664e5 | ||
|
|
8d7499342f | ||
|
|
a7b24627b5 | ||
|
|
452324cf01 | ||
|
|
15c5c8db24 | ||
|
|
423efd54e4 | ||
|
|
5e45e0b3d7 | ||
|
|
a60e7f2376 | ||
|
|
ac7f0f7581 | ||
|
|
9c872345d6 | ||
|
|
bd312fa747 | ||
|
|
573fba55af | ||
|
|
39787f7d62 | ||
|
|
f4c067d527 | ||
|
|
8d3595c3e2 | ||
|
|
b81a57f1e4 | ||
|
|
a624f8b599 | ||
|
|
74f49a9d89 | ||
|
|
b7b89b30ea | ||
|
|
fb012504b2 | ||
|
|
7ed9ecf350 | ||
|
|
3af1520461 | ||
|
|
a7edda32ba | ||
|
|
7b09e320a8 | ||
|
|
46e077c40b | ||
|
|
7753ba6019 | ||
|
|
511204c158 | ||
|
|
65daef70c1 | ||
|
|
7ab373e86d | ||
|
|
79b81d39d8 | ||
|
|
2bbe5017d4 | ||
|
|
765a932561 | ||
|
|
4a2c9431ee | ||
|
|
de15d10f86 | ||
|
|
55d414bd72 | ||
|
|
1560685020 | ||
|
|
0e44659033 | ||
|
|
3b39cfb1cf | ||
|
|
15bca71b0d | ||
|
|
3c12455c5b | ||
|
|
927b4c928e | ||
|
|
be47cd2549 | ||
|
|
a4a17f93f5 | ||
|
|
745e52b798 | ||
|
|
bbc2da2a4b | ||
|
|
4c1793b4e3 | ||
|
|
d1ca73f813 | ||
|
|
cdc7d0fe0e | ||
|
|
a27a271034 | ||
|
|
6c097d24b1 | ||
|
|
0115b655bd | ||
|
|
e8f1463cc2 | ||
|
|
e164d53fcc | ||
|
|
b8f7b3cc8d | ||
|
|
b0e6fb73c6 | ||
|
|
dd50f1737b | ||
|
|
38a99856c0 | ||
|
|
09e7f0d013 | ||
|
|
e05e413cc4 | ||
|
|
2c21cbb5e6 | ||
|
|
3bc4b4bf9f | ||
|
|
a09f2a6987 | ||
|
|
1f57d730df | ||
|
|
eebc72b2bc | ||
|
|
2f3cf19afa | ||
|
|
10fbda702b | ||
|
|
17f130550e | ||
|
|
251f335fe3 | ||
|
|
ed2fb84e82 | ||
|
|
634684236c | ||
|
|
11e0d9ec14 | ||
|
|
5fd8f5aa27 | ||
|
|
c05ddb6119 | ||
|
|
ec86a972a2 | ||
|
|
62da8a34f3 | ||
|
|
6511ec3aa8 | ||
|
|
1707157c4d | ||
|
|
ee49ab84a4 | ||
|
|
b92a55f5fe | ||
|
|
7d3b16f24c | ||
|
|
b612b99421 | ||
|
|
5a5b3de79a | ||
|
|
0862e21a1b | ||
|
|
c148b768f4 | ||
|
|
fab9f684af | ||
|
|
0af48fe802 | ||
|
|
a9ae2c7457 | ||
|
|
87c0049e75 | ||
|
|
e5eb7ecdc1 | ||
|
|
a7e7823535 | ||
|
|
33ff96fd83 | ||
|
|
58852b3eeb | ||
|
|
403ee260f6 | ||
|
|
84149f26df | ||
|
|
2ab836c11c | ||
|
|
7d9dbd62c7 | ||
|
|
c9a6350894 | ||
|
|
2a4198f94d | ||
|
|
850ab6999c | ||
|
|
2946e81995 | ||
|
|
c78a64ec9b | ||
|
|
61386c5b4d | ||
|
|
279eae4b92 | ||
|
|
37f7af56e4 | ||
|
|
ec2d491dc8 | ||
|
|
890d415e1f | ||
|
|
75f951d254 | ||
|
|
d4b633bfc5 | ||
|
|
b20a534e0c | ||
|
|
64f7b13888 | ||
|
|
7523359aba | ||
|
|
eae9e1cbfa | ||
|
|
178cb98795 | ||
|
|
8f0c628310 | ||
|
|
16794a84de | ||
|
|
189da9afb3 | ||
|
|
af97682cca | ||
|
|
bdd64093e5 | ||
|
|
1dc5a6e5f8 | ||
|
|
89a888de76 | ||
|
|
3be797c759 | ||
|
|
ad585771e7 | ||
|
|
853b536394 | ||
|
|
bfb20aaa47 | ||
|
|
cf4dcb12ed | ||
|
|
3811d916b9 | ||
|
|
db6d3ba486 | ||
|
|
dd5315cbaa | ||
|
|
f8ac0ef0b9 | ||
|
|
b56f7e8ad2 | ||
|
|
57ca1e0cf6 | ||
|
|
d9e0ef0ebf | ||
|
|
8237aba840 | ||
|
|
63781c4953 | ||
|
|
c822012aad | ||
|
|
7d192ace6d | ||
|
|
58e461e4c7 | ||
|
|
5dc10bd5a2 | ||
|
|
f9a098743b | ||
|
|
f1fe70656f | ||
|
|
e24ea7c1bb | ||
|
|
8e2e852b89 | ||
|
|
4c10294a29 | ||
|
|
31bf7443a6 | ||
|
|
e6d18fc948 | ||
|
|
a90ace7fa1 | ||
|
|
caa8210112 | ||
|
|
1c85edbda9 | ||
|
|
72d19cd523 | ||
|
|
cc17aa8d6b | ||
|
|
181e238b55 | ||
|
|
7d74bf781c | ||
|
|
a0fde50c08 | ||
|
|
8224cc34ea | ||
|
|
411f361fcb | ||
|
|
e080bdff0f | ||
|
|
fae8da2bcb | ||
|
|
2d1337e190 | ||
|
|
ec4e3c36af | ||
|
|
1489e6c00e | ||
|
|
61769a1bad | ||
|
|
56839ba50f | ||
|
|
fe6a2e9f14 | ||
|
|
11c91e3b8d | ||
|
|
e70f405abd | ||
|
|
8b8ee00725 | ||
|
|
121126cb50 | ||
|
|
9a13086122 | ||
|
|
c68833cd7f | ||
|
|
d4fa528d5c | ||
|
|
8e19336f49 | ||
|
|
52178caa98 | ||
|
|
e5cb5d439d | ||
|
|
e28fa6c787 | ||
|
|
313574ce97 | ||
|
|
96a84294f4 | ||
|
|
7611aa2f65 | ||
|
|
97fa7e0817 | ||
|
|
417b5b031b | ||
|
|
a7e26e8f59 | ||
|
|
7665e5a035 | ||
|
|
c9a553fdb4 | ||
|
|
5e072dabc3 | ||
|
|
e5b7424592 | ||
|
|
e99dc2a3da | ||
|
|
211214a8d3 | ||
|
|
e10d11c6c7 | ||
|
|
f0088ca2be | ||
|
|
3cd3d1f5ae | ||
|
|
14cef94e61 | ||
|
|
71ef94dae6 | ||
|
|
fc99954b2e | ||
|
|
1526501ed7 | ||
|
|
5d98c09ee9 | ||
|
|
7da5196bac | ||
|
|
7fd40cb0e6 | ||
|
|
b6b1c23575 | ||
|
|
409ded385f | ||
|
|
e2d0c9f3c1 | ||
|
|
9cf85f90fb | ||
|
|
fb796d14ec | ||
|
|
43c2eb383e | ||
|
|
2bc46b8f21 | ||
|
|
c84648c157 | ||
|
|
b15441df1c | ||
|
|
85f32d6c0f | ||
|
|
9908c93d4c | ||
|
|
388c7f706d | ||
|
|
36398eedca | ||
|
|
aa41b813b8 | ||
|
|
4e2fe6427c | ||
|
|
7f0a0ce5e5 | ||
|
|
2a39bc6e68 | ||
|
|
07c47eed54 | ||
|
|
a165072915 | ||
|
|
c5109d39d0 | ||
|
|
e4a51e460e | ||
|
|
2467e9996e | ||
|
|
3afd12f977 | ||
|
|
f45b3fa3f2 | ||
|
|
fc6b08c8ab | ||
|
|
441cd27a53 | ||
|
|
c73737f77f | ||
|
|
ecd92d5e71 | ||
|
|
d1143b4580 | ||
|
|
09595697cc | ||
|
|
1f887a6ca0 | ||
|
|
eba6e46c74 | ||
|
|
f0daf11375 | ||
|
|
71341a623a | ||
|
|
e2a7a795d4 | ||
|
|
ed7d7a9ad9 | ||
|
|
9b69bde613 | ||
|
|
119ffbab40 | ||
|
|
e92b54b869 | ||
|
|
0273e128f4 | ||
|
|
a948050015 | ||
|
|
a0dbeabed1 | ||
|
|
eb615347d2 | ||
|
|
2d4063234a | ||
|
|
4fcb66df92 | ||
|
|
a9bdac836c | ||
|
|
bb696f3fd0 | ||
|
|
bc26244114 | ||
|
|
08c2f03ccc | ||
|
|
9e35e5c2b0 | ||
|
|
d10f63b666 | ||
|
|
aa6ac5a751 | ||
|
|
42e79bfab9 | ||
|
|
a3b4f80c99 | ||
|
|
b2f3cfde0b | ||
|
|
c036480ce2 | ||
|
|
627a487fcf | ||
|
|
b0d0d046e7 | ||
|
|
e09c42a78a | ||
|
|
28d7e11e4f | ||
|
|
a5d35d6e84 | ||
|
|
e8982068b6 | ||
|
|
6e3670bce5 | ||
|
|
14aca11dcd | ||
|
|
e7b738fe35 | ||
|
|
1ee636461c | ||
|
|
c7e7d5e980 | ||
|
|
57bf76a0e1 | ||
|
|
c80c80200c | ||
|
|
5859f9a3cb | ||
|
|
a825414558 | ||
|
|
6577be3744 | ||
|
|
e936041713 | ||
|
|
a8b31090a1 | ||
|
|
14e708f366 | ||
|
|
3502ff837f | ||
|
|
84cfe5db53 | ||
|
|
26bd8304c6 | ||
|
|
ee3973f507 | ||
|
|
55fa051d3a | ||
|
|
4d38833170 | ||
|
|
e47646ddba | ||
|
|
7081e2ab66 | ||
|
|
e286536959 | ||
|
|
88374c2522 | ||
|
|
3fcfab9772 | ||
|
|
de8888ec58 | ||
|
|
3af8dc9580 | ||
|
|
71ae7f10f7 | ||
|
|
38304136d3 | ||
|
|
ff4b1758e1 | ||
|
|
a9b4894b07 | ||
|
|
28ba5fc8a4 | ||
|
|
c7a4c2c88a | ||
|
|
0617768ee2 | ||
|
|
62b60c70e6 | ||
|
|
0394f32438 | ||
|
|
569184a5b0 | ||
|
|
41c4b51be5 | ||
|
|
513bf485f2 | ||
|
|
263240919c | ||
|
|
d2b9493d72 | ||
|
|
60cea6c8bf | ||
|
|
9b7f0627ea | ||
|
|
2e9cebf025 | ||
|
|
48d4ea5542 | ||
|
|
9bdbbec0c8 | ||
|
|
8f0b3cb00f | ||
|
|
646fa53b44 | ||
|
|
7f10264fb6 | ||
|
|
7e0fdf5928 | ||
|
|
4a28d28c08 | ||
|
|
bfea79f1e4 | ||
|
|
87d78e87d2 | ||
|
|
c712c6e55e | ||
|
|
510054492c | ||
|
|
12db7a9af2 | ||
|
|
fcab682231 | ||
|
|
0981a6403d | ||
|
|
37bd4dfd83 | ||
|
|
057d77fcd4 | ||
|
|
713ea080d2 | ||
|
|
bf5063de9a | ||
|
|
13efedde34 | ||
|
|
c1beefd543 | ||
|
|
1d81c17335 | ||
|
|
b8b87716db | ||
|
|
1108bf7d86 | ||
|
|
62747c934d | ||
|
|
81b90c9f15 | ||
|
|
5a772a5770 | ||
|
|
3433ec306e | ||
|
|
03039ebfa8 | ||
|
|
271c23f459 | ||
|
|
0892eab1d3 | ||
|
|
d68996127d | ||
|
|
83270557a7 | ||
|
|
0e4f80bf1b | ||
|
|
b7abc8566e | ||
|
|
92f86de938 | ||
|
|
b17019a21c | ||
|
|
be58b929f2 | ||
|
|
25baaf530d | ||
|
|
320d488627 | ||
|
|
879f818d81 | ||
|
|
80a6751c51 | ||
|
|
05bef92f0f | ||
|
|
01d5ecb86b | ||
|
|
f50f46c1ce | ||
|
|
adae49b184 | ||
|
|
2bd7c75a35 | ||
|
|
9955155ce0 | ||
|
|
b0e0f7ae76 | ||
|
|
0315b87664 | ||
|
|
ac29f8bc91 | ||
|
|
ec6a427e0a | ||
|
|
09b59bd56a | ||
|
|
d0548caa76 | ||
|
|
96d2a331a1 | ||
|
|
1d7ed6737e | ||
|
|
97b6656182 | ||
|
|
f108eac527 | ||
|
|
144c3b3eb2 | ||
|
|
426e108b34 | ||
|
|
229ad01042 | ||
|
|
71e3d8b1de | ||
|
|
a5750a6ef6 | ||
|
|
d5e39260b3 | ||
|
|
0996fdfb55 | ||
|
|
07eb108a6d | ||
|
|
41249377d2 | ||
|
|
be091b17d9 | ||
|
|
398467b2f4 | ||
|
|
796f069d74 | ||
|
|
82b6245aaa | ||
|
|
f0e5cf53b8 | ||
|
|
7585d37818 | ||
|
|
ee54ebfe77 | ||
|
|
43ee4a8faf | ||
|
|
ab5bcd6d2f | ||
|
|
6b8c99a275 | ||
|
|
073221d321 | ||
|
|
dfb9579a73 | ||
|
|
26bc83c984 | ||
|
|
8139a079f8 | ||
|
|
0d341c256b | ||
|
|
5a17bfc9c9 | ||
|
|
1d981b3171 | ||
|
|
743ec43460 | ||
|
|
87ee3a6f58 | ||
|
|
10897787af | ||
|
|
4d073b0350 | ||
|
|
625018b654 | ||
|
|
5f2410119d | ||
|
|
424c0d0ebb | ||
|
|
71c9adfdba | ||
|
|
723bb4d0b9 | ||
|
|
cb76635da7 | ||
|
|
237e31b3ce | ||
|
|
d0741f21b1 | ||
|
|
a376608344 | ||
|
|
7a964efb3a | ||
|
|
1d0da944a6 | ||
|
|
d0880694eb | ||
|
|
1f689bdaae | ||
|
|
6a0361d0c6 | ||
|
|
f29c7bf910 | ||
|
|
2cc4c73b64 | ||
|
|
8841a328c8 | ||
|
|
c555b60b36 | ||
|
|
b30e2ab5dc | ||
|
|
115792d1db | ||
|
|
7075a5828e | ||
|
|
bd04ce62e0 | ||
|
|
3bb6ecdc3b | ||
|
|
a885e7309a | ||
|
|
6706a23fb5 | ||
|
|
c7faab4d7c | ||
|
|
080ba00956 | ||
|
|
2613ebfa01 | ||
|
|
53c526c01d | ||
|
|
dc371618ba | ||
|
|
59288417f0 | ||
|
|
1dd401b570 | ||
|
|
ee194ab369 | ||
|
|
b8113abd93 | ||
|
|
5182da9f45 | ||
|
|
14c25717ab | ||
|
|
3087ac1145 | ||
|
|
cdbde5b88d | ||
|
|
7a1ee99345 | ||
|
|
7a31a3d106 | ||
|
|
fe3dba3fd7 | ||
|
|
31c7f25541 | ||
|
|
45aef06d00 | ||
|
|
0eb71cdce8 | ||
|
|
45bc511955 | ||
|
|
dedf56b5f8 | ||
|
|
1e28f2478c | ||
|
|
3cdbcbff8f | ||
|
|
9a3bc9cc1e | ||
|
|
6bd905ff43 | ||
|
|
ae83ceab5e | ||
|
|
28ee59dd64 | ||
|
|
b4ef3d91ab | ||
|
|
efac4a135a | ||
|
|
984e91e519 | ||
|
|
49dc62201c | ||
|
|
4791fc341e | ||
|
|
4743a5e166 | ||
|
|
908c66ca84 | ||
|
|
62719f58c9 | ||
|
|
d183cd3c78 | ||
|
|
ac5a901daf | ||
|
|
aaee03d502 | ||
|
|
329948e685 | ||
|
|
f6a76ebcd5 | ||
|
|
1cb87164d9 | ||
|
|
64fa70ac0a | ||
|
|
2c42bda9ce | ||
|
|
1787892d32 | ||
|
|
7cc8f63125 | ||
|
|
2b6515e704 | ||
|
|
27bc8d4f7b | ||
|
|
90d531c640 | ||
|
|
2c24b9da5a | ||
|
|
d79a2bb17e | ||
|
|
4ac1bf2d47 | ||
|
|
8b4b647d77 | ||
|
|
6495717036 | ||
|
|
d23fa84471 | ||
|
|
937c8ba231 | ||
|
|
88beeb7916 | ||
|
|
34a27c7cab | ||
|
|
c04541b4da | ||
|
|
8d4a86635f | ||
|
|
2dc6ee7e1c | ||
|
|
ccab565a4a | ||
|
|
5c8fbe8186 | ||
|
|
7fd46dcee9 | ||
|
|
3ef4c4fbe7 | ||
|
|
47258f40ea | ||
|
|
123a3c0347 | ||
|
|
d60a2e693c | ||
|
|
db524a35c6 | ||
|
|
f23a860b33 | ||
|
|
df008d99f5 | ||
|
|
fd920fba9b | ||
|
|
d9cd8c6fff | ||
|
|
b303c785e9 | ||
|
|
36fa21d7ce | ||
|
|
f1a388700f | ||
|
|
2b66a7a39a | ||
|
|
4daa584b7d | ||
|
|
de9507bc63 | ||
|
|
2b48d09286 | ||
|
|
97741aaf1c | ||
|
|
146779340c | ||
|
|
d1f6fab68a | ||
|
|
c835918123 | ||
|
|
fd9345cda3 | ||
|
|
8a615ad969 | ||
|
|
eaaa4a7b31 | ||
|
|
c3e5654113 | ||
|
|
ff2a40b109 | ||
|
|
7e00a6e2ff | ||
|
|
8ee36fb78c | ||
|
|
c3483747eb | ||
|
|
3fda792929 | ||
|
|
ba57a9ba07 | ||
|
|
a489ac07cd | ||
|
|
3505417e3f | ||
|
|
29e78780e5 | ||
|
|
868caeaf1b | ||
|
|
7f72c7b5fc | ||
|
|
e428019170 | ||
|
|
e9407cd48d | ||
|
|
5042be1b72 | ||
|
|
315713ff9a | ||
|
|
7b3fb23216 | ||
|
|
1d6861667b | ||
|
|
ae1df044e2 | ||
|
|
9d2de46c47 | ||
|
|
dfa74daf52 | ||
|
|
d4110eef7e | ||
|
|
86833454a4 | ||
|
|
b8f7563da9 | ||
|
|
e080ccbcf8 | ||
|
|
13469e1576 | ||
|
|
8f23ba076b | ||
|
|
2cf1ff41c0 | ||
|
|
118517b076 | ||
|
|
45abcbc301 | ||
|
|
d5df1c8ae3 | ||
|
|
9712decefe | ||
|
|
6ba87c37d6 | ||
|
|
b06bc799bc | ||
|
|
a36b316079 | ||
|
|
c54fc44b33 | ||
|
|
c7b903f4b0 | ||
|
|
cdfa31c390 | ||
|
|
3d51c2a4e7 | ||
|
|
b94229fb8e | ||
|
|
637c5c2936 | ||
|
|
cbaabe7c24 | ||
|
|
35c7269bac | ||
|
|
ed85388de5 | ||
|
|
f79434f49d | ||
|
|
fcba2eabc4 | ||
|
|
e523b34db9 | ||
|
|
96b5f8786b | ||
|
|
1a1e0ef138 | ||
|
|
2d3ea552c4 | ||
|
|
c4e72e6ca9 | ||
|
|
9f6f3dd75d | ||
|
|
6b994cb5ff | ||
|
|
c44324fda5 | ||
|
|
8d91a88b22 | ||
|
|
f7258e314d | ||
|
|
6a3c6c43ea | ||
|
|
6c1977b448 | ||
|
|
71602afcad | ||
|
|
0c053431f5 | ||
|
|
185a983c9d | ||
|
|
adbbb1ce02 | ||
|
|
f78d094483 | ||
|
|
7eeb79ce67 | ||
|
|
7caa67d8ec | ||
|
|
919b1b42fa | ||
|
|
760807c5e0 | ||
|
|
9ac401267a | ||
|
|
a71200a57a | ||
|
|
b062e7e774 | ||
|
|
d42aa08705 | ||
|
|
282c6777ee | ||
|
|
9981d74ee1 | ||
|
|
1a4506f6ab | ||
|
|
914caab43d | ||
|
|
5eb11800a7 | ||
|
|
1424e8e29b | ||
|
|
42f079c355 | ||
|
|
8f884d7f23 | ||
|
|
2cf21a3008 | ||
|
|
2361ca2c71 | ||
|
|
3cee2d185d | ||
|
|
cc785ccad0 | ||
|
|
da4a2b7b6e | ||
|
|
f17be2403f | ||
|
|
47bb49384e | ||
|
|
8eed1a8bec | ||
|
|
fcf7fcee03 | ||
|
|
5c18d6865d | ||
|
|
cdf8c67898 | ||
|
|
00265af528 | ||
|
|
77b76ae51b | ||
|
|
9ef2370a2a | ||
|
|
c700421aa7 | ||
|
|
77abe882ab | ||
|
|
023f94b066 | ||
|
|
7ea1ef3feb | ||
|
|
df463f4ea6 | ||
|
|
3da4c9c384 | ||
|
|
97bc185152 | ||
|
|
c8780da19c | ||
|
|
c02bf4986f | ||
|
|
9a5d5d9aec | ||
|
|
2c62a8dbbc | ||
|
|
55629a4891 | ||
|
|
907133a38c | ||
|
|
86c0858130 | ||
|
|
30511fd3ab | ||
|
|
614a6ab861 | ||
|
|
4bff2814a9 | ||
|
|
8e0ffde3e0 | ||
|
|
795153b213 | ||
|
|
fd08d41962 | ||
|
|
75e35f3832 | ||
|
|
16268f92cc | ||
|
|
d72c863353 | ||
|
|
96b6a1a418 | ||
|
|
0067555c38 | ||
|
|
77d4453334 | ||
|
|
c563c2bfec | ||
|
|
266153f218 | ||
|
|
73e737d775 | ||
|
|
5029101048 | ||
|
|
0c9a241487 | ||
|
|
41d2cd318d | ||
|
|
0d2cdb5c2f | ||
|
|
f8d55b5448 | ||
|
|
00a3a8834b | ||
|
|
32f6ddf6db | ||
|
|
1220ff5da6 | ||
|
|
89c576fbe1 | ||
|
|
e276ec2e94 | ||
|
|
bafbf679b6 | ||
|
|
8e2ef2842e | ||
|
|
218b70fd96 | ||
|
|
e3323e8888 | ||
|
|
eacaf3489e | ||
|
|
2deac34648 | ||
|
|
e8c52c6780 | ||
|
|
e7e7ae0104 | ||
|
|
0d4c1e8460 | ||
|
|
cdabea7c76 | ||
|
|
749091bf3a | ||
|
|
28810e6ce0 | ||
|
|
f2c15b73ad | ||
|
|
a88527b2a0 | ||
|
|
b1e8db7ca7 | ||
|
|
06657b3e10 | ||
|
|
81a7ea36db | ||
|
|
af74c037f4 | ||
|
|
6796749136 | ||
|
|
e67a6dc321 | ||
|
|
15a215729e | ||
|
|
ce95c55d65 | ||
|
|
8eb066c692 | ||
|
|
a0de20e9bc | ||
|
|
2dbf58d461 | ||
|
|
9a47e1834f | ||
|
|
61ed3b8ab3 | ||
|
|
bb1552be29 | ||
|
|
5614ece9a1 | ||
|
|
3546b30473 | ||
|
|
cf32da3748 | ||
|
|
909b0c7462 | ||
|
|
15cd5c777b | ||
|
|
37c653396b | ||
|
|
8c4bcd36ea | ||
|
|
88610b1b74 | ||
|
|
9aeb111fba | ||
|
|
7ca5219297 | ||
|
|
f4a00eba26 | ||
|
|
86a8900e21 | ||
|
|
67bb885900 | ||
|
|
e55ac77c94 | ||
|
|
f9205caf22 | ||
|
|
3c9af7f151 | ||
|
|
5e54e78176 | ||
|
|
caf018538f | ||
|
|
ccae2c733b | ||
|
|
5673c4cf91 | ||
|
|
ec8af1dd40 | ||
|
|
50c5abf6bb | ||
|
|
a44377c7b0 | ||
|
|
5237f44c4a | ||
|
|
99e9abe843 | ||
|
|
5b4bbab9be | ||
|
|
413c69ddc9 | ||
|
|
53f8459e97 | ||
|
|
3714b7ea7d | ||
|
|
0ecb920866 | ||
|
|
563099f7fa | ||
|
|
ce76a25101 | ||
|
|
57dc0304b5 | ||
|
|
872e73314e | ||
|
|
727bd73d0b | ||
|
|
a2a1901b09 | ||
|
|
43869b9938 | ||
|
|
9a86c0cebc | ||
|
|
d0cc4006d3 | ||
|
|
dc2911ae72 | ||
|
|
16053e81bf | ||
|
|
d59d57957c | ||
|
|
4e792546e8 | ||
|
|
79aa74b771 | ||
|
|
bdec4e6488 | ||
|
|
71ddeb40a6 | ||
|
|
80ef6cbaab | ||
|
|
15dbb6383c | ||
|
|
3e9fb0dc84 | ||
|
|
95df39c292 | ||
|
|
aab41b78af | ||
|
|
96ecee431b | ||
|
|
64bf44eaf0 | ||
|
|
9996cc495c | ||
|
|
bce11a5fc7 | ||
|
|
cce57139cf | ||
|
|
937bead547 | ||
|
|
3fba5e7867 | ||
|
|
a27e191335 | ||
|
|
8c087e4cb8 | ||
|
|
39bab5f4a1 | ||
|
|
d004a0e3df | ||
|
|
bc0cf74ba7 | ||
|
|
e64e3b4939 | ||
|
|
6de0a854fb | ||
|
|
97ad4e4654 | ||
|
|
748fa3e28f | ||
|
|
7ef0239f19 | ||
|
|
2daa4ed813 | ||
|
|
8714fa7dec | ||
|
|
896421fe9f | ||
|
|
4e6c75040e | ||
|
|
7448d3f171 | ||
|
|
5e5713b90f | ||
|
|
9785aff76c | ||
|
|
f4e958a66d | ||
|
|
5a5691b5b7 | ||
|
|
9848381a04 | ||
|
|
623d58b67e | ||
|
|
6985abc3fd | ||
|
|
0628aa887f | ||
|
|
c13094acfd | ||
|
|
308bfcbf25 | ||
|
|
de0ad657ba | ||
|
|
13674c3939 | ||
|
|
b9cc47ee67 | ||
|
|
6db110f5cb | ||
|
|
af81c6aa94 | ||
|
|
54393addd3 | ||
|
|
e3fb706c65 | ||
|
|
2dd7f0156a | ||
|
|
5b8672c10a | ||
|
|
7a14792e21 | ||
|
|
d88c484fa1 | ||
|
|
bdd90e890b | ||
|
|
b5fc5e0a71 | ||
|
|
002fa35b92 | ||
|
|
d023578f90 | ||
|
|
1876fe42c9 | ||
|
|
86acaa5264 | ||
|
|
d898f9aabf | ||
|
|
33c919ab09 | ||
|
|
deca74bc3a | ||
|
|
74b3251c7a | ||
|
|
65afe13f0a | ||
|
|
bd4b1b2d08 | ||
|
|
1a1526ec30 | ||
|
|
6830b1229e | ||
|
|
e7c8b498d1 | ||
|
|
b15b996cc4 | ||
|
|
d8ade1c527 | ||
|
|
6043cd5ad8 | ||
|
|
86d5209118 | ||
|
|
c629573230 | ||
|
|
291a161cc6 | ||
|
|
4c7145c293 | ||
|
|
4d4b95923e | ||
|
|
71e6d5f9a6 | ||
|
|
446a5a95bc | ||
|
|
19edbd3581 | ||
|
|
166737fb76 | ||
|
|
b6be33cded | ||
|
|
c80637b05c | ||
|
|
3ec67b9193 | ||
|
|
40debbc7b5 | ||
|
|
ccecabfbf4 | ||
|
|
1e30e578e0 | ||
|
|
29e9683293 | ||
|
|
8138729aea | ||
|
|
9bb81731f8 | ||
|
|
050b0a2ced | ||
|
|
3bb903cf92 | ||
|
|
41afcaddd5 | ||
|
|
8c7fa0213f | ||
|
|
49b5e4dbad | ||
|
|
c8e79397f5 | ||
|
|
43eedfd253 | ||
|
|
ee1b25820e | ||
|
|
193cf66328 | ||
|
|
77e55fade2 | ||
|
|
09f1661638 | ||
|
|
e841422b1f | ||
|
|
97ade7dd7e | ||
|
|
e24f53cb34 | ||
|
|
62b7b76662 | ||
|
|
c7434264ae | ||
|
|
026081e131 | ||
|
|
47586fd861 | ||
|
|
8e79ece203 | ||
|
|
a23bd80a31 | ||
|
|
f4a86ff4c4 | ||
|
|
73c151b3ac | ||
|
|
0a10be6586 | ||
|
|
1e3bf9729e | ||
|
|
924d3a8d6e | ||
|
|
2a07bb8a7f | ||
|
|
b3186d07f5 | ||
|
|
ecee3828b3 | ||
|
|
a64f992092 | ||
|
|
5682d1688d | ||
|
|
57b35654e7 | ||
|
|
a67edd328e | ||
|
|
20a4cab57e | ||
|
|
ab3b556144 | ||
|
|
1649191ffd | ||
|
|
41fce277cd | ||
|
|
39c4c7fce8 | ||
|
|
3a696e1895 | ||
|
|
a793f698f3 | ||
|
|
889637321b | ||
|
|
a52455f160 | ||
|
|
9358243b66 | ||
|
|
9e54d1d6eb | ||
|
|
1aaeee19a1 | ||
|
|
6153ad3b31 | ||
|
|
7e3af2debc | ||
|
|
450a12c6be | ||
|
|
196c55e341 | ||
|
|
2c3317f582 | ||
|
|
e0c066d850 | ||
|
|
e1cbca2572 | ||
|
|
e1095205e4 | ||
|
|
e5c79a1d1f | ||
|
|
6551399bcc | ||
|
|
c48fb88e6b | ||
|
|
e04838cc71 | ||
|
|
0916d72ddf | ||
|
|
a933421860 | ||
|
|
66d7f1a058 | ||
|
|
e77a07cbb3 | ||
|
|
c897b96c41 | ||
|
|
679e4c47ce | ||
|
|
13ab03a03a | ||
|
|
56962deb30 | ||
|
|
e86516bbaf | ||
|
|
b6acd24e5a | ||
|
|
e31cd9717c | ||
|
|
6eb6f35f24 | ||
|
|
e9decd2574 | ||
|
|
2fd9ffda43 | ||
|
|
a84a7a70f3 | ||
|
|
dcab7a19e5 | ||
|
|
d4c78a982e | ||
|
|
7716e0bc92 | ||
|
|
5dea0658e4 | ||
|
|
cfd03d7be3 | ||
|
|
9240e83802 | ||
|
|
2c07d5e000 | ||
|
|
ea5fe54c6b | ||
|
|
08d1ec09cc | ||
|
|
b8db8301df | ||
|
|
d18bd14045 | ||
|
|
65500927c2 | ||
|
|
4a5a997e18 | ||
|
|
fadffeaa2d | ||
|
|
c0e4a74c71 | ||
|
|
b2be8c3ab7 | ||
|
|
5252051291 | ||
|
|
ccaea09a65 | ||
|
|
b2c1d086b5 | ||
|
|
3a787df934 | ||
|
|
635ce30db5 | ||
|
|
201f618cc7 | ||
|
|
81922fc057 | ||
|
|
b9a58b8f24 | ||
|
|
b75aadfb6b | ||
|
|
80a74181e4 | ||
|
|
6f4f19004c | ||
|
|
e2f23e391b | ||
|
|
f2debbef19 | ||
|
|
21a3fc4b0f | ||
|
|
0e9e2bbdca | ||
|
|
4f2d73aa7c | ||
|
|
c20f8b13a5 | ||
|
|
7f4e7a2579 | ||
|
|
82a0e23265 | ||
|
|
e36e485698 | ||
|
|
ddb7296663 | ||
|
|
604706a827 | ||
|
|
c594644aa7 | ||
|
|
47f94c6988 | ||
|
|
c56c09e2c0 | ||
|
|
65bf6dbff7 | ||
|
|
7273b58bbe | ||
|
|
adb6ea546f | ||
|
|
3ff8bb55a7 | ||
|
|
99c58706d3 | ||
|
|
523873fe16 | ||
|
|
de01c5b23d | ||
|
|
57dc135844 | ||
|
|
e24129febe | ||
|
|
b27926fd4d | ||
|
|
cfb0f3f94c | ||
|
|
eaee6b700d | ||
|
|
032f24bfef | ||
|
|
d93209ee48 | ||
|
|
eb79e655e6 | ||
|
|
4f3074e538 | ||
|
|
8312e8f539 | ||
|
|
d4daa0c4fa | ||
|
|
a9ad69efae | ||
|
|
0a45dbf5f6 | ||
|
|
a61f852cd2 | ||
|
|
26ba5bf0be | ||
|
|
702f9477ab | ||
|
|
179542f938 | ||
|
|
b77e2503f5 | ||
|
|
1542a006cb | ||
|
|
bb1c3f23ab | ||
|
|
8650e0fedd | ||
|
|
abac5d8ebd | ||
|
|
aa7c8b6b5b | ||
|
|
e69115fd8f | ||
|
|
7b1f35426d | ||
|
|
6757e1b865 | ||
|
|
c72e6a93dc | ||
|
|
c0b4411337 | ||
|
|
0e613a362e | ||
|
|
4123a3fd6b | ||
|
|
087522c601 | ||
|
|
fb51d51582 | ||
|
|
f5641037cf | ||
|
|
39580057fc | ||
|
|
a855ffe58e | ||
|
|
42d473968f | ||
|
|
8a2e401025 | ||
|
|
a912b3448f | ||
|
|
8b896f37f3 | ||
|
|
6bf90eb22f | ||
|
|
421174ce0c | ||
|
|
1982978f74 | ||
|
|
6fd2887543 | ||
|
|
44ee8d9ce3 | ||
|
|
bf77b444bf | ||
|
|
a7da0c90bf | ||
|
|
6238ae6032 | ||
|
|
d437a14f25 | ||
|
|
e1be3d9f48 | ||
|
|
e686ce8680 | ||
|
|
93c26a0e65 | ||
|
|
02550cc4dc | ||
|
|
b243aa84e2 | ||
|
|
cfed4bc204 | ||
|
|
4a321487f3 | ||
|
|
74c06ef877 | ||
|
|
51155b639b | ||
|
|
775fdeb8e4 | ||
|
|
eb0b3bfa4c | ||
|
|
2a784fa3d4 | ||
|
|
5c40100b57 | ||
|
|
ae5cc28a1a | ||
|
|
cf39e07dba | ||
|
|
e49ed1bb13 | ||
|
|
3887423381 | ||
|
|
f05ea577f4 | ||
|
|
effd8e12af | ||
|
|
89c157a6c1 | ||
|
|
832547f192 | ||
|
|
36fdfd92ad | ||
|
|
cd6dcfa574 | ||
|
|
8a796db3c9 | ||
|
|
87b6810eba | ||
|
|
50c994be93 | ||
|
|
797c2d1e74 | ||
|
|
d7c469d94e | ||
|
|
3b7baca46c | ||
|
|
0957bd27c6 | ||
|
|
5464dec057 | ||
|
|
feb458f8dd | ||
|
|
9c1e0c49bf | ||
|
|
36f2afea8a | ||
|
|
5c187e131a | ||
|
|
cb68a2cdd2 | ||
|
|
3499ed7b82 | ||
|
|
9ff24b9107 | ||
|
|
ee7a6e8f92 | ||
|
|
88d623b631 | ||
|
|
2a97ce6aed | ||
|
|
bb403e2e58 | ||
|
|
496d6bf168 | ||
|
|
b00169e634 | ||
|
|
9be58ffc7c | ||
|
|
1fb847a5ad | ||
|
|
8a823d1361 | ||
|
|
ab30a69ad9 | ||
|
|
0a95bdc5f5 | ||
|
|
d594bb238b | ||
|
|
0d179b8c97 | ||
|
|
1b33999f10 | ||
|
|
3f06d9d3ba | ||
|
|
7da9178b83 | ||
|
|
a766240c06 | ||
|
|
a827367a2f | ||
|
|
4f75f4cb6e | ||
|
|
975c30afba | ||
|
|
ca92442601 | ||
|
|
77ca079688 | ||
|
|
665de339e5 | ||
|
|
84495489d9 | ||
|
|
8eb0e09ce3 | ||
|
|
2ab43f6367 | ||
|
|
b14bc75dc4 | ||
|
|
860acbf6af | ||
|
|
10c5d7f6f6 | ||
|
|
e842d8793c | ||
|
|
348a3082c9 | ||
|
|
033b9590bd | ||
|
|
95961d098d | ||
|
|
7a50d2f996 | ||
|
|
6fef943e49 | ||
|
|
76b9eb8098 | ||
|
|
d45524cbfb | ||
|
|
aa9fff9199 | ||
|
|
1a47376040 | ||
|
|
051998dd80 | ||
|
|
cf4a44aaf7 | ||
|
|
85c72cdccd | ||
|
|
3a41b0dfbf | ||
|
|
6436bab1be | ||
|
|
91e1e4d614 | ||
|
|
af93973237 | ||
|
|
2e08a61565 | ||
|
|
0adb8cb765 | ||
|
|
f71e9dd187 | ||
|
|
186a633185 | ||
|
|
d1b1acaf1d | ||
|
|
ff6c3a705b | ||
|
|
badd2e46aa | ||
|
|
98e5ba6b61 | ||
|
|
2156fb1ad7 | ||
|
|
92095c9247 | ||
|
|
524bd47315 | ||
|
|
c3e21ea388 | ||
|
|
f0d962f8a0 | ||
|
|
e4963256e1 | ||
|
|
362f259f47 | ||
|
|
8b91284f7f | ||
|
|
2a0c7fb57a | ||
|
|
2fecc0c465 | ||
|
|
94d795883f | ||
|
|
f0f6fcf256 | ||
|
|
28041cbbd9 | ||
|
|
821949f087 | ||
|
|
0fcab79486 | ||
|
|
234b1b0293 | ||
|
|
f07d620ee8 | ||
|
|
190a72cab5 | ||
|
|
1a4f369e2b | ||
|
|
ce112f7c20 | ||
|
|
4e0e0c1797 | ||
|
|
54ef9105d0 | ||
|
|
9e1421fb6c | ||
|
|
2ba9c11dd0 | ||
|
|
db719d489f | ||
|
|
7a5aab61ae | ||
|
|
7b4806b2e8 | ||
|
|
a10f537131 | ||
|
|
89c556d163 | ||
|
|
b03be15442 | ||
|
|
7879ad44cd | ||
|
|
131527bdab | ||
|
|
a244c41ea7 | ||
|
|
a32f6c66b8 | ||
|
|
b4f0b8b728 | ||
|
|
af06d31d21 | ||
|
|
d01d0c13f4 | ||
|
|
be268f95e6 | ||
|
|
a86563cfb1 | ||
|
|
009882999f | ||
|
|
1da4fed9b5 | ||
|
|
6ee4b30ee9 | ||
|
|
4f9f21f661 | ||
|
|
527c3390f4 | ||
|
|
f9a488cd5a | ||
|
|
02bd322c1d | ||
|
|
a0acc6e913 | ||
|
|
0419aada6e | ||
|
|
6f442b6617 | ||
|
|
a0a5100807 | ||
|
|
a769fc4ff3 | ||
|
|
83cca343c3 | ||
|
|
bfcbb94aed | ||
|
|
5d928d5187 | ||
|
|
23fa6018a4 | ||
|
|
53eb9a5f14 | ||
|
|
443bfec035 | ||
|
|
8534ce5ec2 | ||
|
|
92bedbb881 | ||
|
|
7090604de7 | ||
|
|
7dd0a63b68 | ||
|
|
a4ecd9d73e | ||
|
|
e94b667e16 | ||
|
|
f6127df15d | ||
|
|
a8afb9bd3c | ||
|
|
86622662bf | ||
|
|
291fdd1894 | ||
|
|
46cef36184 | ||
|
|
bc449e10be | ||
|
|
d8703c223b | ||
|
|
28166c5bb1 | ||
|
|
54fdc572c1 | ||
|
|
6fb8fab665 | ||
|
|
df6b433066 | ||
|
|
26e30bf8e1 | ||
|
|
49d0ce1de4 | ||
|
|
d020e05f5b | ||
|
|
c346c57341 | ||
|
|
d237549496 | ||
|
|
607fef2d8f | ||
|
|
a50bf1c944 | ||
|
|
4ea0074548 | ||
|
|
4b46c73088 | ||
|
|
6fc32d9645 | ||
|
|
279ea25e7d | ||
|
|
50f17666fa | ||
|
|
b097840ba4 | ||
|
|
22ebd1c7a9 | ||
|
|
df764a3f3b | ||
|
|
5ccb499a6f | ||
|
|
b24ef60b33 | ||
|
|
6450126933 | ||
|
|
314a9e52e9 | ||
|
|
5d7bd37ab2 | ||
|
|
c96e71a3d7 | ||
|
|
e3f9581a69 | ||
|
|
1d89098f77 | ||
|
|
71b3aa9cca | ||
|
|
991bd0004a | ||
|
|
d3ff9600b5 | ||
|
|
df9850f5ca | ||
|
|
fa9677242c | ||
|
|
131b06fec3 | ||
|
|
8e2b5d7ce3 | ||
|
|
edaa1f7fcd | ||
|
|
30d56b7064 | ||
|
|
1ed793f000 | ||
|
|
ae9291f625 | ||
|
|
1ad6f4a1ce | ||
|
|
932abeb0e2 | ||
|
|
35a0f1b246 | ||
|
|
cb3af09ebd | ||
|
|
9fb413a126 | ||
|
|
27406c8daa | ||
|
|
f6129ebfee | ||
|
|
7f5fe8c877 | ||
|
|
07628dc05d | ||
|
|
78a29b5a87 | ||
|
|
6704f7bbb0 |
26
.gitignore
vendored
26
.gitignore
vendored
@@ -1,33 +1,11 @@
|
||||
*.log
|
||||
*.pyc
|
||||
|
||||
nominatim/*.d
|
||||
nominatim/*.o
|
||||
nominatim/nominatim
|
||||
module/nominatim.so
|
||||
module/nominatim.o
|
||||
settings/configuration.txt
|
||||
settings/download.lock
|
||||
settings/state.txt
|
||||
build
|
||||
settings/local.php
|
||||
|
||||
.deps/
|
||||
autom4te.cache/
|
||||
config.*
|
||||
configure
|
||||
Makefile
|
||||
!tests/scenes/bin/Makefile
|
||||
Makefile.in
|
||||
stamp-h1
|
||||
missing
|
||||
INSTALL
|
||||
aclocal.m4
|
||||
depcomp
|
||||
install-sh
|
||||
compile
|
||||
|
||||
data/wiki_import.sql
|
||||
data/wiki_specialphrases.sql
|
||||
data/osmosischange.osc
|
||||
|
||||
.vagrant
|
||||
.vagrant
|
||||
|
||||
34
.travis.yml
Normal file
34
.travis.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
sudo: required
|
||||
dist: xenial
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
git:
|
||||
depth: 3
|
||||
env:
|
||||
- TEST_SUITE=tests
|
||||
- TEST_SUITE=monaco
|
||||
before_install:
|
||||
- phpenv global 7.1
|
||||
install:
|
||||
- vagrant/install-on-travis-ci.sh
|
||||
before_script:
|
||||
- psql -U postgres -c "create extension postgis"
|
||||
script:
|
||||
- cd $TRAVIS_BUILD_DIR/
|
||||
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
|
||||
- cd $TRAVIS_BUILD_DIR/test/php
|
||||
- if [[ $TEST_SUITE == "tests" ]]; then /usr/bin/phpunit ./ ; fi
|
||||
- cd $TRAVIS_BUILD_DIR/test/bdd
|
||||
- # behave --format=progress3 api
|
||||
- if [[ $TEST_SUITE == "tests" ]]; then behave -DREMOVE_TEMPLATE=1 --format=progress3 db ; fi
|
||||
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 osm2pgsql ; fi
|
||||
- cd $TRAVIS_BUILD_DIR/build
|
||||
- if [[ $TEST_SUITE == "monaco" ]]; then wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf; fi
|
||||
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/setup.php --osm-file ../data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | grep -v 'ETA (seconds)'; fi
|
||||
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/specialphrases.php --wiki-import | psql -d test_api_nominatim >/dev/null; fi
|
||||
notifications:
|
||||
email: false
|
||||
22
AUTHORS
22
AUTHORS
@@ -2,18 +2,14 @@ Nominatim was written by:
|
||||
|
||||
Brian Quinion
|
||||
Sarah Hoffmann
|
||||
Frederik Ramm
|
||||
Michael Spreng
|
||||
Daniele Forsi
|
||||
mfn
|
||||
Grant Slater
|
||||
Andree Klattenhoff
|
||||
IrlJidel
|
||||
appelflap
|
||||
b3nn0
|
||||
Spin0us
|
||||
Kurt Roeckx
|
||||
Rodolphe Quiédeville
|
||||
Marc Tobias Metten
|
||||
|
||||
Reverse geocoding using Tiger data feature made possible with support from Guru Labs
|
||||
markigail
|
||||
gemo1011
|
||||
IrlJidel
|
||||
Frederik Ramm
|
||||
|
||||
and many more.
|
||||
|
||||
For a full list of contributors see
|
||||
https://github.com/openstreetmap/Nominatim/graphs/contributors
|
||||
|
||||
175
CMakeLists.txt
Normal file
175
CMakeLists.txt
Normal file
@@ -0,0 +1,175 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# CMake Config
|
||||
#
|
||||
# Nominatim
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Project version
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
project(nominatim)
|
||||
|
||||
set(NOMINATIM_VERSION_MAJOR 3)
|
||||
set(NOMINATIM_VERSION_MINOR 4)
|
||||
set(NOMINATIM_VERSION_PATCH 2)
|
||||
|
||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
||||
|
||||
add_definitions(-DNOMINATIM_VERSION="${NOMINATIM_VERSION}")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Find external dependencies
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
set(BUILD_TESTS off CACHE BOOL "Build test suite" FORCE)
|
||||
set(WITH_LUA off CACHE BOOL "Build with lua support" FORCE)
|
||||
set(ONLY_DOCS off CACHE BOOL "Build documentation only")
|
||||
|
||||
if (NOT ONLY_DOCS)
|
||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/osm2pgsql/CMakeLists.txt")
|
||||
message(FATAL_ERROR "The osm2pgsql directory is empty.\
|
||||
Did you forget to check out Nominatim recursively?\
|
||||
\nTry updating submodules with: git submodule update --init")
|
||||
endif()
|
||||
add_subdirectory(osm2pgsql)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
unset(PostgreSQL_TYPE_INCLUDE_DIR CACHE)
|
||||
set(PostgreSQL_TYPE_INCLUDE_DIR "/usr/include/")
|
||||
find_package(PostgreSQL REQUIRED)
|
||||
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
||||
link_directories(${PostgreSQL_LIBRARY_DIRS})
|
||||
|
||||
find_program(PYOSMIUM pyosmium-get-changes)
|
||||
if (NOT EXISTS "${PYOSMIUM}")
|
||||
set(PYOSMIUM_PATH "")
|
||||
message(WARNING "pyosmium-get-changes not found (required for updates)")
|
||||
else()
|
||||
set(PYOSMIUM_PATH "${PYOSMIUM}")
|
||||
message(STATUS "Using pyosmium-get-changes at ${PYOSMIUM_PATH}")
|
||||
endif()
|
||||
|
||||
|
||||
find_program(PG_CONFIG pg_config)
|
||||
execute_process(COMMAND ${PG_CONFIG} --pgxs
|
||||
OUTPUT_VARIABLE PGXS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if (NOT EXISTS "${PGXS}")
|
||||
message(FATAL_ERROR "Postgresql server package not found.")
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
find_package(BZip2 REQUIRED)
|
||||
|
||||
find_package(LibXml2 REQUIRED)
|
||||
include_directories(${LIBXML2_INCLUDE_DIR})
|
||||
|
||||
# Setting PHP binary variable as to command line (prevailing) or auto detect
|
||||
if (NOT PHP_BIN)
|
||||
find_program (PHP_BIN php)
|
||||
endif()
|
||||
# sanity check if PHP binary exists
|
||||
if (NOT EXISTS ${PHP_BIN})
|
||||
message(FATAL_ERROR "PHP binary not found. Install php or provide location with -DPHP_BIN=/path/php ")
|
||||
endif()
|
||||
message (STATUS "Using PHP binary " ${PHP_BIN})
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Setup settings and paths
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
set(WEBSITESCRIPTS
|
||||
website/deletable.php
|
||||
website/details.php
|
||||
website/hierarchy.php
|
||||
website/lookup.php
|
||||
website/polygons.php
|
||||
website/reverse.php
|
||||
website/search.php
|
||||
website/status.php
|
||||
)
|
||||
|
||||
set(CUSTOMSCRIPTS
|
||||
utils/country_languages.php
|
||||
utils/importWikipedia.php
|
||||
utils/export.php
|
||||
utils/query.php
|
||||
utils/setup.php
|
||||
utils/specialphrases.php
|
||||
utils/update.php
|
||||
utils/warm.php
|
||||
)
|
||||
|
||||
foreach (script_source ${CUSTOMSCRIPTS})
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/script.tmpl
|
||||
${PROJECT_BINARY_DIR}/${script_source})
|
||||
endforeach()
|
||||
|
||||
foreach (script_source ${WEBSITESCRIPTS})
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/website.tmpl
|
||||
${PROJECT_BINARY_DIR}/${script_source})
|
||||
endforeach()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/settings/defaults.php
|
||||
${PROJECT_BINARY_DIR}/settings/settings.php)
|
||||
|
||||
set(WEBPATHS css images js)
|
||||
|
||||
foreach (wp ${WEBPATHS})
|
||||
execute_process(
|
||||
COMMAND ln -sf ${PROJECT_SOURCE_DIR}/website/${wp} ${PROJECT_BINARY_DIR}/website/
|
||||
)
|
||||
endforeach()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (NOT ONLY_DOCS)
|
||||
include(CTest)
|
||||
|
||||
set(TEST_BDD db osm2pgsql api)
|
||||
|
||||
foreach (test ${TEST_BDD})
|
||||
add_test(NAME bdd_${test}
|
||||
COMMAND lettuce features/${test}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)
|
||||
set_tests_properties(bdd_${test}
|
||||
PROPERTIES ENVIRONMENT "NOMINATIM_DIR=${PROJECT_BINARY_DIR}")
|
||||
endforeach()
|
||||
|
||||
add_test(NAME php
|
||||
COMMAND phpunit ./
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests-php)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (NOT ONLY_DOCS)
|
||||
add_subdirectory(module)
|
||||
add_subdirectory(nominatim)
|
||||
endif()
|
||||
add_subdirectory(docs)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
102
CONTRIBUTING.md
Normal file
102
CONTRIBUTING.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Nominatim contribution guidelines
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
Bugs can be reported at https://github.com/openstreetmap/Nominatim/issues.
|
||||
Please always open a separate issue for each problem. In particular, do
|
||||
not add your bugs to closed issues. They may looks similar to you but
|
||||
often are completely different from the maintainer's point of view.
|
||||
|
||||
### When Reporting Bad Search Results...
|
||||
|
||||
Please make sure to add the following information:
|
||||
|
||||
* the URL of the query that produces the bad result
|
||||
* the result you are getting
|
||||
* the expected result, preferably a link to the OSM object you want to find,
|
||||
otherwise an address that is as precise as possible
|
||||
|
||||
To get the link to the OSM object, you can try the following:
|
||||
|
||||
* go to https://openstreetmap.org
|
||||
* zoom to the area of the map where you expect the result and
|
||||
zoom in as much as possible
|
||||
* click on the question mark on the right side of the map,
|
||||
then with the queston cursor on the map where your object is located
|
||||
* find the object of interest in the list that appears on the left side
|
||||
* click on the object and report the URL back that the browser shows
|
||||
|
||||
### When Reporting Problems with your Installation...
|
||||
|
||||
Please add the following information to your issue:
|
||||
|
||||
* hardware configuration: RAM size, CPUs, kind and size of disks
|
||||
* Operating system (also mention if you are running on a cloud service)
|
||||
* Postgres and Postgis version
|
||||
* list of settings you changed in your Postgres configuration
|
||||
* Nominatim version (release version or,
|
||||
if you run from the git repo, the output of `git rev-parse HEAD`)
|
||||
* (if applicable) exact command line of the command that was causing the issue
|
||||
|
||||
|
||||
## Workflow for Pull Requests
|
||||
|
||||
We love to get pull requests from you. We operate the "Fork & Pull" model
|
||||
explained at
|
||||
|
||||
https://help.github.com/articles/using-pull-requests
|
||||
|
||||
You should fork the project into your own repo, create a topic branch
|
||||
there and then make one or more pull requests back to the openstreetmap repository.
|
||||
Your pull requests will then be reviewed and discussed. Please be aware
|
||||
that you are responsible for your pull requests. You should be prepared
|
||||
to get change requests because as the maintainers we have to make sure
|
||||
that your contribution fits well with the rest of the code. Please make
|
||||
sure that you have time to react to these comments and amend the code or
|
||||
engage in a conversion. Do not expect that others will pick up your code,
|
||||
it will almost never happen.
|
||||
|
||||
Please open a separate pull request for each issue you want to address.
|
||||
Don't mix multiple changes. In particular, don't mix style cleanups with
|
||||
feature pull requests. If you plan to make larger changes, please open
|
||||
an issue first or comment on the appropriate issue already existing so
|
||||
that duplicate work can be avoided.
|
||||
|
||||
## Coding style
|
||||
|
||||
Nominatim historically hasn't followed a particular coding style but we
|
||||
are in process of consolidating the style. The following rules apply:
|
||||
|
||||
* Python code uses the official Python style
|
||||
* indention
|
||||
* SQL use 2 spaces
|
||||
* all other file types use 4 spaces
|
||||
* [BSD style](https://en.wikipedia.org/wiki/Indent_style#Allman_style) for braces
|
||||
* spaces
|
||||
* spaces before and after equal signs and operators
|
||||
* no trailing spaces
|
||||
* no spaces after opening and before closing bracket
|
||||
* leave out space between a function name and bracket
|
||||
but add one between control statement(if, while, etc.) and bracket
|
||||
* for PHP variables use CamelCase with a prefixing letter indicating the type
|
||||
(i - integer, f - float, a - array, s - string, o - object)
|
||||
|
||||
The coding style is enforced with PHPCS and can be tested with:
|
||||
|
||||
```
|
||||
phpcs --report-width=120 --colors .
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Before submitting a pull request make sure that the following tests pass:
|
||||
|
||||
```
|
||||
cd test/bdd
|
||||
behave -DBUILDDIR=<builddir> db osm2pgsql
|
||||
```
|
||||
|
||||
```
|
||||
cd test/php
|
||||
phpunit ./
|
||||
```
|
||||
14
COPYING
14
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
|
||||
133
ChangeLog
133
ChangeLog
@@ -1,3 +1,136 @@
|
||||
3.4.2
|
||||
* security fix: fix possible SQL injection via details API
|
||||
|
||||
3.4.1
|
||||
* update osm2pgsql
|
||||
* move deletion to copy thread (fixes deadlock in updates)
|
||||
* fix filtering where valid address objects got dropped
|
||||
* fix typo in import styles
|
||||
|
||||
3.4.0
|
||||
|
||||
* increase required version for PostgreSQL(9.3), PostGIS(2.2) and PHP(7.0)
|
||||
* better error reporting for out-of-memory errors
|
||||
* exclude postcode ranges separated by colon from centre point calculation
|
||||
* update osm2pgsql, better handling of imports without flatnode file
|
||||
* switch to more efficient algorithm for word set computation
|
||||
* use only boundries for country and state parts of addresses
|
||||
* improve updates of addresses with housenumbers and interpolations
|
||||
* remove country from place_addressline table and use country_code instead
|
||||
* optimise indexes on search_name partition tables
|
||||
* improve searching of attached streets for large objects like airports
|
||||
* drop support for python 2
|
||||
* new scripts for importing Wikidata for importance
|
||||
* create and drop indexes concurrently to not clash with auto vacuum
|
||||
* various documentation improvements
|
||||
|
||||
|
||||
3.3.0
|
||||
|
||||
* zoom 17 in reverse now zooms in on minor streets
|
||||
* fix use of postcode relations in address
|
||||
* support for housenumber 0 on interpolations
|
||||
* replace database abstraction DB with PDO and switch to using exceptions
|
||||
* exclude line features at rank 30 from reverse geocoding
|
||||
* remove self-reference and country from place_addressline
|
||||
* make json output more readable (less escaping)
|
||||
* update conversion scripts for postcodes
|
||||
* scripts in utils/ are no longer executable (always use scripts in build dir)
|
||||
* remove Natural Earth country fallback (OSM is complete enough)
|
||||
* make rank assignments configurable
|
||||
* allow accept languages with underscore
|
||||
* new reverse-only import mode (without search index table)
|
||||
* rely on boundaries only for states and countries
|
||||
* update osm2pgsql, now using a configurable style
|
||||
* provide multiple import styles
|
||||
* improve search when house number and postcodes are dropped
|
||||
* overhaul of setup code
|
||||
* add support for PHPUnit 6
|
||||
* update test database
|
||||
* various documentation improvements
|
||||
|
||||
3.2.0
|
||||
|
||||
* complete rewrite of reverse search algorithm
|
||||
* add new geojson and geocodejson output formats
|
||||
* add simple export script to exprot addresses to CSV
|
||||
* remove is_in terms from address computation
|
||||
* remove unused search_name_country tables
|
||||
* various smaller fixes to query parsing
|
||||
* convert Tokens and token types to class types
|
||||
* correctly handle update when boundary object type is changed
|
||||
* improve debug output for /search endpoint
|
||||
* update to latest osm2pgsql and leaflet.js
|
||||
* overhaul of /details endpoint:
|
||||
* new class parameter when using osmtype/osmid parameters
|
||||
* permalink to instance-independent osmtype/osmid parameter format
|
||||
* new json output format
|
||||
* update CentOS vagrant machine to use SELinux
|
||||
* add vagrant scripts for Ubuntu 18.04
|
||||
* fix build process for BSD
|
||||
* enable running the database on a different host than the setup scripts
|
||||
* allow to configure use of custom PHP binaries (PHP_BIN)
|
||||
* extensive coding style improvements to PHP code
|
||||
* more PHP unit tests for new classes
|
||||
* increase coverage for API tests
|
||||
* add documentation for API
|
||||
|
||||
3.1.0
|
||||
|
||||
* rework postcode handling and introduce location_postcode table
|
||||
* make setup less verbose and print a summary in the end
|
||||
* setup: error out when web site user does not exist
|
||||
* add more API tests to complete code coverage
|
||||
* reinstate key-value amenity search (special term [key=value])
|
||||
* fix detection of coordinates in query
|
||||
* various smaller tweaks to ranking of search interpretations
|
||||
* complete overhaul of PHP frontend code using OOP
|
||||
* add address rank to details page
|
||||
* update Tiger scripts for 2017 data and clean up unused code
|
||||
* various bug fixes and improvements to UI
|
||||
* improve reverse geocoding performance close to coasts
|
||||
* more PHP style cleanup (quoting)
|
||||
* allow unnamed road in reverse geocoding to avoid too far off results
|
||||
* add function to recalculate counts for full-word search term
|
||||
* add function to check if new updates are available
|
||||
* update documentation and switch to mkdocs for generating HTML
|
||||
|
||||
3.0.1
|
||||
|
||||
* fix bug in geometry building algorithm in osm2pgsql
|
||||
* fix typos in build instructions
|
||||
|
||||
3.0.0
|
||||
|
||||
* move to cmake build system
|
||||
* various fixes to HTML output
|
||||
* reverse endpoint now can return geometries
|
||||
* add support for PHP7
|
||||
* move to on-the-fly computation of interpolations
|
||||
* improve handling of linked places (updating)
|
||||
* improve test framework:
|
||||
* replace lettuce with behave
|
||||
* use smaller database for API tests
|
||||
* drop support for postgres < 9.1, postgis < 2.0 and PHP < 5.4
|
||||
* make external data use optional (useful for imports without US)
|
||||
* detect postgres and postgis versions automatically
|
||||
* clean up query logging and remove unused tables
|
||||
* move installation documentation into this repo
|
||||
* add self-documenting vagrant scripts
|
||||
* remove --create-website, recommend to use website directory in build
|
||||
* add accessor functions for URL parameters and improve erro checking
|
||||
* remove IP blocking and rate-limiting code
|
||||
* enable CI via travis
|
||||
* reformatting for more consistent coding style
|
||||
* make country search term creation part of setup
|
||||
* update country names and country grid
|
||||
* handle roads that cross boundaries better
|
||||
* keep full information on address tags
|
||||
* update to refactored osm2pgsql which use libosmium based types
|
||||
* switch from osmosis to pyosmium for updates
|
||||
* be more strict when matching against special search terms
|
||||
* handle postcode entries with mutliple values correctly
|
||||
|
||||
2.5
|
||||
|
||||
* reverse geocoding includes looking up housenumbers from Tiger data
|
||||
|
||||
27
Makefile.am
27
Makefile.am
@@ -1,27 +0,0 @@
|
||||
ACLOCAL_AMFLAGS = -I osm2pgsql/m4
|
||||
AUTOMAKE_OPTIONS = -Wno-portability
|
||||
|
||||
SUBDIRS = osm2pgsql module nominatim
|
||||
|
||||
NOMINATIM_SERVER ?= $(shell echo a | php -F lib/init.php -E 'echo CONST_Website_BaseURL."\n";')
|
||||
NOMINATIM_DATABASE ?= $(shell echo a | php -F lib/init.php -E 'echo DB::parseDSN(CONST_Database_DSN)["database"];')
|
||||
|
||||
install:
|
||||
@echo Nominatim needs to be executed directly from this directory. No install necessary.
|
||||
|
||||
test:
|
||||
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only
|
||||
|
||||
test-fast:
|
||||
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} NOMINATIM_REUSE_TEMPLATE=1 lettuce -t -Fail -t -poldi-only
|
||||
|
||||
test-db:
|
||||
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only features/db
|
||||
|
||||
test-db-fast:
|
||||
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} NOMINATIM_REUSE_TEMPLATE=1 lettuce -t -Fail -t -poldi-only features/db
|
||||
|
||||
test-api:
|
||||
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only features/api
|
||||
|
||||
.PHONY: test test-fast test-db test-db-fast test-api
|
||||
59
README
59
README
@@ -1,59 +0,0 @@
|
||||
Nominatim
|
||||
=========
|
||||
|
||||
Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
|
||||
by name and address (geocoding) and to generate synthetic addresses of
|
||||
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
||||
at http://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
||||
sources for the Search box on the OpenStreetMap home page and powers the search
|
||||
on the MapQuest Open Initiative websites.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
More information about Nominatim, including usage and installation instructions,
|
||||
can be found in the OSM wiki at:
|
||||
|
||||
http://wiki.openstreetmap.org/wiki/Nominatim
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The following instructions is a quick guide to installation. A more detailed guide
|
||||
how to set up your own instance of Nominatim can be found in the wiki:
|
||||
|
||||
http://wiki.openstreetmap.org/wiki/Nominatim/Installation
|
||||
|
||||
Note that this repository contains a submodule called osm2pgsql. Make sure it
|
||||
is cloned as well by running `git submodule update --init`.
|
||||
|
||||
Installation steps:
|
||||
|
||||
0. If checking out from git run:
|
||||
|
||||
./autogen.sh
|
||||
|
||||
1. Compile Nominatim:
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
2. Get OSM data and import:
|
||||
|
||||
./utils/setup.php --osm-file <your planet file> --all
|
||||
|
||||
3. Point your webserver to the ./website directory.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
The source code is available under a GPLv2 license.
|
||||
|
||||
Contact and Bugreports
|
||||
======================
|
||||
|
||||
For questions you can join the geocoding mailinglist, see
|
||||
http://lists.openstreetmap.org/listinfo/geocoding
|
||||
|
||||
Bugs may be reported on the github project site:
|
||||
https://github.com/twain47/Nominatim
|
||||
63
README.md
Normal file
63
README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
[](https://travis-ci.org/openstreetmap/Nominatim)
|
||||
|
||||
Nominatim
|
||||
=========
|
||||
|
||||
Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
|
||||
by name and address (geocoding) and to generate synthetic addresses of
|
||||
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
||||
at https://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
||||
sources for the Search box on the OpenStreetMap home page.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The documentation of the latest development version is in the
|
||||
`docs/` subdirectory. A HTML version can be found at
|
||||
https://nominatim.org/release-docs/develop/ .
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The latest stable release can be downloaded from https://nominatim.org.
|
||||
There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/admin/Installation).
|
||||
|
||||
Detailed installation instructions for the development version can be
|
||||
found at [nominatim.org](https://nominatim.org/release-docs/develop/admin/Installation)
|
||||
as well.
|
||||
|
||||
A quick summary of the necessary steps:
|
||||
|
||||
1. Compile Nominatim:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
2. Get OSM data and import:
|
||||
|
||||
./build/utils/setup.php --osm-file <your planet file> --all
|
||||
|
||||
3. Point your webserver to the ./build/website directory.
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
The source code is available under a GPLv2 license.
|
||||
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Contributions are welcome. For details see [contribution guide](CONTRIBUTING.md).
|
||||
|
||||
Both bug reports and pull requests are welcome.
|
||||
|
||||
|
||||
Mailing list
|
||||
============
|
||||
|
||||
For questions you can join the geocoding mailing list, see
|
||||
https://lists.openstreetmap.org/listinfo/geocoding
|
||||
92
VAGRANT.md
92
VAGRANT.md
@@ -1,11 +1,11 @@
|
||||
# Install Nominatim in a virtual machine for development and testing
|
||||
|
||||
This document describes how you can install Nominatim inside a Ubuntu 14
|
||||
This document describes how you can install Nominatim inside a Ubuntu 16
|
||||
virtual machine on your desktop/laptop (host machine). The goal is to give
|
||||
you a development environment to easily edit code and run the test suite
|
||||
without affecting the rest of your system.
|
||||
|
||||
The installation can run largely unsupervised. You should expect 1-2h from
|
||||
The installation can run largely unsupervised. You should expect 1h from
|
||||
start to finish depending on how fast your computer and download speed
|
||||
is.
|
||||
|
||||
@@ -17,9 +17,9 @@ is.
|
||||
|
||||
3. Nominatim
|
||||
|
||||
git clone --recursive https://github.com/twain47/Nominatim.git
|
||||
git clone --recursive https://github.com/openstreetmap/Nominatim.git
|
||||
|
||||
If you haven't used `--recursive`, then you can load the submodules using
|
||||
If you forgot `--recursive`, it you can later load the submodules using
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
@@ -37,24 +37,19 @@ is.
|
||||
vagrant ssh ubuntu
|
||||
|
||||
3. Import a small country (Monaco)
|
||||
|
||||
You need to give the virtual machine more memory (2GB) for an import,
|
||||
see `Vagrantfile`. Otherwise 1GB is enough.
|
||||
|
||||
See the FAQ how to skip this step and point Nominatim to an existing database.
|
||||
|
||||
```
|
||||
# inside the virtual machine:
|
||||
cd Nominatim
|
||||
wget --no-verbose --output-document=data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
||||
./utils/setup.php --osm-file data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | tee monaco.$$.log
|
||||
./utils/specialphrases.php --countries > data/specialphrases_countries.sql
|
||||
psql -d nominatim -f data/specialphrases_countries.sql
|
||||
```
|
||||
```
|
||||
# inside the virtual machine:
|
||||
cd build
|
||||
wget --no-verbose --output-document=/tmp/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
||||
./utils/setup.php --osm-file /tmp/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | tee monaco.$$.log
|
||||
```
|
||||
|
||||
To repeat an import you'd need to delete the database first
|
||||
To repeat an import you'd need to delete the database first
|
||||
|
||||
dropdb -if-exists nominatim
|
||||
dropdb --if-exists nominatim
|
||||
|
||||
|
||||
|
||||
@@ -66,45 +61,62 @@ see Nominatim in action on [locahost:8089](http://localhost:8089/nominatim/).
|
||||
You edit code on your host machine in any editor you like. There is no need to
|
||||
restart any software: just refresh your browser window.
|
||||
|
||||
Note that the webserver uses files from the /build directory. If you change
|
||||
files in Nominatim/website or Nominatim/utils for example you first need to
|
||||
copy them into the /build directory by running the `cmake` step from the
|
||||
installation.
|
||||
|
||||
PHP errors are written to `/var/log/apache2/error.log`.
|
||||
|
||||
With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when
|
||||
you either add `&debug=1` to the URL (preferred) or set
|
||||
`@define('CONST_Debug', true);` in `settings/local.php`.
|
||||
|
||||
In the Python BDD test you can use `logger.info()` for temporary debug
|
||||
statements.
|
||||
|
||||
|
||||
|
||||
## Running unit tests
|
||||
|
||||
cd ~/Nominatim/tests/php
|
||||
phpunit ./
|
||||
|
||||
|
||||
## Running PHP code style tests
|
||||
|
||||
cd ~/Nominatim
|
||||
phpcs --colors .
|
||||
|
||||
|
||||
## Running functional tests
|
||||
|
||||
Tests in `/features/db` and `/features/osm2pgsql` have to pass 100%. Other
|
||||
Tests in `test/bdd/db` and `test/bdd/osm2pgsql` have to pass 100%. Other
|
||||
tests might require full planet-wide data. Sadly even if you have your own
|
||||
planet-wide data there will be enough differences to the openstreetmap.org
|
||||
installation to cause false positives in the other tests (see FAQ).
|
||||
|
||||
To run the full test suite
|
||||
|
||||
cd ~/Nominatim/tests
|
||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce features
|
||||
cd ~/Nominatim/test/bdd
|
||||
behave -DBUILDDIR=/home/vagrant/build/ db osm2pgsql
|
||||
|
||||
To run a single file
|
||||
|
||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce features/api/reverse.feature
|
||||
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature
|
||||
|
||||
Or a single test by line number
|
||||
|
||||
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature:34
|
||||
|
||||
To run specific tests you can add tags just before the `Scenario line`, e.g.
|
||||
To run specific groups of tests you can add tags just before the `Scenario line`, e.g.
|
||||
|
||||
@bug-34
|
||||
Scenario: address lookup for non-existing or invalid node, way, relation
|
||||
|
||||
and then
|
||||
|
||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce -t bug-34
|
||||
|
||||
|
||||
## Running unit tests
|
||||
|
||||
cd ~/Nominatim/tests-php
|
||||
phpunit ./
|
||||
behave -DBUILDDIR=/home/vagrant/build/ --tags @bug-34
|
||||
|
||||
|
||||
|
||||
@@ -129,19 +141,20 @@ No. Long running Nominatim installations will differ once new import features (o
|
||||
bug fixes) get added since those usually only get applied to new/changed data.
|
||||
|
||||
Also this document skips the optional Wikipedia data import which affects ranking
|
||||
of search results. See [Nominatim installation](http://wiki.openstreetmap.org/wiki/Nominatim/Installation) for details.
|
||||
of search results. See [Nominatim installation](http://nominatim.org/release-docs/latest/Installation) for details.
|
||||
|
||||
##### Why Ubuntu and CentOS, can I test CentOS/CoreOS/FreeBSD?
|
||||
##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD?
|
||||
|
||||
There is a Vagrant script for CentOS available. Simply start your box
|
||||
with `vagrant up centos` and then log in with `vagrant ssh centos`.
|
||||
In general Nominatim will also run in the other environments. The installation steps
|
||||
There is a Vagrant script for CentOS available, but the Nominatim directory
|
||||
isn't symlinked/mounted to the host which makes development trickier. We used
|
||||
it mainly for debugging installation with SELinux.
|
||||
|
||||
In general Nominatim will run in the other environments. The installation steps
|
||||
are slightly different, e.g. the name of the package manager, Apache2 package
|
||||
name, location of files. We chose Ubuntu because that is closest to the
|
||||
nominatim.openstreetmap.org production environment.
|
||||
|
||||
You can configure/download other Vagrant boxes from [vagrantbox.es](http://www.vagrantbox.es/).
|
||||
|
||||
You can configure/download other Vagrant boxes from [https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search).
|
||||
|
||||
##### How can I connect to an existing database?
|
||||
|
||||
@@ -149,7 +162,7 @@ Let's say you have a Postgres database named `nominatim_it` on server `your-serv
|
||||
|
||||
pgsql://postgres@your-server.com:5432/nominatim_it
|
||||
|
||||
No data import necessary, no restarting necessary.
|
||||
No data import necessary or restarting necessary.
|
||||
|
||||
If the Postgres installation is behind a firewall, you can try
|
||||
|
||||
@@ -158,14 +171,15 @@ If the Postgres installation is behind a firewall, you can try
|
||||
inside the virtual machine. It will map the port to `localhost:9999` and then
|
||||
you edit `settings/local.php` with
|
||||
|
||||
pgsql://postgres@localhost:9999/nominatim_it
|
||||
@define('CONST_Database_DSN', 'pgsql:host=localhost;port=9999;user=postgres;dbname=nominatim_it');
|
||||
|
||||
To access postgres directly remember to specify the hostname, e.g. `psql --host localhost --port 9999 nominatim_it`
|
||||
|
||||
|
||||
##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"?
|
||||
|
||||
Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws) or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean).
|
||||
Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws)
|
||||
or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean).
|
||||
|
||||
|
||||
|
||||
|
||||
81
Vagrantfile
vendored
81
Vagrantfile
vendored
@@ -3,45 +3,68 @@
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
# Apache webserver
|
||||
config.vm.network "forwarded_port", guest: 8089, host: 8089
|
||||
config.vm.network "forwarded_port", guest: 80, host: 8089
|
||||
|
||||
# If true, then any SSH connections made will enable agent forwarding.
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
config.vm.synced_folder ".", "/home/vagrant/Nominatim"
|
||||
|
||||
config.vm.define "ubuntu" do |sub|
|
||||
sub.vm.box = "ubuntu/trusty64"
|
||||
sub.vm.provision :shell, :path => "vagrant/ubuntu-trusty-provision.sh"
|
||||
checkout = "yes"
|
||||
if ENV['CHECKOUT'] != 'y' then
|
||||
config.vm.synced_folder ".", "/home/vagrant/Nominatim"
|
||||
checkout = "no"
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu", primary: true do |sub|
|
||||
sub.vm.box = "bento/ubuntu-18.04"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu18nginx" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-18.04"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-18-nginx.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu16" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-16.04"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-16.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "travis" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-14.04"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/install-on-travis-ci.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "centos" do |sub|
|
||||
sub.vm.box = "bento/centos-7.2"
|
||||
sub.vm.provision :shell, :path => "vagrant/centos-7-provision.sh"
|
||||
sub.vm.box = "centos/7"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Centos-7.sh"
|
||||
s.privileged = false
|
||||
s.args = "yes"
|
||||
end
|
||||
sub.vm.synced_folder ".", "/home/vagrant/Nominatim", disabled: true
|
||||
sub.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
end
|
||||
|
||||
# configure shared package cache if possible
|
||||
#if Vagrant.has_plugin?("vagrant-cachier")
|
||||
# config.cache.enable :apt
|
||||
# config.cache.scope = :box
|
||||
#end
|
||||
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.gui = false
|
||||
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||
vb.memory = 2048
|
||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"]
|
||||
end
|
||||
|
||||
|
||||
# config.vm.provider :digital_ocean do |provider, override|
|
||||
# override.ssh.private_key_path = '~/.ssh/id_rsa'
|
||||
# override.vm.box = 'digital_ocean'
|
||||
# override.vm.box_url = "https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box"
|
||||
|
||||
# provider.token = ''
|
||||
# # provider.token = 'YOUR TOKEN'
|
||||
# provider.image = 'ubuntu-14-04-x64'
|
||||
# provider.region = 'nyc2'
|
||||
# provider.size = '512mb'
|
||||
# end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
autoreconf -vfi
|
||||
4
cmake/script.tmpl
Executable file
4
cmake/script.tmpl
Executable file
@@ -0,0 +1,4 @@
|
||||
#!@PHP_BIN@ -Cq
|
||||
<?php
|
||||
require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
|
||||
require_once(CONST_BasePath.'/@script_source@');
|
||||
3
cmake/website.tmpl
Executable file
3
cmake/website.tmpl
Executable file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
|
||||
require_once(CONST_BasePath.'/@script_source@');
|
||||
67
configure.ac
67
configure.ac
@@ -1,67 +0,0 @@
|
||||
AC_INIT(Nominatim,2.5)
|
||||
if git rev-parse HEAD 2>/dev/null >/dev/null; then
|
||||
AC_SUBST([PACKAGE_VERSION], [$PACKAGE_VERSION-git-`git rev-parse --short HEAD`])
|
||||
fi
|
||||
|
||||
dnl Required autoconf version
|
||||
AC_PREREQ(2.61)
|
||||
|
||||
AM_INIT_AUTOMAKE([1.9.6 dist-bzip2 std-options check-news])
|
||||
|
||||
dnl Additional macro definitions are in here
|
||||
m4_include([m4/ax_lib_postgresql_svr.m4])
|
||||
AC_CONFIG_MACRO_DIR([osm2pgsql/m4])
|
||||
|
||||
dnl Generate configuration header file
|
||||
AC_CONFIG_HEADER(nominatim/config.h)
|
||||
|
||||
|
||||
dnl Find C compiler
|
||||
AC_PROG_CC
|
||||
|
||||
dnl Find C++ compiler
|
||||
AC_PROG_CXX
|
||||
|
||||
dnl pthread
|
||||
AX_PTHREAD([], [AC_MSG_ERROR([pthread library required])])
|
||||
|
||||
dnl Check for Geos library
|
||||
AX_LIB_GEOS
|
||||
if test "x$GEOS_VERSION" = "x"
|
||||
then
|
||||
AC_MSG_ERROR([required library not found]);
|
||||
fi
|
||||
|
||||
dnl Check for Proj library
|
||||
AX_LIB_PROJ
|
||||
if test "$HAVE_PROJ" = "no"
|
||||
then
|
||||
AC_MSG_ERROR([required library not found]);
|
||||
fi
|
||||
|
||||
|
||||
dnl Check for PostgresSQL client library
|
||||
AX_LIB_POSTGRESQL(9.0)
|
||||
if test "x$POSTGRESQL_VERSION" = "x"
|
||||
then
|
||||
AC_MSG_ERROR([postgresql client library not found])
|
||||
fi
|
||||
AX_LIB_POSTGRESQL_SVR(9.0)
|
||||
if test ! -f "$POSTGRESQL_PGXS"
|
||||
then
|
||||
AC_MSG_ERROR([postgresql server development library not found])
|
||||
fi
|
||||
|
||||
dnl Check for bzip2 library
|
||||
AX_LIB_BZIP2
|
||||
if test "$HAVE_BZIP2" = "no"
|
||||
then
|
||||
AC_MSG_ERROR([required library not found]);
|
||||
fi
|
||||
|
||||
dnl Check for libxml2 library
|
||||
AM_PATH_XML2
|
||||
|
||||
AC_CONFIG_SUBDIRS([osm2pgsql])
|
||||
|
||||
AC_OUTPUT(Makefile nominatim/Makefile module/Makefile)
|
||||
@@ -1,36 +0,0 @@
|
||||
# This file includes a small subset of OpenLayers code, designed to be
|
||||
# integrated into another application. It includes only the Layer types
|
||||
# neccesary to create tiled or untiled WMS, and does not include any Controls.
|
||||
# This is the result of what was at the time called "Webmap.js" at the FOSS4G
|
||||
# Web Mapping BOF.
|
||||
|
||||
[first]
|
||||
|
||||
[last]
|
||||
|
||||
[include]
|
||||
OpenLayers/Map.js
|
||||
OpenLayers/Kinetic.js
|
||||
OpenLayers/Geometry/MultiLineString.js
|
||||
OpenLayers/Geometry/MultiPolygon.js
|
||||
OpenLayers/Format/WKT.js
|
||||
OpenLayers/Layer/OSM.js
|
||||
OpenLayers/Layer/Vector.js
|
||||
OpenLayers/Layer/SphericalMercator.js
|
||||
OpenLayers/Control/Attribution.js
|
||||
OpenLayers/Control/KeyboardDefaults.js
|
||||
OpenLayers/Control/Navigation.js
|
||||
OpenLayers/Control/MousePosition.js
|
||||
OpenLayers/Control/PanZoomBar.js
|
||||
OpenLayers/Control/Permalink.js
|
||||
OpenLayers/Control/TouchNavigation.js
|
||||
OpenLayers/Style.js
|
||||
OpenLayers/Protocol/HTTP.js
|
||||
OpenLayers/Projection.js
|
||||
OpenLayers/Renderer/SVG.js
|
||||
OpenLayers/Renderer/VML.js
|
||||
OpenLayers/Renderer/Canvas.js
|
||||
|
||||
[exclude]
|
||||
|
||||
|
||||
77
data-sources/country-grid/README.md
Normal file
77
data-sources/country-grid/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Fallback Country Boundaries
|
||||
|
||||
Each place is assigned a `country_code` and partition. Partitions derive from `country_code`.
|
||||
|
||||
Nominatim imports two pre-generated files
|
||||
|
||||
* `data/country_name.sql` (country code, name, default language, partition)
|
||||
* `data/country_osm_grid.sql` (country code, geometry)
|
||||
|
||||
before creating places in the database. This helps with fast lookups and missing data (e.g. if the data the user wants to import doesn't contain any country places).
|
||||
|
||||
The number of countries in the world can change (South Sudan created 2011, Germany reunification), so can their boundaries. This document explain how the pre-generated files can be updated.
|
||||
|
||||
|
||||
|
||||
## Country code
|
||||
|
||||
Each place is assigned a two letter country_code based on its location, e.g. `gb` for Great Britain. Or `NULL` if no suitable country is found (usually it's in open water then).
|
||||
|
||||
In `sql/functions.sql: get_country_code(geometry)` the place's center is checked against
|
||||
|
||||
1. country places already imported from the user's data file. Places are imported by rank low-to-high. Lowest rank 2 is countries so most places should be matched. Still the data file might be incomplete.
|
||||
2. if unmatched: OSM grid boundaries
|
||||
3. if still unmatched: OSM grid boundaries, but allow a small distance
|
||||
|
||||
|
||||
|
||||
## Partitions
|
||||
|
||||
Each place is assigned partition, which is a number 0..250. 0 is fallback/other.
|
||||
|
||||
During place indexing (`sql/functions.sql: placex_insert()`) a place is assigned the partition based on its country code (`sql/functions.sql: get_partition(country_code)`). It checks in the `country_name` table.
|
||||
|
||||
Most countries have their own partition, some share a partition. Thus partition counts vary greatly.
|
||||
|
||||
Several database tables are split by partition to allow queries to run against less indices and improve caching.
|
||||
|
||||
* `location_area_large_<partition>`
|
||||
* `search_name_<partition>`
|
||||
* `location_road_<partition>`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Data files
|
||||
|
||||
### data/country_name.sql
|
||||
|
||||
Export from existing database table plus manual changes. `country_default_language_code` most taken from [https://wiki.openstreetmap.org/wiki/Nominatim/Country_Codes](), see `utils/country_languages.php`.
|
||||
|
||||
|
||||
|
||||
### data/country_osm_grid.sql
|
||||
|
||||
`country_grid.sql` merges territories by country. Then uses `function.sql: quad_split_geometry` to split each country into multiple [Quadtree](https://en.wikipedia.org/wiki/Quadtree) polygons for faster point-in-polygon lookups.
|
||||
|
||||
To visualize one country as geojson feature collection, e.g. for loading into [geojson.io](http://geojson.io/):
|
||||
|
||||
```
|
||||
-- http://www.postgresonline.com/journal/archives/267-Creating-GeoJSON-Feature-Collections-with-JSON-and-PostGIS-functions.html
|
||||
|
||||
SELECT row_to_json(fc)
|
||||
FROM (
|
||||
SELECT 'FeatureCollection' As type, array_to_json(array_agg(f)) As features
|
||||
FROM (
|
||||
SELECT 'Feature' As type,
|
||||
ST_AsGeoJSON(lg.geometry)::json As geometry,
|
||||
row_to_json((country_code, area)) As properties
|
||||
FROM country_osm_grid As lg where country_code='mx'
|
||||
) As f
|
||||
) As fc;
|
||||
```
|
||||
|
||||
`cat /tmp/query.sql | psql -At nominatim > /tmp/mexico.quad.geojson`
|
||||
|
||||

|
||||
BIN
data-sources/country-grid/mexico.quad.png
Normal file
BIN
data-sources/country-grid/mexico.quad.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 320 KiB |
56
data-sources/gb-postcodes/README.md
Normal file
56
data-sources/gb-postcodes/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# GB Postcodes
|
||||
|
||||
|
||||
The server [importing instructions](https://www.nominatim.org/release-docs/latest/admin/Import-and-Update/) allow optionally download [`gb_postcode_data.sql.gz`](https://www.nominatim.org/data/gb_postcode_data.sql.gz). This document explains how the file got created.
|
||||
|
||||
## GB vs UK
|
||||
|
||||
GB (Great Britain) is more correct as the Ordnance Survey dataset doesn't contain postcodes from Northern Ireland.
|
||||
|
||||
## Importing separately after the initial import
|
||||
|
||||
If you forgot to download the file, or have a new version, you can import it separately:
|
||||
|
||||
1. Import the downloaded `gb_postcode_data.sql.gz` file.
|
||||
|
||||
2. Run the SQL query `SELECT count(getorcreate_postcode_id(postcode)) FROM gb_postcode;`. This will update the search index.
|
||||
|
||||
3. Run `utils/setup.php --calculate-postcodes` from the build directory. This will copy data form the `gb_postcode` table to the `location_postcodes` table.
|
||||
|
||||
|
||||
|
||||
## Converting Code-Point Open data
|
||||
|
||||
1. Download from [Code-Point® Open](https://www.ordnancesurvey.co.uk/business-and-government/products/code-point-open.html). It requires an email address where a download link will be send to.
|
||||
|
||||
2. `unzip codepo_gb.zip`
|
||||
|
||||
Unpacked you'll see a directory of CSV files.
|
||||
|
||||
$ more codepo_gb/Data/CSV/n.csv
|
||||
"N1 0AA",10,530626,183961,"E92000001","E19000003","E18000007","","E09000019","E05000368"
|
||||
"N1 0AB",10,530559,183978,"E92000001","E19000003","E18000007","","E09000019","E05000368"
|
||||
|
||||
The coordinates are "Northings" and "Eastings" in [OSGB 1936](http://epsg.io/1314) projection. They can be projected to WGS84 like this
|
||||
|
||||
SELECT ST_AsText(ST_Transform(ST_SetSRID('POINT(530626 183961)'::geometry,27700), 4326));
|
||||
POINT(-0.117872733220225 51.5394424719303)
|
||||
|
||||
[-0.117872733220225 51.5394424719303 on OSM map](https://www.openstreetmap.org/?mlon=-0.117872733220225&mlat=51.5394424719303&zoom=16)
|
||||
|
||||
|
||||
|
||||
3. Create database, import CSV files, add geometry column, dump into file
|
||||
|
||||
DBNAME=create_gb_postcode_file
|
||||
createdb $DBNAME
|
||||
echo 'CREATE EXTENSION postgis' | psql $DBNAME
|
||||
|
||||
cat data/gb_postcode_table.sql | psql $DBNAME
|
||||
cat codepo_gb/Data/CSV/*.csv | ./data-sources/gb-postcodes/convert_codepoint.php | psql $DBNAME
|
||||
cat codepo_gb/Doc/licence.txt | iconv -f iso-8859-1 -t utf-8 | dos2unix | sed 's/^/-- /g' > gb_postcode_data.sql
|
||||
pg_dump -a -t gb_postcode $DBNAME | grep -v '^--' >> gb_postcode_data.sql
|
||||
|
||||
gzip -9 -f gb_postcode_data.sql
|
||||
ls -lah gb_postcode_data.*
|
||||
# dropdb $DBNAME
|
||||
37
data-sources/gb-postcodes/convert_codepoint.php
Executable file
37
data-sources/gb-postcodes/convert_codepoint.php
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
echo <<< EOT
|
||||
|
||||
ALTER TABLE gb_postcode ADD COLUMN easting bigint;
|
||||
ALTER TABLE gb_postcode ADD COLUMN northing bigint;
|
||||
|
||||
TRUNCATE gb_postcode;
|
||||
|
||||
COPY gb_postcode (id, postcode, easting, northing) FROM stdin;
|
||||
|
||||
EOT;
|
||||
|
||||
$iCounter = 0;
|
||||
while ($sLine = fgets(STDIN)) {
|
||||
$aColumns = str_getcsv($sLine);
|
||||
|
||||
// insert space before the third last position
|
||||
// https://stackoverflow.com/a/9144834
|
||||
$postcode = $aColumns[0];
|
||||
$postcode = preg_replace('/\s*(...)$/', ' $1', $postcode);
|
||||
|
||||
echo join("\t", array($iCounter, $postcode, $aColumns[2], $aColumns[3]))."\n";
|
||||
|
||||
$iCounter = $iCounter + 1;
|
||||
}
|
||||
|
||||
echo <<< EOT
|
||||
\.
|
||||
|
||||
UPDATE gb_postcode SET geometry=ST_Transform(ST_SetSRID(CONCAT('POINT(', easting, ' ', northing, ')')::geometry, 27700), 4326);
|
||||
|
||||
ALTER TABLE gb_postcode DROP COLUMN easting;
|
||||
ALTER TABLE gb_postcode DROP COLUMN northing;
|
||||
|
||||
EOT;
|
||||
26
data-sources/us-tiger/README.md
Normal file
26
data-sources/us-tiger/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# US TIGER address data
|
||||
|
||||
Convert [TIGER](https://www.census.gov/geo/maps-data/data/tiger.html)/Line dataset of the US Census Bureau to SQL files which can be imported by Nominatim. The created tables in the Nominatim database are separate from OpenStreetMap tables and get queried at search time separately.
|
||||
|
||||
The dataset gets updated once per year. Downloading is prone to be slow (can take a full day) and converting them can take hours as well.
|
||||
|
||||
Replace '2019' with the current year throughout.
|
||||
|
||||
1. Install the GDAL library and python bindings and the unzip tool
|
||||
|
||||
# Ubuntu:
|
||||
sudo apt-get install python3-gdal unzip
|
||||
|
||||
2. Get the TIGER 2019 data. You will need the EDGES files
|
||||
(3,233 zip files, 11GB total).
|
||||
|
||||
wget -r ftp://ftp2.census.gov/geo/tiger/TIGER2019/EDGES/
|
||||
|
||||
3. Convert the data into SQL statements. Adjust the file paths in the scripts as needed
|
||||
|
||||
cd data-sources/us-tiger
|
||||
./convert.sh <input-path> <output-path>
|
||||
|
||||
4. Maybe: package the created files
|
||||
|
||||
tar -czf tiger2019-nominatim-preprocessed.tar.gz tiger
|
||||
48
data-sources/us-tiger/convert.sh
Executable file
48
data-sources/us-tiger/convert.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
INPATH=$1
|
||||
OUTPATH=$2
|
||||
|
||||
if [[ ! -d "$INPATH" ]]; then
|
||||
echo "input path does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$OUTPATH" ]]; then
|
||||
echo "output path does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INREGEX='_([0-9]{5})_edges.zip'
|
||||
WORKPATH="$OUTPATH/tmp-workdir/"
|
||||
mkdir -p "$WORKPATH"
|
||||
|
||||
|
||||
|
||||
INFILES=($INPATH/*.zip)
|
||||
echo "Found ${#INFILES[*]} files."
|
||||
|
||||
for F in ${INFILES[*]}; do
|
||||
# echo $F
|
||||
|
||||
if [[ "$F" =~ $INREGEX ]]; then
|
||||
COUNTYID=${BASH_REMATCH[1]}
|
||||
SHAPEFILE="$WORKPATH/$(basename $F '.zip').shp"
|
||||
SQLFILE="$OUTPATH/$COUNTYID.sql"
|
||||
|
||||
unzip -o -q -d "$WORKPATH" "$F"
|
||||
if [[ ! -e "$SHAPEFILE" ]]; then
|
||||
echo "Unzip failed. $SHAPEFILE not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./tiger_address_convert.py "$SHAPEFILE" "$SQLFILE"
|
||||
|
||||
rm $WORKPATH/*
|
||||
fi
|
||||
done
|
||||
|
||||
OUTFILES=($OUTPATH/*.sql)
|
||||
echo "Wrote ${#OUTFILES[*]} files."
|
||||
|
||||
rmdir $WORKPATH
|
||||
620
data-sources/us-tiger/tiger_address_convert.py
Executable file
620
data-sources/us-tiger/tiger_address_convert.py
Executable file
@@ -0,0 +1,620 @@
|
||||
#!/usr/bin/python3
|
||||
# Tiger road data to OSM conversion script
|
||||
# Creates Karlsruhe-style address ways beside the main way
|
||||
# based on the Massachusetts GIS script by christopher schmidt
|
||||
|
||||
#BUGS:
|
||||
# On very tight curves, a loop may be generated in the address way.
|
||||
# It would be nice if the ends of the address ways were not pulled back from dead ends
|
||||
|
||||
|
||||
# Ways that include these mtfccs should not be uploaded
|
||||
# H1100 Connector
|
||||
# H3010 Stream/River
|
||||
# H3013 Braided Stream
|
||||
# H3020 Canal, Ditch or Aqueduct
|
||||
# L4130 Point-to-Point Line
|
||||
# L4140 Property/Parcel Line (Including PLSS)
|
||||
# P0001 Nonvisible Linear Legal/Statistical Boundary
|
||||
# P0002 Perennial Shoreline
|
||||
# P0003 Intermittent Shoreline
|
||||
# P0004 Other non-visible bounding Edge (e.g., Census water boundary, boundary of an areal feature)
|
||||
ignoremtfcc = [ "H1100", "H3010", "H3013", "H3020", "L4130", "L4140", "P0001", "P0002", "P0003", "P0004" ]
|
||||
|
||||
# Sets the distance that the address ways should be from the main way, in feet.
|
||||
address_distance = 30
|
||||
|
||||
# Sets the distance that the ends of the address ways should be pulled back from the ends of the main way, in feet
|
||||
address_pullback = 45
|
||||
|
||||
import sys, os.path, json
|
||||
try:
|
||||
from osgeo import ogr
|
||||
from osgeo import osr
|
||||
except:
|
||||
import ogr
|
||||
import osr
|
||||
|
||||
# https://www.census.gov/geo/reference/codes/cou.html
|
||||
# tiger_county_fips.json was generated from the following:
|
||||
# wget https://www2.census.gov/geo/docs/reference/codes/files/national_county.txt
|
||||
# cat national_county.txt | perl -F, -naE'($F[0] ne 'AS') && $F[3] =~ s/ ((city|City|County|District|Borough|City and Borough|Municipio|Municipality|Parish|Island|Census Area)(?:, |\Z))+//; say qq( "$F[1]$F[2]": "$F[3], $F[0]",)'
|
||||
json_fh = open(os.path.dirname(sys.argv[0]) + "/tiger_county_fips.json")
|
||||
county_fips_data = json.load(json_fh)
|
||||
|
||||
def parse_shp_for_geom_and_tags( filename ):
|
||||
#ogr.RegisterAll()
|
||||
|
||||
dr = ogr.GetDriverByName("ESRI Shapefile")
|
||||
poDS = dr.Open( filename )
|
||||
|
||||
if poDS == None:
|
||||
raise "Open failed."
|
||||
|
||||
poLayer = poDS.GetLayer( 0 )
|
||||
|
||||
fieldNameList = []
|
||||
layerDefinition = poLayer.GetLayerDefn()
|
||||
for i in range(layerDefinition.GetFieldCount()):
|
||||
fieldNameList.append(layerDefinition.GetFieldDefn(i).GetName())
|
||||
# sys.stderr.write(",".join(fieldNameList))
|
||||
|
||||
poLayer.ResetReading()
|
||||
|
||||
ret = []
|
||||
|
||||
poFeature = poLayer.GetNextFeature()
|
||||
while poFeature:
|
||||
tags = {}
|
||||
|
||||
# WAY ID
|
||||
tags["tiger:way_id"] = int( poFeature.GetField("TLID") )
|
||||
|
||||
# FEATURE IDENTIFICATION
|
||||
mtfcc = poFeature.GetField("MTFCC");
|
||||
if mtfcc != None:
|
||||
|
||||
if mtfcc == "L4010": #Pipeline
|
||||
tags["man_made"] = "pipeline"
|
||||
if mtfcc == "L4020": #Powerline
|
||||
tags["power"] = "line"
|
||||
if mtfcc == "L4031": #Aerial Tramway/Ski Lift
|
||||
tags["aerialway"] = "cable_car"
|
||||
if mtfcc == "L4110": #Fence Line
|
||||
tags["barrier"] = "fence"
|
||||
if mtfcc == "L4125": #Cliff/Escarpment
|
||||
tags["natural"] = "cliff"
|
||||
if mtfcc == "L4165": #Ferry Crossing
|
||||
tags["route"] = "ferry"
|
||||
if mtfcc == "R1011": #Railroad Feature (Main, Spur, or Yard)
|
||||
tags["railway"] = "rail"
|
||||
ttyp = poFeature.GetField("TTYP")
|
||||
if ttyp != None:
|
||||
if ttyp == "S":
|
||||
tags["service"] = "spur"
|
||||
if ttyp == "Y":
|
||||
tags["service"] = "yard"
|
||||
tags["tiger:ttyp"] = ttyp
|
||||
if mtfcc == "R1051": #Carline, Streetcar Track, Monorail, Other Mass Transit Rail)
|
||||
tags["railway"] = "light_rail"
|
||||
if mtfcc == "R1052": #Cog Rail Line, Incline Rail Line, Tram
|
||||
tags["railway"] = "incline"
|
||||
if mtfcc == "S1100":
|
||||
tags["highway"] = "primary"
|
||||
if mtfcc == "S1200":
|
||||
tags["highway"] = "secondary"
|
||||
if mtfcc == "S1400":
|
||||
tags["highway"] = "residential"
|
||||
if mtfcc == "S1500":
|
||||
tags["highway"] = "track"
|
||||
if mtfcc == "S1630": #Ramp
|
||||
tags["highway"] = "motorway_link"
|
||||
if mtfcc == "S1640": #Service Drive usually along a limited access highway
|
||||
tags["highway"] = "service"
|
||||
if mtfcc == "S1710": #Walkway/Pedestrian Trail
|
||||
tags["highway"] = "path"
|
||||
if mtfcc == "S1720":
|
||||
tags["highway"] = "steps"
|
||||
if mtfcc == "S1730": #Alley
|
||||
tags["highway"] = "service"
|
||||
tags["service"] = "alley"
|
||||
if mtfcc == "S1740": #Private Road for service vehicles (logging, oil, fields, ranches, etc.)
|
||||
tags["highway"] = "service"
|
||||
tags["access"] = "private"
|
||||
if mtfcc == "S1750": #Private Driveway
|
||||
tags["highway"] = "service"
|
||||
tags["access"] = "private"
|
||||
tags["service"] = "driveway"
|
||||
if mtfcc == "S1780": #Parking Lot Road
|
||||
tags["highway"] = "service"
|
||||
tags["service"] = "parking_aisle"
|
||||
if mtfcc == "S1820": #Bike Path or Trail
|
||||
tags["highway"] = "cycleway"
|
||||
if mtfcc == "S1830": #Bridle Path
|
||||
tags["highway"] = "bridleway"
|
||||
tags["tiger:mtfcc"] = mtfcc
|
||||
|
||||
# FEATURE NAME
|
||||
if poFeature.GetField("FULLNAME"):
|
||||
#capitalizes the first letter of each word
|
||||
name = poFeature.GetField( "FULLNAME" )
|
||||
tags["name"] = name
|
||||
|
||||
#Attempt to guess highway grade
|
||||
if name[0:2] == "I-":
|
||||
tags["highway"] = "motorway"
|
||||
if name[0:3] == "US ":
|
||||
tags["highway"] = "primary"
|
||||
if name[0:3] == "US-":
|
||||
tags["highway"] = "primary"
|
||||
if name[0:3] == "Hwy":
|
||||
if tags["highway"] != "primary":
|
||||
tags["highway"] = "secondary"
|
||||
|
||||
# TIGER 2017 no longer contains this field
|
||||
if 'DIVROAD' in fieldNameList:
|
||||
divroad = poFeature.GetField("DIVROAD")
|
||||
if divroad != None:
|
||||
if divroad == "Y" and "highway" in tags and tags["highway"] == "residential":
|
||||
tags["highway"] = "tertiary"
|
||||
tags["tiger:separated"] = divroad
|
||||
|
||||
statefp = poFeature.GetField("STATEFP")
|
||||
countyfp = poFeature.GetField("COUNTYFP")
|
||||
if (statefp != None) and (countyfp != None):
|
||||
county_name = county_fips_data.get(statefp + '' + countyfp)
|
||||
if county_name:
|
||||
tags["tiger:county"] = county_name
|
||||
|
||||
# tlid = poFeature.GetField("TLID")
|
||||
# if tlid != None:
|
||||
# tags["tiger:tlid"] = tlid
|
||||
|
||||
lfromadd = poFeature.GetField("LFROMADD")
|
||||
if lfromadd != None:
|
||||
tags["tiger:lfromadd"] = lfromadd
|
||||
|
||||
rfromadd = poFeature.GetField("RFROMADD")
|
||||
if rfromadd != None:
|
||||
tags["tiger:rfromadd"] = rfromadd
|
||||
|
||||
ltoadd = poFeature.GetField("LTOADD")
|
||||
if ltoadd != None:
|
||||
tags["tiger:ltoadd"] = ltoadd
|
||||
|
||||
rtoadd = poFeature.GetField("RTOADD")
|
||||
if rtoadd != None:
|
||||
tags["tiger:rtoadd"] = rtoadd
|
||||
|
||||
zipl = poFeature.GetField("ZIPL")
|
||||
if zipl != None:
|
||||
tags["tiger:zip_left"] = zipl
|
||||
|
||||
zipr = poFeature.GetField("ZIPR")
|
||||
if zipr != None:
|
||||
tags["tiger:zip_right"] = zipr
|
||||
|
||||
if mtfcc not in ignoremtfcc:
|
||||
# COPY DOWN THE GEOMETRY
|
||||
geom = []
|
||||
|
||||
rawgeom = poFeature.GetGeometryRef()
|
||||
for i in range( rawgeom.GetPointCount() ):
|
||||
geom.append( (rawgeom.GetX(i), rawgeom.GetY(i)) )
|
||||
|
||||
ret.append( (geom, tags) )
|
||||
poFeature = poLayer.GetNextFeature()
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# ====================================
|
||||
# to do read .prj file for this data
|
||||
# Change the Projcs_wkt to match your datas prj file.
|
||||
# ====================================
|
||||
projcs_wkt = \
|
||||
"""GEOGCS["GCS_North_American_1983",
|
||||
DATUM["D_North_American_1983",
|
||||
SPHEROID["GRS_1980",6378137,298.257222101]],
|
||||
PRIMEM["Greenwich",0],
|
||||
UNIT["Degree",0.017453292519943295]]"""
|
||||
|
||||
from_proj = osr.SpatialReference()
|
||||
from_proj.ImportFromWkt( projcs_wkt )
|
||||
|
||||
# output to WGS84
|
||||
to_proj = osr.SpatialReference()
|
||||
to_proj.SetWellKnownGeogCS( "EPSG:4326" )
|
||||
|
||||
tr = osr.CoordinateTransformation( from_proj, to_proj )
|
||||
|
||||
import math
|
||||
def length(segment, nodelist):
|
||||
'''Returns the length (in feet) of a segment'''
|
||||
first = True
|
||||
distance = 0
|
||||
lat_feet = 364613 #The approximate number of feet in one degree of latitude
|
||||
for point in segment:
|
||||
pointid, (lat, lon) = nodelist[ round_point( point ) ]
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
#The approximate number of feet in one degree of longitute
|
||||
lrad = math.radians(lat)
|
||||
lon_feet = 365527.822 * math.cos(lrad) - 306.75853 * math.cos(3 * lrad) + 0.3937 * math.cos(5 * lrad)
|
||||
distance += math.sqrt(((lat - previous[0])*lat_feet)**2 + ((lon - previous[1])*lon_feet)**2)
|
||||
previous = (lat, lon)
|
||||
return distance
|
||||
|
||||
def addressways(waylist, nodelist, first_id):
|
||||
id = first_id
|
||||
lat_feet = 364613 #The approximate number of feet in one degree of latitude
|
||||
distance = float(address_distance)
|
||||
ret = []
|
||||
|
||||
for waykey, segments in waylist.items():
|
||||
waykey = dict(waykey)
|
||||
rsegments = []
|
||||
lsegments = []
|
||||
for segment in segments:
|
||||
lsegment = []
|
||||
rsegment = []
|
||||
lastpoint = None
|
||||
|
||||
# Don't pull back the ends of very short ways too much
|
||||
seglength = length(segment, nodelist)
|
||||
if seglength < float(address_pullback) * 3.0:
|
||||
pullback = seglength / 3.0
|
||||
else:
|
||||
pullback = float(address_pullback)
|
||||
if "tiger:lfromadd" in waykey:
|
||||
lfromadd = waykey["tiger:lfromadd"]
|
||||
else:
|
||||
lfromadd = None
|
||||
if "tiger:ltoadd" in waykey:
|
||||
ltoadd = waykey["tiger:ltoadd"]
|
||||
else:
|
||||
ltoadd = None
|
||||
if "tiger:rfromadd" in waykey:
|
||||
rfromadd = waykey["tiger:rfromadd"]
|
||||
else:
|
||||
rfromadd = None
|
||||
if "tiger:rtoadd" in waykey:
|
||||
rtoadd = waykey["tiger:rtoadd"]
|
||||
else:
|
||||
rtoadd = None
|
||||
if rfromadd != None and rtoadd != None:
|
||||
right = True
|
||||
else:
|
||||
right = False
|
||||
if lfromadd != None and ltoadd != None:
|
||||
left = True
|
||||
else:
|
||||
left = False
|
||||
if left or right:
|
||||
first = True
|
||||
firstpointid, firstpoint = nodelist[ round_point( segment[0] ) ]
|
||||
|
||||
finalpointid, finalpoint = nodelist[ round_point( segment[len(segment) - 1] ) ]
|
||||
for point in segment:
|
||||
pointid, (lat, lon) = nodelist[ round_point( point ) ]
|
||||
|
||||
#The approximate number of feet in one degree of longitute
|
||||
lrad = math.radians(lat)
|
||||
lon_feet = 365527.822 * math.cos(lrad) - 306.75853 * math.cos(3 * lrad) + 0.3937 * math.cos(5 * lrad)
|
||||
|
||||
#Calculate the points of the offset ways
|
||||
if lastpoint != None:
|
||||
#Skip points too close to start
|
||||
if math.sqrt((lat * lat_feet - firstpoint[0] * lat_feet)**2 + (lon * lon_feet - firstpoint[1] * lon_feet)**2) < pullback:
|
||||
#Preserve very short ways (but will be rendered backwards)
|
||||
if pointid != finalpointid:
|
||||
continue
|
||||
#Skip points too close to end
|
||||
if math.sqrt((lat * lat_feet - finalpoint[0] * lat_feet)**2 + (lon * lon_feet - finalpoint[1] * lon_feet)**2) < pullback:
|
||||
#Preserve very short ways (but will be rendered backwards)
|
||||
if (pointid != firstpointid) and (pointid != finalpointid):
|
||||
continue
|
||||
|
||||
X = (lon - lastpoint[1]) * lon_feet
|
||||
Y = (lat - lastpoint[0]) * lat_feet
|
||||
if Y != 0:
|
||||
theta = math.pi/2 - math.atan( X / Y)
|
||||
Xp = math.sin(theta) * distance
|
||||
Yp = math.cos(theta) * distance
|
||||
else:
|
||||
Xp = 0
|
||||
if X > 0:
|
||||
Yp = -distance
|
||||
else:
|
||||
Yp = distance
|
||||
|
||||
if Y > 0:
|
||||
Xp = -Xp
|
||||
else:
|
||||
Yp = -Yp
|
||||
|
||||
if first:
|
||||
first = False
|
||||
dX = - (Yp * (pullback / distance)) / lon_feet #Pull back the first point
|
||||
dY = (Xp * (pullback / distance)) / lat_feet
|
||||
if left:
|
||||
lpoint = (lastpoint[0] + (Yp / lat_feet) - dY, lastpoint[1] + (Xp / lon_feet) - dX)
|
||||
lsegment.append( (id, lpoint) )
|
||||
id += 1
|
||||
if right:
|
||||
rpoint = (lastpoint[0] - (Yp / lat_feet) - dY, lastpoint[1] - (Xp / lon_feet) - dX)
|
||||
rsegment.append( (id, rpoint) )
|
||||
id += 1
|
||||
|
||||
else:
|
||||
#round the curves
|
||||
if delta[1] != 0:
|
||||
theta = abs(math.atan(delta[0] / delta[1]))
|
||||
else:
|
||||
theta = math.pi / 2
|
||||
if Xp != 0:
|
||||
theta = theta - abs(math.atan(Yp / Xp))
|
||||
else: theta = theta - math.pi / 2
|
||||
r = 1 + abs(math.tan(theta/2))
|
||||
if left:
|
||||
lpoint = (lastpoint[0] + (Yp + delta[0]) * r / (lat_feet * 2), lastpoint[1] + (Xp + delta[1]) * r / (lon_feet * 2))
|
||||
lsegment.append( (id, lpoint) )
|
||||
id += 1
|
||||
if right:
|
||||
rpoint = (lastpoint[0] - (Yp + delta[0]) * r / (lat_feet * 2), lastpoint[1] - (Xp + delta[1]) * r / (lon_feet * 2))
|
||||
|
||||
rsegment.append( (id, rpoint) )
|
||||
id += 1
|
||||
|
||||
delta = (Yp, Xp)
|
||||
|
||||
lastpoint = (lat, lon)
|
||||
|
||||
|
||||
#Add in the last node
|
||||
dX = - (Yp * (pullback / distance)) / lon_feet
|
||||
dY = (Xp * (pullback / distance)) / lat_feet
|
||||
if left:
|
||||
lpoint = (lastpoint[0] + (Yp + delta[0]) / (lat_feet * 2) + dY, lastpoint[1] + (Xp + delta[1]) / (lon_feet * 2) + dX )
|
||||
lsegment.append( (id, lpoint) )
|
||||
id += 1
|
||||
if right:
|
||||
rpoint = (lastpoint[0] - Yp / lat_feet + dY, lastpoint[1] - Xp / lon_feet + dX)
|
||||
rsegment.append( (id, rpoint) )
|
||||
id += 1
|
||||
|
||||
#Generate the tags for ways and nodes
|
||||
zipr = ''
|
||||
zipl = ''
|
||||
name = ''
|
||||
county = ''
|
||||
if "tiger:zip_right" in waykey:
|
||||
zipr = waykey["tiger:zip_right"]
|
||||
if "tiger:zip_left" in waykey:
|
||||
zipl = waykey["tiger:zip_left"]
|
||||
if "name" in waykey:
|
||||
name = waykey["name"]
|
||||
if "tiger:county" in waykey:
|
||||
county = waykey["tiger:county"]
|
||||
if "tiger:separated" in waykey: # No longer set in Tiger-2017
|
||||
separated = waykey["tiger:separated"]
|
||||
else:
|
||||
separated = "N"
|
||||
|
||||
#Write the nodes of the offset ways
|
||||
if right:
|
||||
rlinestring = [];
|
||||
for i, point in rsegment:
|
||||
rlinestring.append( "%f %f" % (point[1], point[0]) )
|
||||
if left:
|
||||
llinestring = [];
|
||||
for i, point in lsegment:
|
||||
llinestring.append( "%f %f" % (point[1], point[0]) )
|
||||
if right:
|
||||
rsegments.append( rsegment )
|
||||
if left:
|
||||
lsegments.append( lsegment )
|
||||
rtofromint = right #Do the addresses convert to integers?
|
||||
ltofromint = left #Do the addresses convert to integers?
|
||||
if right:
|
||||
try: rfromint = int(rfromadd)
|
||||
except:
|
||||
print("Non integer address: %s" % rfromadd)
|
||||
rtofromint = False
|
||||
try: rtoint = int(rtoadd)
|
||||
except:
|
||||
print("Non integer address: %s" % rtoadd)
|
||||
rtofromint = False
|
||||
if left:
|
||||
try: lfromint = int(lfromadd)
|
||||
except:
|
||||
print("Non integer address: %s" % lfromadd)
|
||||
ltofromint = False
|
||||
try: ltoint = int(ltoadd)
|
||||
except:
|
||||
print("Non integer address: %s" % ltoadd)
|
||||
ltofromint = False
|
||||
if right:
|
||||
id += 1
|
||||
|
||||
interpolationtype = "all";
|
||||
if rtofromint:
|
||||
if (rfromint % 2) == 0 and (rtoint % 2) == 0:
|
||||
if separated == "Y": #Doesn't matter if there is another side
|
||||
interpolationtype = "even";
|
||||
elif ltofromint and (lfromint % 2) == 1 and (ltoint % 2) == 1:
|
||||
interpolationtype = "even";
|
||||
elif (rfromint % 2) == 1 and (rtoint % 2) == 1:
|
||||
if separated == "Y": #Doesn't matter if there is another side
|
||||
interpolationtype = "odd";
|
||||
elif ltofromint and (lfromint % 2) == 0 and (ltoint % 2) == 0:
|
||||
interpolationtype = "odd";
|
||||
|
||||
ret.append( "SELECT tiger_line_import(ST_GeomFromText('LINESTRING(%s)',4326), %s, %s, %s, %s, %s, %s);" %
|
||||
( ",".join(rlinestring), sql_quote(rfromadd), sql_quote(rtoadd), sql_quote(interpolationtype), sql_quote(name), sql_quote(county), sql_quote(zipr) ) )
|
||||
|
||||
if left:
|
||||
id += 1
|
||||
|
||||
interpolationtype = "all";
|
||||
if ltofromint:
|
||||
if (lfromint % 2) == 0 and (ltoint % 2) == 0:
|
||||
if separated == "Y":
|
||||
interpolationtype = "even";
|
||||
elif rtofromint and (rfromint % 2) == 1 and (rtoint % 2) == 1:
|
||||
interpolationtype = "even";
|
||||
elif (lfromint % 2) == 1 and (ltoint % 2) == 1:
|
||||
if separated == "Y":
|
||||
interpolationtype = "odd";
|
||||
elif rtofromint and (rfromint %2 ) == 0 and (rtoint % 2) == 0:
|
||||
interpolationtype = "odd";
|
||||
|
||||
ret.append( "SELECT tiger_line_import(ST_GeomFromText('LINESTRING(%s)',4326), %s, %s, %s, %s, %s, %s);" %
|
||||
( ",".join(llinestring), sql_quote(lfromadd), sql_quote(ltoadd), sql_quote(interpolationtype), sql_quote(name), sql_quote(county), sql_quote(zipl) ) )
|
||||
|
||||
return ret
|
||||
|
||||
def sql_quote( string ):
|
||||
return "'" + string.replace("'", "''") + "'"
|
||||
|
||||
def unproject( point ):
|
||||
pt = tr.TransformPoint( point[0], point[1] )
|
||||
return (pt[1], pt[0])
|
||||
|
||||
def round_point( point, accuracy=8 ):
|
||||
return tuple( [ round(x,accuracy) for x in point ] )
|
||||
|
||||
def compile_nodelist( parsed_gisdata, first_id=1 ):
|
||||
nodelist = {}
|
||||
|
||||
i = first_id
|
||||
for geom, tags in parsed_gisdata:
|
||||
if len( geom )==0:
|
||||
continue
|
||||
|
||||
for point in geom:
|
||||
r_point = round_point( point )
|
||||
if r_point not in nodelist:
|
||||
nodelist[ r_point ] = (i, unproject( point ))
|
||||
i += 1
|
||||
|
||||
return (i, nodelist)
|
||||
|
||||
def adjacent( left, right ):
|
||||
left_left = round_point(left[0])
|
||||
left_right = round_point(left[-1])
|
||||
right_left = round_point(right[0])
|
||||
right_right = round_point(right[-1])
|
||||
|
||||
return ( left_left == right_left or
|
||||
left_left == right_right or
|
||||
left_right == right_left or
|
||||
left_right == right_right )
|
||||
|
||||
def glom( left, right ):
|
||||
left = list( left )
|
||||
right = list( right )
|
||||
|
||||
left_left = round_point(left[0])
|
||||
left_right = round_point(left[-1])
|
||||
right_left = round_point(right[0])
|
||||
right_right = round_point(right[-1])
|
||||
|
||||
if left_left == right_left:
|
||||
left.reverse()
|
||||
return left[0:-1] + right
|
||||
|
||||
if left_left == right_right:
|
||||
return right[0:-1] + left
|
||||
|
||||
if left_right == right_left:
|
||||
return left[0:-1] + right
|
||||
|
||||
if left_right == right_right:
|
||||
right.reverse()
|
||||
return left[0:-1] + right
|
||||
|
||||
raise 'segments are not adjacent'
|
||||
|
||||
def glom_once( segments ):
|
||||
if len(segments)==0:
|
||||
return segments
|
||||
|
||||
unsorted = list( segments )
|
||||
x = unsorted.pop(0)
|
||||
|
||||
while len( unsorted ) > 0:
|
||||
n = len( unsorted )
|
||||
|
||||
for i in range(0, n):
|
||||
y = unsorted[i]
|
||||
if adjacent( x, y ):
|
||||
y = unsorted.pop(i)
|
||||
x = glom( x, y )
|
||||
break
|
||||
|
||||
# Sorted and unsorted lists have no adjacent segments
|
||||
if len( unsorted ) == n:
|
||||
break
|
||||
|
||||
return x, unsorted
|
||||
|
||||
def glom_all( segments ):
|
||||
unsorted = segments
|
||||
chunks = []
|
||||
|
||||
while unsorted != []:
|
||||
chunk, unsorted = glom_once( unsorted )
|
||||
chunks.append( chunk )
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
|
||||
def compile_waylist( parsed_gisdata ):
|
||||
waylist = {}
|
||||
|
||||
#Group by tiger:way_id
|
||||
for geom, tags in parsed_gisdata:
|
||||
way_key = tags.copy()
|
||||
way_key = ( way_key['tiger:way_id'], tuple( [(k,v) for k,v in way_key.items()] ) )
|
||||
|
||||
if way_key not in waylist:
|
||||
waylist[way_key] = []
|
||||
|
||||
waylist[way_key].append( geom )
|
||||
|
||||
ret = {}
|
||||
for (way_id, way_key), segments in waylist.items():
|
||||
ret[way_key] = glom_all( segments )
|
||||
return ret
|
||||
|
||||
|
||||
def shape_to_sql( shp_filename, sql_filename ):
|
||||
|
||||
print("parsing shpfile %s" % shp_filename)
|
||||
parsed_features = parse_shp_for_geom_and_tags( shp_filename )
|
||||
|
||||
print("compiling nodelist")
|
||||
i, nodelist = compile_nodelist( parsed_features )
|
||||
|
||||
print("compiling waylist")
|
||||
waylist = compile_waylist( parsed_features )
|
||||
|
||||
print("preparing address ways")
|
||||
sql_lines = addressways(waylist, nodelist, i)
|
||||
|
||||
print("writing %s" % sql_filename)
|
||||
fp = open( sql_filename, "w" )
|
||||
fp.write( "\n".join( sql_lines ) )
|
||||
fp.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys, os.path
|
||||
if len(sys.argv) < 3:
|
||||
print("%s input.shp output.sql" % sys.argv[0])
|
||||
sys.exit()
|
||||
shp_filename = sys.argv[1]
|
||||
sql_filename = sys.argv[2]
|
||||
shape_to_sql(shp_filename, sql_filename)
|
||||
3237
data-sources/us-tiger/tiger_county_fips.json
Normal file
3237
data-sources/us-tiger/tiger_county_fips.json
Normal file
File diff suppressed because it is too large
Load Diff
58
data-sources/wikipedia-wikidata/README.md
Normal file
58
data-sources/wikipedia-wikidata/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
## Add Wikipedia and Wikidata to Nominatim
|
||||
|
||||
OSM contributors frequently tag items with links to Wikipedia and Wikidata. Nominatim can use the page ranking of Wikipedia pages to help indicate the relative importance of osm features. This is done by calculating an importance score between 0 and 1 based on the number of inlinks to an article for a location. If two places have the same name and one is more important than the other, the wikipedia score often points to the correct place.
|
||||
|
||||
These scripts extract and prepare both Wikipedia page rank and Wikidata links for use in Nominatim.
|
||||
|
||||
#### Create a new postgres DB for Processing
|
||||
|
||||
Due to the size of initial and intermediate tables, processing can be done in an external database:
|
||||
```
|
||||
CREATE DATABASE wikiprocessingdb;
|
||||
```
|
||||
---
|
||||
Wikipedia
|
||||
---
|
||||
|
||||
Processing these data requires a large amount of disk space (~1TB) and considerable time (>24 hours).
|
||||
|
||||
#### Import & Process Wikipedia tables
|
||||
|
||||
This step downloads and converts [Wikipedia](https://dumps.wikimedia.org/) page data SQL dumps to postgreSQL files which can be imported and processed with pagelink information from Wikipedia language sites to calculate importance scores.
|
||||
|
||||
- The script will processes data from whatever set of Wikipedia languages are specified in the initial languages array
|
||||
|
||||
- Note that processing the top 40 Wikipedia languages can take over a day, and will add nearly 1TB to the processing database. The final output tables will be approximately 11GB and 2GB in size
|
||||
|
||||
To download, convert, and import the data, then process summary statistics and compute importance scores, run:
|
||||
```
|
||||
./wikipedia_import.sh
|
||||
```
|
||||
---
|
||||
Wikidata
|
||||
---
|
||||
|
||||
This script downloads and processes Wikidata to enrich the previously created Wikipedia tables for use in Nominatim.
|
||||
|
||||
#### Import & Process Wikidata
|
||||
|
||||
This step downloads and converts [Wikidata](https://dumps.wikimedia.org/wikidatawiki/) page data SQL dumps to postgreSQL files which can be processed and imported into Nominatim database. Also utilizes Wikidata Query Service API to discover and include place types.
|
||||
|
||||
- Script presumes that the user has already processed Wikipedia tables as specified above
|
||||
|
||||
- Script requires wikidata_place_types.txt and wikidata_place_type_levles.csv
|
||||
|
||||
- script requires the [jq json parser](https://stedolan.github.io/jq/)
|
||||
|
||||
- Script processes data from whatever set of Wikipedia languages are specified in the initial languages array
|
||||
|
||||
- Script queries Wikidata Query Service API and imports all instances of place types listed in wikidata_place_types.txt
|
||||
|
||||
- Script updates wikipedia_articles table with extracted wikidata
|
||||
|
||||
By including Wikidata in the wikipedia_articles table, new connections can be made on the fly from the Nominatim placex table to wikipedia_article importance scores.
|
||||
|
||||
To download, convert, and import the data, then process required items, run:
|
||||
```
|
||||
./wikidata_import.sh
|
||||
```
|
||||
95
data-sources/wikipedia-wikidata/import_wikidata.sh
Executable file
95
data-sources/wikipedia-wikidata/import_wikidata.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
psqlcmd() {
|
||||
psql wikiprocessingdb
|
||||
}
|
||||
|
||||
mysql2pgsqlcmd() {
|
||||
./mysql2pgsql.perl /dev/stdin /dev/stdout
|
||||
}
|
||||
|
||||
|
||||
# list the languages to process (refer to List of Wikipedias here: https://en.wikipedia.org/wiki/List_of_Wikipedias)
|
||||
|
||||
language=( "ar" "bg" "ca" "cs" "da" "de" "en" "es" "eo" "eu" "fa" "fr" "ko" "hi" "hr" "id" "it" "he" "lt" "hu" "ms" "nl" "ja" "no" "pl" "pt" "kk" "ro" "ru" "sk" "sl" "sr" "fi" "sv" "tr" "uk" "vi" "vo" "war" "zh" )
|
||||
|
||||
|
||||
# get a few wikidata dump tables
|
||||
|
||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-geo_tags.sql.gz
|
||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-page.sql.gz
|
||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-wb_items_per_site.sql.gz
|
||||
|
||||
|
||||
# import wikidata tables
|
||||
|
||||
gzip -dc wikidatawiki-latest-geo_tags.sql.gz | mysql2pgsqlcmd | psqlcmd
|
||||
gzip -dc wikidatawiki-latest-page.sql.gz | mysql2pgsqlcmd | psqlcmd
|
||||
gzip -dc wikidatawiki-latest-wb_items_per_site.sql.gz | mysql2pgsqlcmd | psqlcmd
|
||||
|
||||
|
||||
# get wikidata places from wikidata query API
|
||||
|
||||
while read F ; do
|
||||
wget "https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=SELECT ?item WHERE{?item wdt:P31*/wdt:P279*wd:$F;}" -O $F.json
|
||||
jq -r '.results | .[] | .[] | [.item.value] | @csv' $F.json >> $F.txt
|
||||
awk -v qid=$F '{print $0 ","qid}' $F.txt | sed -e 's!"http://www.wikidata.org/entity/!!' | sed 's/"//g' >> $F.csv
|
||||
cat $F.csv >> wikidata_place_dump.csv
|
||||
rm $F.json $F.txt $F.csv
|
||||
done < wikidata_place_types.txt
|
||||
|
||||
|
||||
# import wikidata places
|
||||
|
||||
echo "CREATE TABLE wikidata_place_dump (item text, instance_of text);" | psqlcmd
|
||||
echo "COPY wikidata_place_dump (item, instance_of) FROM '/srv/nominatim/Nominatim/data-sources/wikipedia-wikidata/wikidata_place_dump.csv' DELIMITER ',' CSV;" | psqlcmd
|
||||
|
||||
echo "CREATE TABLE wikidata_place_type_levels (place_type text, level integer);" | psqlcmd
|
||||
echo "COPY wikidata_place_type_levels (place_type, level) FROM '/srv/nominatim/Nominatim/data-sources/wikipedia-wikidata/wikidata_place_type_levels.csv' DELIMITER ',' CSV HEADER;" | psqlcmd
|
||||
|
||||
|
||||
# create derived tables
|
||||
|
||||
echo "CREATE TABLE geo_earth_primary AS SELECT gt_page_id, gt_lat, gt_lon FROM geo_tags WHERE gt_globe = 'earth' AND gt_primary = 1 AND NOT( gt_lat < -90 OR gt_lat > 90 OR gt_lon < -180 OR gt_lon > 180 OR gt_lat=0 OR gt_lon=0) ;" | psqlcmd
|
||||
echo "CREATE TABLE geo_earth_wikidata AS SELECT DISTINCT geo_earth_primary.gt_page_id, geo_earth_primary.gt_lat, geo_earth_primary.gt_lon, page.page_title, page.page_namespace FROM geo_earth_primary LEFT OUTER JOIN page ON (geo_earth_primary.gt_page_id = page.page_id) ORDER BY geo_earth_primary.gt_page_id;" | psqlcmd
|
||||
|
||||
echo "ALTER TABLE wikidata_place_dump ADD COLUMN ont_level integer, ADD COLUMN lat numeric(11,8), ADD COLUMN lon numeric(11,8);" | psqlcmd
|
||||
echo "UPDATE wikidata_place_dump SET ont_level = wikidata_place_type_levels.level FROM wikidata_place_type_levels WHERE wikidata_place_dump.instance_of = wikidata_place_type_levels.place_type;" | psqlcmd
|
||||
|
||||
echo "CREATE TABLE wikidata_places AS SELECT DISTINCT ON (item) item, instance_of, MAX(ont_level) AS ont_level, lat, lon FROM wikidata_place_dump GROUP BY item, instance_of, ont_level, lat, lon ORDER BY item;" | psqlcmd
|
||||
echo "UPDATE wikidata_places SET lat = geo_earth_wikidata.gt_lat, lon = geo_earth_wikidata.gt_lon FROM geo_earth_wikidata WHERE wikidata_places.item = geo_earth_wikidata.page_title" | psqlcmd
|
||||
|
||||
|
||||
# process language pages
|
||||
|
||||
echo "CREATE TABLE wikidata_pages (item text, instance_of text, lat numeric(11,8), lon numeric(11,8), ips_site_page text, language text );" | psqlcmd
|
||||
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
echo "CREATE TABLE wikidata_${i}_pages as select wikidata_places.item, wikidata_places.instance_of, wikidata_places.lat, wikidata_places.lon, wb_items_per_site.ips_site_page FROM wikidata_places LEFT JOIN wb_items_per_site ON (CAST (( LTRIM(wikidata_places.item, 'Q')) AS INTEGER) = wb_items_per_site.ips_item_id) WHERE ips_site_id = '${i}wiki' AND LEFT(wikidata_places.item,1) = 'Q' order by wikidata_places.item;" | psqlcmd
|
||||
echo "ALTER TABLE wikidata_${i}_pages ADD COLUMN language text;" | psqlcmd
|
||||
echo "UPDATE wikidata_${i}_pages SET language = '${i}';" | psqlcmd
|
||||
echo "INSERT INTO wikidata_pages SELECT item, instance_of, lat, lon, ips_site_page, language FROM wikidata_${i}_pages;" | psqlcmd
|
||||
done
|
||||
|
||||
echo "ALTER TABLE wikidata_pages ADD COLUMN wp_page_title text;" | psqlcmd
|
||||
echo "UPDATE wikidata_pages SET wp_page_title = REPLACE(ips_site_page, ' ', '_');" | psqlcmd
|
||||
echo "ALTER TABLE wikidata_pages DROP COLUMN ips_site_page;" | psqlcmd
|
||||
|
||||
|
||||
# add wikidata to wikipedia_article table
|
||||
|
||||
echo "UPDATE wikipedia_article SET lat = wikidata_pages.lat, lon = wikidata_pages.lon, wd_page_title = wikidata_pages.item, instance_of = wikidata_pages.instance_of FROM wikidata_pages WHERE wikipedia_article.language = wikidata_pages.language AND wikipedia_article.title = wikidata_pages.wp_page_title;" | psqlcmd
|
||||
echo "CREATE TABLE wikipedia_article_slim AS SELECT * FROM wikipedia_article WHERE wikidata_id IS NOT NULL;" | psqlcmd
|
||||
echo "ALTER TABLE wikipedia_article RENAME TO wikipedia_article_full;" | psqlcmd
|
||||
echo "ALTER TABLE wikipedia_article_slim RENAME TO wikipedia_article;" | psqlcmd
|
||||
|
||||
|
||||
# clean up intermediate tables
|
||||
|
||||
echo "DROP TABLE wikidata_place_dump;" | psqlcmd
|
||||
echo "DROP TABLE geo_earth_primary;" | psqlcmd
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
echo "DROP TABLE wikidata_${i}_pages;" | psqlcmd
|
||||
done
|
||||
77
data-sources/wikipedia-wikidata/import_wikipedia.sh
Executable file
77
data-sources/wikipedia-wikidata/import_wikipedia.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
psqlcmd() {
|
||||
psql wikiprocessingdb
|
||||
}
|
||||
|
||||
mysql2pgsqlcmd() {
|
||||
./mysql2pgsql.perl /dev/stdin /dev/stdout
|
||||
}
|
||||
|
||||
|
||||
# list the languages to process (refer to List of Wikipedias here: https://en.wikipedia.org/wiki/List_of_Wikipedias)
|
||||
|
||||
language=( "ar" "bg" "ca" "cs" "da" "de" "en" "es" "eo" "eu" "fa" "fr" "ko" "hi" "hr" "id" "it" "he" "lt" "hu" "ms" "nl" "ja" "no" "pl" "pt" "kk" "ro" "ru" "sk" "sl" "sr" "fi" "sv" "tr" "uk" "vi" "vo" "war" "zh" )
|
||||
|
||||
|
||||
# create wikipedia calculation tables
|
||||
|
||||
echo "CREATE TABLE linkcounts (language text, title text, count integer, sumcount integer, lat double precision, lon double precision);" | psqlcmd
|
||||
echo "CREATE TABLE wikipedia_article (language text NOT NULL, title text NOT NULL, langcount integer, othercount integer, totalcount integer, lat double precision, lon double precision, importance double precision, title_en text, osm_type character(1), osm_id bigint );" | psqlcmd
|
||||
echo "CREATE TABLE wikipedia_redirect (language text, from_title text, to_title text );" | psqlcmd
|
||||
|
||||
|
||||
# download individual wikipedia language tables
|
||||
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-page.sql.gz
|
||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-pagelinks.sql.gz
|
||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-langlinks.sql.gz
|
||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-redirect.sql.gz
|
||||
done
|
||||
|
||||
|
||||
# import individual wikipedia language tables
|
||||
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
gzip -dc ${i}wiki-latest-pagelinks.sql.gz | sed "s/\`pagelinks\`/\`${i}pagelinks\`/g" | mysql2pgsqlcmd | psqlcmd
|
||||
gzip -dc ${i}wiki-latest-page.sql.gz | sed "s/\`page\`/\`${i}page\`/g" | mysql2pgsqlcmd | psqlcmd
|
||||
gzip -dc ${i}wiki-latest-langlinks.sql.gz | sed "s/\`langlinks\`/\`${i}langlinks\`/g" | mysql2pgsqlcmd | psqlcmd
|
||||
gzip -dc ${i}wiki-latest-redirect.sql.gz | sed "s/\`redirect\`/\`${i}redirect\`/g" | mysql2pgsqlcmd | psqlcmd
|
||||
done
|
||||
|
||||
|
||||
# process language tables and associated pagelink counts
|
||||
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
echo "create table ${i}pagelinkcount as select pl_title as title,count(*) as count from ${i}pagelinks where pl_namespace = 0 group by pl_title;" | psqlcmd
|
||||
echo "insert into linkcounts select '${i}',pl_title,count(*) from ${i}pagelinks where pl_namespace = 0 group by pl_title;" | psqlcmd
|
||||
echo "insert into wikipedia_redirect select '${i}',page_title,rd_title from ${i}redirect join ${i}page on (rd_from = page_id) where page_namespace = 0 and rd_namespace = 0;" | psqlcmd
|
||||
echo "alter table ${i}pagelinkcount add column othercount integer;" | psqlcmd
|
||||
echo "update ${i}pagelinkcount set othercount = 0;" | psqlcmd
|
||||
for j in "${language[@]}"
|
||||
do
|
||||
echo "update ${i}pagelinkcount set othercount = ${i}pagelinkcount.othercount + x.count from (select page_title as title,count from ${i}langlinks join ${i}page on (ll_from = page_id) join ${j}pagelinkcount on (ll_lang = '${j}' and ll_title = title)) as x where x.title = ${i}pagelinkcount.title;" | psqlcmd
|
||||
done
|
||||
echo "insert into wikipedia_article select '${i}', title, count, othercount, count+othercount from ${i}pagelinkcount;" | psqlcmd
|
||||
done
|
||||
|
||||
|
||||
# calculate importance score for each wikipedia page
|
||||
|
||||
echo "update wikipedia_article set importance = log(totalcount)/log((select max(totalcount) from wikipedia_article))" | psqlcmd
|
||||
|
||||
|
||||
# clean up intermediate tables to conserve space
|
||||
|
||||
for i in "${language[@]}"
|
||||
do
|
||||
echo "DROP TABLE ${i}pagelinks;" | psqlcmd
|
||||
echo "DROP TABLE ${i}page;" | psqlcmd
|
||||
echo "DROP TABLE ${i}langlinks;" | psqlcmd
|
||||
echo "DROP TABLE ${i}redirect;" | psqlcmd
|
||||
echo "DROP TABLE ${i}pagelinkcount;" | psqlcmd
|
||||
done
|
||||
@@ -341,7 +341,7 @@ if (/(create\s+table\s+)([-_\w]+)\s/i) { # example: CREATE TABLE `english_engli
|
||||
# in the foreign-key case it will only remove the foreign-key constraint, not the other table entirely.)
|
||||
# (source: 8.1.3 docs, section "drop table")
|
||||
warn "table $table will be dropped CASCADE\n";
|
||||
$pre_create_sql .= "DROP TABLE $table CASCADE\\g\n"; # custom dumps may be missing the 'dump' commands
|
||||
$pre_create_sql .= "DROP TABLE $table CASCADE;\n"; # custom dumps may be missing the 'dump' commands
|
||||
}
|
||||
|
||||
s/(create\s+table\s+)([-_\w]+)\s/$1 $table /i;
|
||||
@@ -367,6 +367,7 @@ if ($create_sql ne "") { # we are inside create table statement so lets
|
||||
s/INSERT METHOD[=\s+][^;\s]+//i;
|
||||
s/PASSWORD=[^;\s]+//i;
|
||||
s/ROW_FORMAT=(?:DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT)+//i;
|
||||
s/KEY_BLOCK_SIZE=8//i;
|
||||
s/DELAY KEY WRITE=[^;\s]+//i;
|
||||
s/INDEX DIRECTORY[=\s+][^;\s]+//i;
|
||||
s/DATA DIRECTORY=[^;\s]+//i;
|
||||
@@ -389,6 +390,7 @@ if ($create_sql ne "") { # we are inside create table statement so lets
|
||||
s/DEFAULT CHARSET=[^;\s]+//i; # my mysql version is 4.1.11
|
||||
s/ENGINE\s*=\s*[^;\s]+//i; # my mysql version is 4.1.11
|
||||
s/ROW_FORMAT=[^;\s]+//i; # my mysql version is 5.0.22
|
||||
s/KEY_BLOCK_SIZE=8//i;
|
||||
s/MIN_ROWS=[^;\s]+//i;
|
||||
s/MAX_ROWS=[^;\s]+//i;
|
||||
s/AVG_ROW_LENGTH=[^;\s]+//i;
|
||||
199
data-sources/wikipedia-wikidata/wikidata_place_type_levels.csv
Normal file
199
data-sources/wikipedia-wikidata/wikidata_place_type_levels.csv
Normal file
@@ -0,0 +1,199 @@
|
||||
place_type,level
|
||||
Q9842,4
|
||||
Q9430,3
|
||||
Q928830,4
|
||||
Q9259,1
|
||||
Q91028,5
|
||||
Q8514,2
|
||||
Q8502,2
|
||||
Q83405,3
|
||||
Q82794,2
|
||||
Q820477,1
|
||||
Q811979,1
|
||||
Q8072,2
|
||||
Q79007,2
|
||||
Q786014,3
|
||||
Q75848,2
|
||||
Q75520,2
|
||||
Q728937,4
|
||||
Q7275,2
|
||||
Q719456,3
|
||||
Q7075,3
|
||||
Q697295,4
|
||||
Q6852233,2
|
||||
Q682943,3
|
||||
Q665487,5
|
||||
Q655686,3
|
||||
Q643589,5
|
||||
Q641226,2
|
||||
Q631305,2
|
||||
Q6256,2
|
||||
Q6023295,2
|
||||
Q5773747,5
|
||||
Q56061,1
|
||||
Q55659167,4
|
||||
Q55488,4
|
||||
Q55465477,3
|
||||
Q54050,2
|
||||
Q532,3
|
||||
Q53060,2
|
||||
Q52177058,4
|
||||
Q515716,5
|
||||
Q5153984,4
|
||||
Q515,3
|
||||
Q5144960,5
|
||||
Q5119,4
|
||||
Q5119,4
|
||||
Q5107,2
|
||||
Q5084,4
|
||||
Q5031071,4
|
||||
Q5003624,2
|
||||
Q4989906,1
|
||||
Q4976993,3
|
||||
Q486972,1
|
||||
Q486972,2
|
||||
Q483110,3
|
||||
Q4830453,4
|
||||
Q47521,3
|
||||
Q473972,1
|
||||
Q46831,2
|
||||
Q46614560,5
|
||||
Q44782,3
|
||||
Q44613,4
|
||||
Q44539,4
|
||||
Q44494,2
|
||||
Q44377,2
|
||||
Q4421,2
|
||||
Q43501,2
|
||||
Q4286337,3
|
||||
Q42523,3
|
||||
Q41176,2
|
||||
Q40357,3
|
||||
Q4022,4
|
||||
Q40080,2
|
||||
Q39816,2
|
||||
Q39715,3
|
||||
Q39614,1
|
||||
Q3957,3
|
||||
Q3947,4
|
||||
Q3914,3
|
||||
Q38723,2
|
||||
Q38720,3
|
||||
Q3623867,5
|
||||
Q35666,2
|
||||
Q355304,3
|
||||
Q35509,2
|
||||
Q35112127,3
|
||||
Q34985575,4
|
||||
Q34876,5
|
||||
Q34763,2
|
||||
Q34627,4
|
||||
Q3455524,3
|
||||
Q34442,4
|
||||
Q33837,2
|
||||
Q33506,3
|
||||
Q32815,4
|
||||
Q3257686,2
|
||||
Q3240715,2
|
||||
Q3191695,5
|
||||
Q3153117,2
|
||||
Q30198,2
|
||||
Q30139652,3
|
||||
Q294422,3
|
||||
Q2870166,3
|
||||
Q27686,3
|
||||
Q274153,3
|
||||
Q271669,1
|
||||
Q2659904,2
|
||||
Q24529780,2
|
||||
Q24354,3
|
||||
Q2354973,4
|
||||
Q23442,2
|
||||
Q23413,3
|
||||
Q23397,3
|
||||
Q2327515,4
|
||||
Q2311958,5
|
||||
Q22927291,6
|
||||
Q22698,1
|
||||
Q2175765,4
|
||||
Q205495,4
|
||||
Q204832,3
|
||||
Q2042028,2
|
||||
Q202216,6
|
||||
Q1970725,3
|
||||
Q194203,5
|
||||
Q194195,2
|
||||
Q190429,2
|
||||
Q185187,3
|
||||
Q185113,2
|
||||
Q183366,2
|
||||
Q1799794,1
|
||||
Q1788454,4
|
||||
Q1785071,3
|
||||
Q1777138,3
|
||||
Q177634,2
|
||||
Q177380,2
|
||||
Q174814,4
|
||||
Q174782,2
|
||||
Q17350442,2
|
||||
Q17343829,3
|
||||
Q17334923,0
|
||||
Q17018380,3
|
||||
Q16970,4
|
||||
Q16917,3
|
||||
Q16831714,4
|
||||
Q165,3
|
||||
Q160742,4
|
||||
Q159719,3
|
||||
Q159334,4
|
||||
Q15640612,5
|
||||
Q15324,2
|
||||
Q15284,5
|
||||
Q15243209,6
|
||||
Q152081,1
|
||||
Q15195406,4
|
||||
Q1500350,5
|
||||
Q149621,5
|
||||
Q14757767,4
|
||||
Q14350,3
|
||||
Q1410668,3
|
||||
Q1394476,3
|
||||
Q1377575,2
|
||||
Q1353183,3
|
||||
Q134447,4
|
||||
Q133215,3
|
||||
Q133056,2
|
||||
Q13221722,3
|
||||
Q13220204,2
|
||||
Q1311958,4
|
||||
Q1303167,3
|
||||
Q130003,3
|
||||
Q12518,2
|
||||
Q12516,3
|
||||
Q1248784,3
|
||||
Q123705,3
|
||||
Q12323,3
|
||||
Q12284,4
|
||||
Q12280,4
|
||||
Q121359,2
|
||||
Q1210950,2
|
||||
Q11755880,3
|
||||
Q11707,3
|
||||
Q11315,3
|
||||
Q11303,3
|
||||
Q1115575,4
|
||||
Q1107656,1
|
||||
Q10864048,1
|
||||
Q1076486,2
|
||||
Q105731,3
|
||||
Q105190,3
|
||||
Q1048525,3
|
||||
Q102496,5
|
||||
Q28872924,1
|
||||
Q15617994,1
|
||||
Q159313,2
|
||||
Q24398318,3
|
||||
Q327333,2
|
||||
Q43229,1
|
||||
Q860861,1
|
||||
Q4989906,1
|
||||
|
195
data-sources/wikipedia-wikidata/wikidata_place_types.txt
Normal file
195
data-sources/wikipedia-wikidata/wikidata_place_types.txt
Normal file
@@ -0,0 +1,195 @@
|
||||
Q9842
|
||||
Q9430
|
||||
Q928830
|
||||
Q9259
|
||||
Q91028
|
||||
Q8514
|
||||
Q8502
|
||||
Q83405
|
||||
Q82794
|
||||
Q820477
|
||||
Q811979
|
||||
Q8072
|
||||
Q79007
|
||||
Q786014
|
||||
Q75848
|
||||
Q75520
|
||||
Q728937
|
||||
Q7275
|
||||
Q719456
|
||||
Q7075
|
||||
Q697295
|
||||
Q6852233
|
||||
Q682943
|
||||
Q665487
|
||||
Q655686
|
||||
Q643589
|
||||
Q641226
|
||||
Q631305
|
||||
Q6256
|
||||
Q6023295
|
||||
Q5773747
|
||||
Q56061
|
||||
Q55659167
|
||||
Q55488
|
||||
Q55465477
|
||||
Q54050
|
||||
Q532
|
||||
Q53060
|
||||
Q52177058
|
||||
Q515716
|
||||
Q5153984
|
||||
Q515
|
||||
Q5144960
|
||||
Q5119
|
||||
Q5107
|
||||
Q5084
|
||||
Q5031071
|
||||
Q5003624
|
||||
Q4989906
|
||||
Q4976993
|
||||
Q486972
|
||||
Q483110
|
||||
Q4830453
|
||||
Q47521
|
||||
Q473972
|
||||
Q46831
|
||||
Q46614560
|
||||
Q44782
|
||||
Q44613
|
||||
Q44539
|
||||
Q44494
|
||||
Q44377
|
||||
Q4421
|
||||
Q43501
|
||||
Q4286337
|
||||
Q42523
|
||||
Q41176
|
||||
Q40357
|
||||
Q4022
|
||||
Q40080
|
||||
Q39816
|
||||
Q39715
|
||||
Q39614
|
||||
Q3957
|
||||
Q3947
|
||||
Q3914
|
||||
Q38723
|
||||
Q38720
|
||||
Q3623867
|
||||
Q35666
|
||||
Q355304
|
||||
Q35509
|
||||
Q35112127
|
||||
Q34985575
|
||||
Q34876
|
||||
Q34763
|
||||
Q34627
|
||||
Q3455524
|
||||
Q34442
|
||||
Q33837
|
||||
Q33506
|
||||
Q32815
|
||||
Q3257686
|
||||
Q3240715
|
||||
Q3191695
|
||||
Q3153117
|
||||
Q30198
|
||||
Q30139652
|
||||
Q294422
|
||||
Q2870166
|
||||
Q27686
|
||||
Q274153
|
||||
Q271669
|
||||
Q2659904
|
||||
Q24529780
|
||||
Q24354
|
||||
Q2354973
|
||||
Q23442
|
||||
Q23413
|
||||
Q23397
|
||||
Q2327515
|
||||
Q2311958
|
||||
Q22927291
|
||||
Q22698
|
||||
Q2175765
|
||||
Q205495
|
||||
Q204832
|
||||
Q2042028
|
||||
Q202216
|
||||
Q1970725
|
||||
Q194203
|
||||
Q194195
|
||||
Q190429
|
||||
Q185187
|
||||
Q185113
|
||||
Q183366
|
||||
Q1799794
|
||||
Q1788454
|
||||
Q1785071
|
||||
Q1777138
|
||||
Q177634
|
||||
Q177380
|
||||
Q174814
|
||||
Q174782
|
||||
Q17350442
|
||||
Q17343829
|
||||
Q17334923
|
||||
Q17018380
|
||||
Q16970
|
||||
Q16917
|
||||
Q16831714
|
||||
Q165
|
||||
Q160742
|
||||
Q159719
|
||||
Q159334
|
||||
Q15640612
|
||||
Q15324
|
||||
Q15284
|
||||
Q15243209
|
||||
Q152081
|
||||
Q15195406
|
||||
Q1500350
|
||||
Q149621
|
||||
Q14757767
|
||||
Q14350
|
||||
Q1410668
|
||||
Q1394476
|
||||
Q1377575
|
||||
Q1353183
|
||||
Q134447
|
||||
Q133215
|
||||
Q133056
|
||||
Q13221722
|
||||
Q13220204
|
||||
Q1311958
|
||||
Q1303167
|
||||
Q130003
|
||||
Q12518
|
||||
Q12516
|
||||
Q1248784
|
||||
Q123705
|
||||
Q12323
|
||||
Q12284
|
||||
Q12280
|
||||
Q121359
|
||||
Q1210950
|
||||
Q11755880
|
||||
Q11707
|
||||
Q11315
|
||||
Q11303
|
||||
Q1115575
|
||||
Q1107656
|
||||
Q10864048
|
||||
Q1076486
|
||||
Q105731
|
||||
Q105190
|
||||
Q1048525
|
||||
Q102496
|
||||
Q28872924
|
||||
Q15617994
|
||||
Q159313
|
||||
Q24398318
|
||||
Q327333
|
||||
Q43229
|
||||
Q860861
|
||||
200
data-sources/wikipedia-wikidata/wikidata_places.md
Normal file
200
data-sources/wikipedia-wikidata/wikidata_places.md
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
## Wikidata place types and related OSM Tags
|
||||
|
||||
Wikidata does not have any official ontologies, however the [DBpedia project](https://wiki.dbpedia.org/) has created an [ontology](https://wiki.dbpedia.org/services-resources/ontology) that covered [place types](http://mappings.dbpedia.org/server/ontology/classes/#Place). The table below used the DBpedia place ontology as a starting point, and is provided as a cross-reference to the relevant OSM tags.
|
||||
|
||||
The Wikidata place types listed in the table below can be used in conjunction with the [Wikidata Query Service](https://query.wikidata.org/) to retrieve instances of those place types from the Wikidata knowledgebase.
|
||||
|
||||
```
|
||||
SELECT ?item ?lat ?lon
|
||||
WHERE {
|
||||
?item wdt:P31*/wdt:P279*wd:Q9430; wdt:P625 ?pt.
|
||||
?item p:P625?loc.
|
||||
?loc psv:P625?cnode.
|
||||
?cnode wikibase:geoLatitude?lat.
|
||||
?cnode wikibase:geoLongitude?lon.
|
||||
}
|
||||
```
|
||||
|
||||
An example json return for all instances of the Wikidata item "Q9430" (Ocean) can be seen at [json](https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=SELECT?item?lat?lon%20WHERE{?item%20wdt:P31*/wdt:P279*wd:Q9430;wdt:P625?pt.?item%20p:P625?loc.?loc%20psv:P625?cnode.?cnode%20wikibase:geoLatitude?lat.?cnode%20wikibase:geoLongitude?lon.})
|
||||
|
||||
**NOTE** the OSM tags listed are those listed in the wikidata entries, and not all the possible matches for tags within OSM.
|
||||
|
||||
|
||||
title | concept | OSM Tag |
|
||||
-----------|---------------------------------------|------------------|
|
||||
[Q17334923](https://www.wikidata.org/entity/Q17334923) | Location | |
|
||||
[Q811979](https://www.wikidata.org/entity/Q811979) | Architectural Structure | |
|
||||
[Q194195](https://www.wikidata.org/entity/Q194195) | Amusement park |
|
||||
[Q204832](https://www.wikidata.org/entity/Q204832) | Roller coaster | [attraction=roller_coaster](https://wiki.openstreetmap.org/wiki/Tag:attraction=roller_coaster) |
|
||||
[Q2870166](https://www.wikidata.org/entity/Q2870166) | Water ride | |
|
||||
[Q641226](https://www.wikidata.org/entity/Q641226) | Arena | [amenity=events_centre](https://wiki.openstreetmap.org/wiki/Tag:amenity=events_centre) |
|
||||
[Q41176](https://www.wikidata.org/entity/Q41176) | Building | [building=yes](https://wiki.openstreetmap.org/wiki/Key:building) |
|
||||
[Q1303167](https://www.wikidata.org/entity/Q1303167) | Barn | [building=barn](https://wiki.openstreetmap.org/wiki/Tag:building=barn) |
|
||||
[Q655686](https://www.wikidata.org/entity/Q655686) | Commercial building | [building=commercial](https://wiki.openstreetmap.org/wiki/Tag:building=commercial) |
|
||||
[Q4830453](https://www.wikidata.org/entity/Q4830453) | Business | |
|
||||
[Q7075](https://www.wikidata.org/entity/Q7075) | Library | [amenity=library](https://wiki.openstreetmap.org/wiki/Tag:amenity=library) |
|
||||
[Q133215](https://www.wikidata.org/entity/Q133215) | Casino | [amenity=casino](https://wiki.openstreetmap.org/wiki/Tag:amenity=casino) |
|
||||
[Q23413](https://www.wikidata.org/entity/Q23413) | Castle | [historic=castle](https://wiki.openstreetmap.org/wiki/Tag:historic=castle) |
|
||||
[Q83405](https://www.wikidata.org/entity/Q83405) | Factory | |
|
||||
[Q53060](https://www.wikidata.org/entity/Q53060) | Gate | [barrier=gate](https://wiki.openstreetmap.org/wiki/Tag:barrier=gate) |cnode%20wikibase:geoLatitude?lat.?cnode%20wikibase:geoLongitude?lon.})
|
||||
[Q11755880](https://www.wikidata.org/entity/Q11755880) | Residential Building | [building=residential](https://wiki.openstreetmap.org/wiki/Tag:building=residential) |
|
||||
[Q3947](https://www.wikidata.org/entity/Q3947) | House | [building=house](https://wiki.openstreetmap.org/wiki/Tag:building=house) |
|
||||
[Q35112127](https://www.wikidata.org/entity/Q35112127) | Historic Building | |
|
||||
[Q5773747](https://www.wikidata.org/entity/Q5773747) | Historic house | |
|
||||
[Q38723](https://www.wikidata.org/entity/Q38723) | Higher Education Institution |
|
||||
[Q3914](https://www.wikidata.org/entity/Q3914) | School | [amenity=school](https://wiki.openstreetmap.org/wiki/Tag:amenity=school) |
|
||||
[Q9842](https://www.wikidata.org/entity/Q9842) | Primary school | |
|
||||
[Q159334](https://www.wikidata.org/entity/Q159334) | Secondary school | |
|
||||
[Q16917](https://www.wikidata.org/entity/Q16917) | Hospital | [amenity=hospital](https://wiki.openstreetmap.org/wiki/Tag:amenity=hospital), [healthcare=hospital](https://wiki.openstreetmap.org/wiki/Tag:healthcare=hospital), [building=hospital](https://wiki.openstreetmap.org/wiki/Tag:building=hospital) |
|
||||
[Q27686](https://www.wikidata.org/entity/Q27686) | Hotel | [tourism=hotel](https://wiki.openstreetmap.org/wiki/Tag:tourism=hotel), [building=hotel](https://wiki.openstreetmap.org/wiki/Tag:building=hotel) |
|
||||
[Q33506](https://www.wikidata.org/entity/Q33506) | Museum | [tourism=museum](https://wiki.openstreetmap.org/wiki/Tag:tourism=museum) |
|
||||
[Q40357](https://www.wikidata.org/entity/Q40357) | Prison | [amenity=prison](https://wiki.openstreetmap.org/wiki/Tag:amenity=prison) |
|
||||
[Q24398318](https://www.wikidata.org/entity/Q24398318) | Religious Building | |
|
||||
[Q160742](https://www.wikidata.org/entity/Q160742) | Abbey | |
|
||||
[Q16970](https://www.wikidata.org/entity/Q16970) | Church (building) | [building=church](https://wiki.openstreetmap.org/wiki/Tag:building=church) |
|
||||
[Q44613](https://www.wikidata.org/entity/Q44613) | Monastery | [amenity=monastery](https://wiki.openstreetmap.org/wiki/Tag:amenity=monastery) |
|
||||
[Q32815](https://www.wikidata.org/entity/Q32815) | Mosque | [building=mosque](https://wiki.openstreetmap.org/wiki/Tag:building=mosque) |
|
||||
[Q697295](https://www.wikidata.org/entity/Q697295) | Shrine | [building=shrine](https://wiki.openstreetmap.org/wiki/Tag:building=shrine) |
|
||||
[Q34627](https://www.wikidata.org/entity/Q34627) | Synagogue | [building=synagogue](https://wiki.openstreetmap.org/wiki/Tag:building=synagogue) |
|
||||
[Q44539](https://www.wikidata.org/entity/Q44539) | Temple | [building=temple](https://wiki.openstreetmap.org/wiki/Tag:building=temple) |
|
||||
[Q11707](https://www.wikidata.org/entity/Q11707) | Restaurant | [amenity=restaurant](https://wiki.openstreetmap.org/wiki/Tag:amenity=restaurant) |
|
||||
[Q11315](https://www.wikidata.org/entity/Q11315) | Shopping mall | [shop=mall](https://wiki.openstreetmap.org/wiki/Tag:shop=mall), [shop=shopping_centre](https://wiki.openstreetmap.org/wiki/Tag:shop=shopping_centre) |
|
||||
[Q11303](https://www.wikidata.org/entity/Q11303) | Skyscraper | |
|
||||
[Q17350442](https://www.wikidata.org/entity/Q17350442) | Venue | |
|
||||
[Q41253](https://www.wikidata.org/entity/Q41253) | Movie Theater | [amenity=cinema](https://wiki.openstreetmap.org/wiki/Tag:amenity=cinema) |
|
||||
[Q483110](https://www.wikidata.org/entity/Q483110) | Stadium | [leisure=stadium](https://wiki.openstreetmap.org/wiki/Tag:leisure=stadium), [building=stadium](https://wiki.openstreetmap.org/wiki/Tag:building=stadium) |
|
||||
[Q24354](https://www.wikidata.org/entity/Q24354) | Theater (structure) | [amenity=theatre](https://wiki.openstreetmap.org/wiki/Tag:amenity=theatre) |
|
||||
[Q121359](https://www.wikidata.org/entity/Q121359) | Infrastructure | |
|
||||
[Q1248784](https://www.wikidata.org/entity/Q1248784) | Airport | |
|
||||
[Q12323](https://www.wikidata.org/entity/Q12323) | Dam | [waterway=dam](https://wiki.openstreetmap.org/wiki/Tag:waterway=dam) |
|
||||
[Q1353183](https://www.wikidata.org/entity/Q1353183) | Launch pad | |
|
||||
[Q105190](https://www.wikidata.org/entity/Q105190) | Levee | [man_made=dyke](https://wiki.openstreetmap.org/wiki/Tag:man_made=dyke) |
|
||||
[Q105731](https://www.wikidata.org/entity/Q105731) | Lock (water navigation) | [lock=yes](https://wiki.openstreetmap.org/wiki/Key:lock) |
|
||||
[Q44782](https://www.wikidata.org/entity/Q44782) | Port | |
|
||||
[Q159719](https://www.wikidata.org/entity/Q159719) | Power station | [power=plant](https://wiki.openstreetmap.org/wiki/Tag:power=plant) |
|
||||
[Q174814](https://www.wikidata.org/entity/Q174814) | Electrical substation | |
|
||||
[Q134447](https://www.wikidata.org/entity/Q134447) | Nuclear power plant | [plant:source=nuclear](https://wiki.openstreetmap.org/wiki/Tag:plant:source=nuclear) |
|
||||
[Q786014](https://www.wikidata.org/entity/Q786014) | Rest area | [highway=rest_area](https://wiki.openstreetmap.org/wiki/Tag:highway=rest_area), [highway=services](https://wiki.openstreetmap.org/wiki/Tag:highway=services) |
|
||||
[Q12280](https://www.wikidata.org/entity/Q12280) | Bridge | [bridge=* ](https://wiki.openstreetmap.org/wiki/Key:bridge), [man_made=bridge](https://wiki.openstreetmap.org/wiki/Tag:man_made=bridge) |
|
||||
[Q728937](https://www.wikidata.org/entity/Q728937) | Railroad Line | [railway=rail](https://wiki.openstreetmap.org/wiki/Tag:railway=rail) |
|
||||
[Q1311958](https://www.wikidata.org/entity/Q1311958) | Railway Tunnel | |
|
||||
[Q34442](https://www.wikidata.org/entity/Q34442) | Road | [highway=* ](https://wiki.openstreetmap.org/wiki/Key:highway), [route=road](https://wiki.openstreetmap.org/wiki/Tag:route=road) |
|
||||
[Q1788454](https://www.wikidata.org/entity/Q1788454) | Road junction | |
|
||||
[Q44377](https://www.wikidata.org/entity/Q44377) | Tunnel | [tunnel=* ](https://wiki.openstreetmap.org/wiki/Key:tunnel) |
|
||||
[Q5031071](https://www.wikidata.org/entity/Q5031071) | Canal tunnel | |
|
||||
[Q719456](https://www.wikidata.org/entity/Q719456) | Station | [public_transport=station](https://wiki.openstreetmap.org/wiki/Tag:public_transport=station) |
|
||||
[Q205495](https://www.wikidata.org/entity/Q205495) | Filling station | [amenity=fuel](https://wiki.openstreetmap.org/wiki/Tag:amenity=fuel) |
|
||||
[Q928830](https://www.wikidata.org/entity/Q928830) | Metro station | [station=subway](https://wiki.openstreetmap.org/wiki/Tag:station=subway) |
|
||||
[Q55488](https://www.wikidata.org/entity/Q55488) | Train station | [railway=station](https://wiki.openstreetmap.org/wiki/Tag:railway=station) |
|
||||
[Q2175765](https://www.wikidata.org/entity/Q2175765) | Tram stop | [railway=tram_stop](https://wiki.openstreetmap.org/wiki/Tag:railway=tram_stop), [public_transport=stop_position](https://wiki.openstreetmap.org/wiki/Tag:public_transport=stop_position) |
|
||||
[Q6852233](https://www.wikidata.org/entity/Q6852233) | Military building | |
|
||||
[Q44494](https://www.wikidata.org/entity/Q44494) | Mill (grinding) | |
|
||||
[Q185187](https://www.wikidata.org/entity/Q185187) | Watermill | [man_made=watermill](https://wiki.openstreetmap.org/wiki/Tag:man_made=watermill) |
|
||||
[Q38720](https://www.wikidata.org/entity/Q38720) | Windmill | [man_made=windmill](https://wiki.openstreetmap.org/wiki/Tag:man_made=windmill) |
|
||||
[Q4989906](https://www.wikidata.org/entity/Q4989906) | Monument | [historic=monument](https://wiki.openstreetmap.org/wiki/Tag:historic=monument) |
|
||||
[Q5003624](https://www.wikidata.org/entity/Q5003624) | Memorial | [historic=memorial](https://wiki.openstreetmap.org/wiki/Tag:historic=memorial) |
|
||||
[Q271669](https://www.wikidata.org/entity/Q271669) | Landform | |
|
||||
[Q190429](https://www.wikidata.org/entity/Q190429) | Depression (geology) | |
|
||||
[Q17018380](https://www.wikidata.org/entity/Q17018380) | Bight (geography) | |
|
||||
[Q54050](https://www.wikidata.org/entity/Q54050) | Hill | |
|
||||
[Q1210950](https://www.wikidata.org/entity/Q1210950) | Channel (geography) | |
|
||||
[Q23442](https://www.wikidata.org/entity/Q23442) | Island | [place=island](https://wiki.openstreetmap.org/wiki/Tag:place=island) |
|
||||
[Q42523](https://www.wikidata.org/entity/Q42523) | Atoll | |
|
||||
[Q34763](https://www.wikidata.org/entity/Q34763) | Peninsula | |
|
||||
[Q355304](https://www.wikidata.org/entity/Q355304) | Watercourse | |
|
||||
[Q30198](https://www.wikidata.org/entity/Q30198) | Marsh | [wetland=marsh](https://wiki.openstreetmap.org/wiki/Tag:wetland=marsh) |
|
||||
[Q75520](https://www.wikidata.org/entity/Q75520) | Plateau | |
|
||||
[Q2042028](https://www.wikidata.org/entity/Q2042028) | Ravine | |
|
||||
[Q631305](https://www.wikidata.org/entity/Q631305) | Rock formation | |
|
||||
[Q12516](https://www.wikidata.org/entity/Q12516) | Pyramid | |
|
||||
[Q1076486](https://www.wikidata.org/entity/Q1076486) | Sports venue | |
|
||||
[Q682943](https://www.wikidata.org/entity/Q682943) | Cricket field | [sport=cricket](https://wiki.openstreetmap.org/wiki/Tag:sport=cricket) |
|
||||
[Q1048525](https://www.wikidata.org/entity/Q1048525) | Golf course | [leisure=golf_course](https://wiki.openstreetmap.org/wiki/Tag:leisure=golf_course) |
|
||||
[Q1777138](https://www.wikidata.org/entity/Q1777138) | Race track | [highway=raceway](https://wiki.openstreetmap.org/wiki/Tag:highway=raceway) |
|
||||
[Q130003](https://www.wikidata.org/entity/Q130003) | Ski resort | |
|
||||
[Q174782](https://www.wikidata.org/entity/Q174782) | Town square | [place=square](https://wiki.openstreetmap.org/wiki/Tag:place=square) |
|
||||
[Q12518](https://www.wikidata.org/entity/Q12518) | Tower | [building=tower](https://wiki.openstreetmap.org/wiki/Tag:building=tower), [man_made=tower](https://wiki.openstreetmap.org/wiki/Tag:man_made=tower) |
|
||||
[Q39715](https://www.wikidata.org/entity/Q39715) | Lighthouse | [man_made=lighthouse](https://wiki.openstreetmap.org/wiki/Tag:man_made=lighthouse) |
|
||||
[Q274153](https://www.wikidata.org/entity/Q274153) | Water tower | [building=water_tower](https://wiki.openstreetmap.org/wiki/Tag:building=water_tower), [man_made=water_tower](https://wiki.openstreetmap.org/wiki/Tag:man_made=water_tower) |
|
||||
[Q43501](https://www.wikidata.org/entity/Q43501) | Zoo | [tourism=zoo](https://wiki.openstreetmap.org/wiki/Tag:tourism=zoo) |
|
||||
[Q39614](https://www.wikidata.org/entity/Q39614) | Cemetery | [amenity=grave_yard](https://wiki.openstreetmap.org/wiki/Tag:amenity=grave_yard), [landuse=cemetery](https://wiki.openstreetmap.org/wiki/Tag:landuse=cemetery) |
|
||||
[Q152081](https://www.wikidata.org/entity/Q152081) | Concentration camp | |
|
||||
[Q1107656](https://www.wikidata.org/entity/Q1107656) | Garden | [leisure=garden](https://wiki.openstreetmap.org/wiki/Tag:leisure=garden) |
|
||||
[Q820477](https://www.wikidata.org/entity/Q820477) | Mine | |
|
||||
[Q33837](https://www.wikidata.org/entity/Q33837) | Archipelago | [place=archipelago](https://wiki.openstreetmap.org/wiki/Tag:place=archipelago) |
|
||||
[Q40080](https://www.wikidata.org/entity/Q40080) | Beach | [natural=beach](https://wiki.openstreetmap.org/wiki/Tag:natural=beach) |
|
||||
[Q15324](https://www.wikidata.org/entity/Q15324) | Body of water | [natural=water](https://wiki.openstreetmap.org/wiki/Tag:natural=water) |
|
||||
[Q23397](https://www.wikidata.org/entity/Q23397) | Lake | [water=lake](https://wiki.openstreetmap.org/wiki/Tag:water=lake) |
|
||||
[Q9430](https://www.wikidata.org/entity/Q9430) | Ocean | |
|
||||
[Q165](https://www.wikidata.org/entity/Q165) | Sea | |
|
||||
[Q47521](https://www.wikidata.org/entity/Q47521) | Stream | |
|
||||
[Q12284](https://www.wikidata.org/entity/Q12284) | Canal | [waterway=canal](https://wiki.openstreetmap.org/wiki/Tag:waterway=canal) |
|
||||
[Q4022](https://www.wikidata.org/entity/Q4022) | River | [waterway=river](https://wiki.openstreetmap.org/wiki/Tag:waterway=river), [type=waterway](https://wiki.openstreetmap.org/wiki/Relation:waterway) |
|
||||
[Q185113](https://www.wikidata.org/entity/Q185113) | Cape | [natural=cape](https://wiki.openstreetmap.org/wiki/Tag:natural=cape) |
|
||||
[Q35509](https://www.wikidata.org/entity/Q35509) | Cave | [natural=cave_entrance](https://wiki.openstreetmap.org/wiki/Tag:natural=cave_entrance) |
|
||||
[Q8514](https://www.wikidata.org/entity/Q8514) | Desert | |
|
||||
[Q4421](https://www.wikidata.org/entity/Q4421) | Forest | [natural=wood](https://wiki.openstreetmap.org/wiki/Tag:natural=wood) |
|
||||
[Q35666](https://www.wikidata.org/entity/Q35666) | Glacier | [natural=glacier](https://wiki.openstreetmap.org/wiki/Tag:natural=glacier) |
|
||||
[Q177380](https://www.wikidata.org/entity/Q177380) | Hot spring | |
|
||||
[Q8502](https://www.wikidata.org/entity/Q8502) | Mountain | [natural=peak](https://wiki.openstreetmap.org/wiki/Tag:natural=peak) |
|
||||
[Q133056](https://www.wikidata.org/entity/Q133056) | Mountain pass | |
|
||||
[Q46831](https://www.wikidata.org/entity/Q46831) | Mountain range | |
|
||||
[Q39816](https://www.wikidata.org/entity/Q39816) | Valley | [natural=valley](https://wiki.openstreetmap.org/wiki/Tag:natural=valley) |
|
||||
[Q8072](https://www.wikidata.org/entity/Q8072) | Volcano | [natural=volcano](https://wiki.openstreetmap.org/wiki/Tag:natural=volcano) |
|
||||
[Q43229](https://www.wikidata.org/entity/Q43229) | Organization | |
|
||||
[Q327333](https://www.wikidata.org/entity/Q327333) | Government agency | [office=government](https://wiki.openstreetmap.org/wiki/Tag:office=government)|
|
||||
[Q22698](https://www.wikidata.org/entity/Q22698) | Park | [leisure=park](https://wiki.openstreetmap.org/wiki/Tag:leisure=park) |
|
||||
[Q159313](https://www.wikidata.org/entity/Q159313) | Urban agglomeration | |
|
||||
[Q177634](https://www.wikidata.org/entity/Q177634) | Community | |
|
||||
[Q5107](https://www.wikidata.org/entity/Q5107) | Continent | [place=continent](https://wiki.openstreetmap.org/wiki/Tag:place=continent) |
|
||||
[Q6256](https://www.wikidata.org/entity/Q6256) | Country | [place=country](https://wiki.openstreetmap.org/wiki/Tag:place=country) |
|
||||
[Q75848](https://www.wikidata.org/entity/Q75848) | Gated community | |
|
||||
[Q3153117](https://www.wikidata.org/entity/Q3153117) | Intercommunality | |
|
||||
[Q82794](https://www.wikidata.org/entity/Q82794) | Region | |
|
||||
[Q56061](https://www.wikidata.org/entity/Q56061) | Administrative division | [boundary=administrative](https://wiki.openstreetmap.org/wiki/Tag:boundary=administrative) |
|
||||
[Q665487](https://www.wikidata.org/entity/Q665487) | Diocese | |
|
||||
[Q4976993](https://www.wikidata.org/entity/Q4976993) | Parish | [boundary=civil_parish](https://wiki.openstreetmap.org/wiki/Tag:boundary=civil_parish) |
|
||||
[Q194203](https://www.wikidata.org/entity/Q194203) | Arrondissements of France | |
|
||||
[Q91028](https://www.wikidata.org/entity/Q91028) | Arrondissements of Belgium | |
|
||||
[Q3623867](https://www.wikidata.org/entity/Q3623867) | Arrondissements of Benin | |
|
||||
[Q2311958](https://www.wikidata.org/entity/Q2311958) | Canton (country subdivision) | [political_division=canton](https://wiki.openstreetmap.org/wiki/FR:Cantons_in_France) |
|
||||
[Q643589](https://www.wikidata.org/entity/Q643589) | Department | |
|
||||
[Q202216](https://www.wikidata.org/entity/Q202216) | Overseas department and region | |
|
||||
[Q149621](https://www.wikidata.org/entity/Q149621) | District | [place=district](https://wiki.openstreetmap.org/wiki/Tag:place=district) |
|
||||
[Q15243209](https://www.wikidata.org/wiki/Q15243209) | Historic district | |
|
||||
[Q5144960](https://www.wikidata.org/entity/Q5144960) | Microregion | |
|
||||
[Q15284](https://www.wikidata.org/entity/Q15284) | Municipality | |
|
||||
[Q515716](https://www.wikidata.org/entity/Q515716) | Prefecture | |
|
||||
[Q34876](https://www.wikidata.org/entity/Q34876) | Province | |
|
||||
[Q3191695](https://www.wikidata.org/entity/Q3191695) | Regency (Indonesia) | |
|
||||
[Q1970725](https://www.wikidata.org/entity/Q1970725) | Natural region | |
|
||||
[Q486972](https://www.wikidata.org/entity/Q486972) | Human settlement | |
|
||||
[Q515](https://www.wikidata.org/entity/Q515) | City | [place=city](https://wiki.openstreetmap.org/wiki/Tag:place=city) |
|
||||
[Q5119](https://www.wikidata.org/entity/Q5119) | Capital city | [capital=yes](https://wiki.openstreetmap.org/wiki/Key:capital) |
|
||||
[Q4286337](https://www.wikidata.org/entity/Q4286337) | City district | |
|
||||
[Q1394476](https://www.wikidata.org/entity/Q1394476) | Civil township | |
|
||||
[Q1115575](https://www.wikidata.org/entity/Q1115575) | Civil parish | [designation=civil_parish](https://wiki.openstreetmap.org/wiki/Tag:designation=civil_parish) |
|
||||
[Q5153984](https://www.wikidata.org/entity/Q5153984) | Commune-level subdivisions | |
|
||||
[Q123705](https://www.wikidata.org/entity/Q123705) | Neighbourhood | [place=neighbourhood](https://wiki.openstreetmap.org/wiki/Tag:place=neighbourhood) |
|
||||
[Q1500350](https://www.wikidata.org/entity/Q1500350) | Townships of China | |
|
||||
[Q17343829](https://www.wikidata.org/entity/Q17343829) | Unincorporated Community | |
|
||||
[Q3957](https://www.wikidata.org/entity/Q3957) | Town | [place=town](https://wiki.openstreetmap.org/wiki/Tag:place=town) |
|
||||
[Q532](https://www.wikidata.org/entity/Q532) | Village | [place=village](https://wiki.openstreetmap.org/wiki/Tag:place=village) |
|
||||
[Q5084](https://www.wikidata.org/entity/Q5084) | Hamlet | [place=hamlet](https://wiki.openstreetmap.org/wiki/Tag:place=hamlet) |
|
||||
[Q7275](https://www.wikidata.org/entity/Q7275) | State | |
|
||||
[Q79007](https://www.wikidata.org/entity/Q79007) | Street | |
|
||||
[Q473972](https://www.wikidata.org/entity/Q473972) | Protected area | [boundary=protected_area](https://wiki.openstreetmap.org/wiki/Tag:boundary=protected_area) |
|
||||
[Q1377575](https://www.wikidata.org/entity/Q1377575) | Wildlife refuge | |
|
||||
[Q1410668](https://www.wikidata.org/entity/Q1410668) | National Wildlife Refuge | [protection_title=National Wildlife Refuge](ownership=national), [ownership=national](https://wiki.openstreetmap.org/wiki/Tag:ownership=national)|
|
||||
[Q9259](https://www.wikidata.org/entity/Q9259) | World Heritage Site | |
|
||||
|
||||
---
|
||||
|
||||
### Future Work
|
||||
|
||||
The Wikidata improvements to Nominatim can be further enhanced by:
|
||||
|
||||
- continuing to add new Wikidata links to OSM objects
|
||||
- increasing the number of place types accounted for in the wikipedia_articles table
|
||||
- working to use place types in the wikipedia_article matching process
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
32430
data/us_postcode.sql
32430
data/us_postcode.sql
File diff suppressed because it is too large
Load Diff
16
data/us_postcode_table.sql
Normal file
16
data/us_postcode_table.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
SET statement_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET check_function_bodies = false;
|
||||
SET client_min_messages = warning;
|
||||
|
||||
SET search_path = public, pg_catalog;
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_with_oids = false;
|
||||
|
||||
CREATE TABLE us_postcode (
|
||||
postcode text,
|
||||
x double precision,
|
||||
y double precision
|
||||
);
|
||||
2930
data/us_state.sql
2930
data/us_state.sql
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
79349
data/words.sql
79349
data/words.sql
File diff suppressed because it is too large
Load Diff
28
docs/CMakeLists.txt
Normal file
28
docs/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
# Auto-generated vagrant install documentation
|
||||
|
||||
|
||||
# build the actual documentation
|
||||
|
||||
configure_file(mkdocs.yml ../mkdocs.yml)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data-sources)
|
||||
|
||||
ADD_CUSTOM_TARGET(doc
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/admin ${CMAKE_CURRENT_BINARY_DIR}/admin
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/develop ${CMAKE_CURRENT_BINARY_DIR}/develop
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/api ${CMAKE_CURRENT_BINARY_DIR}/api
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/index.md ${CMAKE_CURRENT_BINARY_DIR}/index.md
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/extra.css ${CMAKE_CURRENT_BINARY_DIR}/extra.css
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/data-sources/overview.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/overview.md
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/us-tiger/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/US-Tiger.md
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/gb-postcodes/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/GB-Postcodes.md
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/country-grid/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/Country-Grid.md
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/country-grid/mexico.quad.png ${CMAKE_CURRENT_BINARY_DIR}/data-sources/mexico.quad.png
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/wikipedia-wikidata/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/Wikipedia-Wikidata.md
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Centos-7.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Centos-7.md
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-16.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-16.md
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-18.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-18.md
|
||||
COMMAND mkdocs build -d ${CMAKE_CURRENT_BINARY_DIR}/../site-html -f ${CMAKE_CURRENT_BINARY_DIR}/../mkdocs.yml
|
||||
)
|
||||
|
||||
|
||||
215
docs/admin/Faq.md
Normal file
215
docs/admin/Faq.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Troubleshooting Nominatim Installations
|
||||
|
||||
## Installation Issues
|
||||
|
||||
### Can a stopped/killed import process be resumed?
|
||||
|
||||
"I accidentally killed the import process after it has been running for many hours. Can it be resumed?"
|
||||
|
||||
It is possible if the import already got to the indexing stage.
|
||||
Check the last line of output that was logged before the process
|
||||
was killed. If it looks like this:
|
||||
|
||||
|
||||
Done 844 in 13 @ 64.923080 per second - Rank 26 ETA (seconds): 7.886255
|
||||
|
||||
then you can resume with the following command:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --index --create-search-indices --create-country-names
|
||||
```
|
||||
|
||||
If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`.
|
||||
|
||||
|
||||
### PHP "open_basedir restriction in effect" warnings
|
||||
|
||||
PHP Warning: file_get_contents(): open_basedir restriction in effect.
|
||||
|
||||
You need to adjust the [open_basedir](https://www.php.net/manual/en/ini.core.php#ini.open-basedir) setting
|
||||
in your PHP configuration (`php.ini file`). By default this setting may look like this:
|
||||
|
||||
open_basedir = /srv/http/:/home/:/tmp/:/usr/share/pear/
|
||||
|
||||
Either add reported directories to the list or disable this setting temporarily by
|
||||
dding ";" at the beginning of the line. Don't forget to enable this setting again
|
||||
once you are done with the PHP command line operations.
|
||||
|
||||
|
||||
### PHP timzeone warnings
|
||||
|
||||
The Apache log may contain lots of PHP warnings like this:
|
||||
`PHP Warning: date_default_timezone_set() function.`
|
||||
|
||||
You should set the default time zone as instructed in the warning in
|
||||
your `php.ini` file. Find the entry about timezone and set it to
|
||||
something like this:
|
||||
|
||||
; Defines the default timezone used by the date functions
|
||||
; https://php.net/date.timezone
|
||||
date.timezone = 'America/Denver'
|
||||
|
||||
Or
|
||||
|
||||
```
|
||||
echo "date.timezone = 'America/Denver'" > /etc/php.d/timezone.ini
|
||||
```
|
||||
|
||||
### nominatim.so version mismatch
|
||||
|
||||
When running the import you may get a version mismatch:
|
||||
`COPY_END for place failed: ERROR: incompatible library "/srv/Nominatim/nominatim/build/module/nominatim.so": version mismatch`
|
||||
|
||||
pg_config seems to use bad includes sometimes when multiple versions
|
||||
of PostgreSQL are available in the system. Make sure you remove the
|
||||
server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
|
||||
and recompile (`cmake .. && make`).
|
||||
|
||||
|
||||
## I see the error "ERROR: permission denied for language c"
|
||||
|
||||
`nominatim.so`, written in C, is required to be installed on the database
|
||||
server. Some managed database (cloud) services like Amazon RDS do not allow
|
||||
this. There is currently no work-around other than installing a database
|
||||
on a non-managed machine.
|
||||
|
||||
|
||||
### I see the error: "function transliteration(text) does not exist"
|
||||
|
||||
Reinstall the nominatim functions with `setup.php --create--functions`
|
||||
and check for any errors, e.g. a missing `nominatim.so` file.
|
||||
|
||||
### I see the error: "ERROR: mmap (remap) failed"
|
||||
|
||||
This may be a simple out-of-memory error. Try reducing the memory used
|
||||
for `--osm2pgsql-cache`. Also make sure that overcommitting memory is
|
||||
allowed: `cat /proc/sys/vm/overcommit_memory` should print 0 or 1.
|
||||
|
||||
If you are using a flatnode file, then it may also be that the underlying
|
||||
filesystem does not fully support 'mmap'. A notable candidate is virtualbox's
|
||||
vboxfs.
|
||||
|
||||
### nominatim UPDATE failed: ERROR: buffer 179261 is not owned by resource owner Portal
|
||||
|
||||
Several users [reported this](https://github.com/openstreetmap/Nominatim/issues/1168) during the initial import of the database. It's
|
||||
something PostgreSQL internal Nominatim doesn't control. And PostgreSQL forums
|
||||
suggest it's threading related but definitely some kind of crash of a process.
|
||||
Users reported either rebooting the server, different hardware or just trying
|
||||
the import again worked.
|
||||
|
||||
### The website shows: "Could not get word tokens"
|
||||
|
||||
The server cannot access your database. Add `&debug=1` to your URL
|
||||
to get the full error message.
|
||||
|
||||
|
||||
### On CentOS the website shows "Could not connect to server"
|
||||
|
||||
`could not connect to server: No such file or directory`
|
||||
|
||||
On CentOS v7 the PostgreSQL server is started with `systemd`.
|
||||
Check if `/usr/lib/systemd/system/httpd.service` contains a line `PrivateTmp=true`.
|
||||
If so then Apache cannot see the `/tmp/.s.PGSQL.5432` file. It's a good security feature,
|
||||
so use the [preferred solution](../appendix/Install-on-Centos-7/#adding-selinux-security-settings).
|
||||
|
||||
However, you can solve this the quick and dirty way by commenting out that line and then run
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart httpd
|
||||
|
||||
|
||||
### "must be an array or an object that implements Countable" warning in /usr/share/pear/DB.php
|
||||
|
||||
The warning started with PHP 7.2. Make sure you have at least [version 1.9.3 of PEAR DB](https://github.com/pear/DB/releases)
|
||||
installed.
|
||||
|
||||
### Website reports "DB Error: insufficient permissions"
|
||||
|
||||
The user the webserver, e.g. Apache, runs under needs to have access to the Nominatim database. You can find the user like [this](https://serverfault.com/questions/125865/finding-out-what-user-apache-is-running-as), for default Ubuntu operating system for example it's `www-data`.
|
||||
|
||||
1. Repeat the `createuser` step of the installation instructions.
|
||||
|
||||
2. Give the user permission to existing tables
|
||||
|
||||
```
|
||||
GRANT usage ON SCHEMA public TO "www-data";
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "www-data";
|
||||
```
|
||||
|
||||
### Website reports "Could not load library "nominatim.so"
|
||||
|
||||
Example error message
|
||||
|
||||
```
|
||||
SELECT make_standard_name('3039 E MEADOWLARK LN') [nativecode=ERROR: could not
|
||||
load library "/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so":
|
||||
/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so: cannot open shared
|
||||
object file: Permission denied
|
||||
CONTEXT: PL/pgSQL function make_standard_name(text) line 5 at assignment]
|
||||
```
|
||||
|
||||
The PostgreSQL database, i.e. user `postgres`, needs to have access to that file.
|
||||
|
||||
The permission need to be read & executable by everybody, e.g.
|
||||
|
||||
```
|
||||
-rwxr-xr-x 1 nominatim nominatim 297984 build/module/nominatim.so
|
||||
```
|
||||
|
||||
Try `chmod a+r nominatim.so; chmod a+x nominatim.so`.
|
||||
|
||||
When running SELinux, make sure that the
|
||||
[context is set up correctly](../appendix/Install-on-Centos-7/#adding-selinux-security-settings).
|
||||
|
||||
### Setup.php fails with "DB Error: extension not found"
|
||||
|
||||
Make sure you have the PostgreSQL extensions "hstore" and "postgis" installed.
|
||||
See the installation instruction for a full list of required packages.
|
||||
|
||||
|
||||
### Setup.php reports "Cannot redeclare getDB()"
|
||||
|
||||
`Cannot redeclare getDB() (previously declared in /your/path/Nominatim/lib/db.php:4)`
|
||||
|
||||
The message is a bit misleading as PHP needs to load the file `DB.php` and
|
||||
instead re-loads Nominatim's `db.php`. To solve this make sure you
|
||||
have the [Pear module 'DB'](https://pear.php.net/package/DB/) installed.
|
||||
|
||||
sudo pear install DB
|
||||
|
||||
### I forgot to delete the flatnodes file before starting an import.
|
||||
|
||||
That's fine. For each import the flatnodes file get overwritten.
|
||||
See [https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage]()
|
||||
for more information.
|
||||
|
||||
|
||||
## Running your own instance
|
||||
|
||||
### Can I import multiple countries and keep them up to date?
|
||||
|
||||
You should use the extracts and updates from https://download.geofabrik.de.
|
||||
For the initial import, download the countries you need and merge them.
|
||||
See [OSM Help](https://help.openstreetmap.org/questions/48843/merging-two-or-more-geographical-areas-to-import-two-or-more-osm-files-in-nominatim)
|
||||
for examples how to do that. Use the resulting single osm file when
|
||||
running `setup.php`.
|
||||
|
||||
For updates you need to download the change files for each country
|
||||
once per day and apply them **separately** using
|
||||
|
||||
./utils/update.php --import-diff <filename> --index
|
||||
|
||||
See [this issue](https://github.com/openstreetmap/Nominatim/issues/60#issuecomment-18679446)
|
||||
for a script that runs the updates using osmosis.
|
||||
|
||||
### Can I import negative OSM ids into Nominatim?
|
||||
|
||||
See [this question of Stackoverflow](https://help.openstreetmap.org/questions/64662/nominatim-flatnode-with-negative-id).
|
||||
|
||||
### Missing XML or text declaration
|
||||
|
||||
The website might show: `XML Parsing Error: XML or text declaration not at start of entity Location.`
|
||||
|
||||
Make sure there are no spaces at the beginning of your `settings/local.php` file.
|
||||
|
||||
|
||||
276
docs/admin/Import-and-Update.md
Normal file
276
docs/admin/Import-and-Update.md
Normal file
@@ -0,0 +1,276 @@
|
||||
# Importing and Updating the Database
|
||||
|
||||
The following instructions explain how to create a Nominatim database
|
||||
from an OSM planet file and how to keep the database up to date. It
|
||||
is assumed that you have already successfully installed the Nominatim
|
||||
software itself, if not return to the [installation page](Installation.md).
|
||||
|
||||
## Configuration setup in settings/local.php
|
||||
|
||||
The Nominatim server can be customized via the file `settings/local.php`
|
||||
in the build directory. Note that this is a PHP file, so it must always
|
||||
start like this:
|
||||
|
||||
<?php
|
||||
|
||||
without any leading spaces.
|
||||
|
||||
There are lots of configuration settings you can tweak. Have a look
|
||||
at `settings/default.php` for a full list. Most should have a sensible default.
|
||||
|
||||
#### Flatnode files
|
||||
|
||||
If you plan to import a large dataset (e.g. Europe, North America, planet),
|
||||
you should also enable flatnode storage of node locations. With this
|
||||
setting enabled, node coordinates are stored in a simple file instead
|
||||
of the database. This will save you import time and disk storage.
|
||||
Add to your `settings/local.php`:
|
||||
|
||||
@define('CONST_Osm2pgsql_Flatnode_File', '/path/to/flatnode.file');
|
||||
|
||||
Replace the second part with a suitable path on your system and make sure
|
||||
the directory exists. There should be at least 40GB of free space.
|
||||
|
||||
## Downloading additional data
|
||||
|
||||
### Wikipedia rankings
|
||||
|
||||
Wikipedia can be used as an optional auxiliary data source to help indicate
|
||||
the importance of OSM features. Nominatim will work without this information
|
||||
but it will improve the quality of the results if this is installed.
|
||||
This data is available as a binary download:
|
||||
|
||||
cd $NOMINATIM_SOURCE_DIR/data
|
||||
wget https://www.nominatim.org/data/wikipedia_article.sql.bin
|
||||
wget https://www.nominatim.org/data/wikipedia_redirect.sql.bin
|
||||
|
||||
Combined the 2 files are around 1.5GB and add around 30GB to the install
|
||||
size of Nominatim. They also increase the install time by an hour or so.
|
||||
|
||||
*NOTE:* you'll need to download the Wikipedia rankings before performing
|
||||
the initial import of the data if you want the rankings applied to the
|
||||
loaded data.
|
||||
|
||||
### Great Britain, USA postcodes
|
||||
|
||||
Nominatim can use postcodes from an external source to improve searches that
|
||||
involve a GB or US postcode. This data can be optionally downloaded:
|
||||
|
||||
cd $NOMINATIM_SOURCE_DIR/data
|
||||
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
||||
wget https://www.nominatim.org/data/us_postcode_data.sql.gz
|
||||
|
||||
## Choosing the Data to Import
|
||||
|
||||
In its default setup Nominatim is configured to import the full OSM data
|
||||
set for the entire planet. Such a setup requires a powerful machine with
|
||||
at least 32GB of RAM and around 800GB of SSD hard disks. Depending on your
|
||||
use case there are various ways to reduce the amount of data imported. This
|
||||
section discusses these methods. They can also be combined.
|
||||
|
||||
### Using an extract
|
||||
|
||||
If you only need geocoding for a smaller region, then precomputed extracts
|
||||
are a good way to reduce the database size and import time.
|
||||
[Geofabrik](https://download.geofabrik.de) offers extracts for most countries.
|
||||
They even have daily updates which can be used with the update process described
|
||||
below. There are also
|
||||
[other providers for extracts](https://wiki.openstreetmap.org/wiki/Planet.osm#Downloading).
|
||||
|
||||
Please be aware that some extracts are not cut exactly along the country
|
||||
boundaries. As a result some parts of the boundary may be missing which means
|
||||
that Nominatim cannot compute the areas for some administrative areas.
|
||||
|
||||
### Dropping Data Required for Dynamic Updates
|
||||
|
||||
About half of the data in Nominatim's database is not really used for serving
|
||||
the API. It is only there to allow the data to be updated from the latest
|
||||
changes from OSM. For many uses these dynamic updates are not really required.
|
||||
If you don't plan to apply updates, the dynamic part of the database can be
|
||||
safely dropped using the following command:
|
||||
|
||||
```
|
||||
./utils/setup.php --drop
|
||||
```
|
||||
|
||||
Note that you still need to provide for sufficient disk space for the initial
|
||||
import. So this option is particularly interesting if you plan to transfer the
|
||||
database or reuse the space later.
|
||||
|
||||
### Reverse-only Imports
|
||||
|
||||
If you only want to use the Nominatim database for reverse lookups or
|
||||
if you plan to use the installation only for exports to a
|
||||
[photon](https://photon.komoot.de/) database, then you can set up a database
|
||||
without search indexes. Add `--reverse-only` to your setup command above.
|
||||
|
||||
This saves about 5% of disk space.
|
||||
|
||||
### Filtering Imported Data
|
||||
|
||||
Nominatim normally sets up a full search database containing administrative
|
||||
boundaries, places, streets, addresses and POI data. There are also other
|
||||
import styles available which only read selected data:
|
||||
|
||||
* **settings/import-admin.style**
|
||||
Only import administrative boundaries and places.
|
||||
* **settings/import-street.style**
|
||||
Like the admin style but also adds streets.
|
||||
* **settings/import-address.style**
|
||||
Import all data necessary to compute addresses down to house number level.
|
||||
* **settings/import-full.style**
|
||||
Default style that also includes points of interest.
|
||||
|
||||
The style can be changed with the configuration `CONST_Import_Style`.
|
||||
|
||||
To give you an idea of the impact of using the different styles, the table
|
||||
below gives rough estimates of the final database size after import of a
|
||||
2018 planet and after using the `--drop` option. It also shows the time
|
||||
needed for the import on a machine with 32GB RAM, 4 CPUS and SSDs. Note that
|
||||
the given sizes are just an estimate meant for comparison of style requirements.
|
||||
Your planet import is likely to be larger as the OSM data grows with time.
|
||||
|
||||
style | Import time | DB size | after drop
|
||||
----------|--------------|------------|------------
|
||||
admin | 5h | 190 GB | 20 GB
|
||||
street | 42h | 400 GB | 180 GB
|
||||
address | 59h | 500 GB | 260 GB
|
||||
full | 80h | 575 GB | 300 GB
|
||||
|
||||
You can also customize the styles further. For an description of the
|
||||
style format see [the development section](../develop/Import.md).
|
||||
|
||||
## Initial import of the data
|
||||
|
||||
**Important:** first try the import with a small extract, for example from
|
||||
[Geofabrik](https://download.geofabrik.de).
|
||||
|
||||
Download the data to import and load the data with the following command
|
||||
from the build directory:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --osm-file <data file> --all [--osm2pgsql-cache 28000] 2>&1 | tee setup.log
|
||||
```
|
||||
|
||||
The `--osm2pgsql-cache` parameter is optional but strongly recommended for
|
||||
planet imports. It sets the node cache size for the osm2pgsql import part
|
||||
(see `-C` parameter in osm2pgsql help). As a rule of thumb, this should be
|
||||
about the same size as the file you are importing but never more than
|
||||
2/3 of RAM available. If your machine starts swapping reduce the size.
|
||||
|
||||
Computing word frequency for search terms can improve the performance of
|
||||
forward geocoding in particular under high load as it helps PostgreSQL's query
|
||||
planner to make the right decisions. To recompute word counts run:
|
||||
|
||||
```sh
|
||||
./utils/update.php --recompute-word-counts
|
||||
```
|
||||
|
||||
This will take a couple of hours for a full planet installation. You can
|
||||
also defer that step to a later point in time when you realise that
|
||||
performance becomes an issue. Just make sure that updates are stopped before
|
||||
running this function.
|
||||
|
||||
If you want to be able to search for places by their type through
|
||||
[special key phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases)
|
||||
you also need to enable these key phrases like this:
|
||||
|
||||
./utils/specialphrases.php --wiki-import > specialphrases.sql
|
||||
psql -d nominatim -f specialphrases.sql
|
||||
|
||||
Note that this command downloads the phrases from the wiki link above.
|
||||
|
||||
|
||||
## Installing Tiger housenumber data for the US
|
||||
|
||||
Nominatim is able to use the official [TIGER](https://www.census.gov/geo/maps-data/data/tiger.html)
|
||||
address set to complement the OSM house number data in the US. You can add
|
||||
TIGER data to your own Nominatim instance by following these steps. The
|
||||
entire US adds about 10GB to your database.
|
||||
|
||||
1. Get preprocessed TIGER 2019 data and unpack it into the
|
||||
data directory in your Nominatim sources:
|
||||
|
||||
cd Nominatim/data
|
||||
wget https://nominatim.org/data/tiger2019-nominatim-preprocessed.tar.gz
|
||||
tar xf tiger2019-nominatim-preprocessed.tar.gz
|
||||
|
||||
`data-source/us-tiger/README.md` explains how the data got preprocessed.
|
||||
|
||||
2. Import the data into your Nominatim database:
|
||||
|
||||
./utils/setup.php --import-tiger-data
|
||||
|
||||
3. Enable use of the Tiger data in your `settings/local.php` by adding:
|
||||
|
||||
@define('CONST_Use_US_Tiger_Data', true);
|
||||
|
||||
4. Apply the new settings:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
|
||||
## Updates
|
||||
|
||||
There are many different ways to update your Nominatim database.
|
||||
The following section describes how to keep it up-to-date with Pyosmium.
|
||||
For a list of other methods see the output of `./utils/update.php --help`.
|
||||
|
||||
#### Installing the newest version of Pyosmium
|
||||
|
||||
It is recommended to install Pyosmium via pip. Make sure to use python3.
|
||||
Run (as the same user who will later run the updates):
|
||||
|
||||
```sh
|
||||
pip3 install --user osmium
|
||||
```
|
||||
|
||||
Nominatim needs a tool called `pyosmium-get-updates` which comes with
|
||||
Pyosmium. You need to tell Nominatim where to find it. Add the
|
||||
following line to your `settings/local.php`:
|
||||
|
||||
@define('CONST_Pyosmium_Binary', '/home/user/.local/bin/pyosmium-get-changes');
|
||||
|
||||
The path above is fine if you used the `--user` parameter with pip.
|
||||
Replace `user` with your user name.
|
||||
|
||||
#### Setting up the update process
|
||||
|
||||
Next the update needs to be initialised. By default Nominatim is configured
|
||||
to update using the global minutely diffs.
|
||||
|
||||
If you want a different update source you will need to add some settings
|
||||
to `settings/local.php`. For example, to use the daily country extracts
|
||||
diffs for Ireland from Geofabrik add the following:
|
||||
|
||||
// base URL of the replication service
|
||||
@define('CONST_Replication_Url', 'https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates');
|
||||
// How often upstream publishes diffs
|
||||
@define('CONST_Replication_Update_Interval', '86400');
|
||||
// How long to sleep if no update found yet
|
||||
@define('CONST_Replication_Recheck_Interval', '900');
|
||||
|
||||
To set up the update process now run the following command:
|
||||
|
||||
./utils/update.php --init-updates
|
||||
|
||||
It outputs the date where updates will start. Recheck that this date is
|
||||
what you expect.
|
||||
|
||||
The `--init-updates` command needs to be rerun whenever the replication service
|
||||
is changed.
|
||||
|
||||
#### Updating Nominatim
|
||||
|
||||
The following command will keep your database constantly up to date:
|
||||
|
||||
./utils/update.php --import-osmosis-all
|
||||
|
||||
(Note that even though the old name "import-osmosis-all" has been kept for compatibility reasons, Osmosis is not required to run this - it uses pyosmium behind the scenes.)
|
||||
|
||||
If you have imported multiple country extracts and want to keep them
|
||||
up-to-date, have a look at the script in
|
||||
[issue #60](https://github.com/openstreetmap/Nominatim/issues/60).
|
||||
|
||||
158
docs/admin/Installation.md
Normal file
158
docs/admin/Installation.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Basic Installation
|
||||
|
||||
This page contains generic installation instructions for Nominatim and its
|
||||
prerequisites. There are also step-by-step instructions available for
|
||||
the following operating systems:
|
||||
|
||||
* [Ubuntu 18.04](../appendix/Install-on-Ubuntu-18.md)
|
||||
* [Ubuntu 16.04](../appendix/Install-on-Ubuntu-16.md)
|
||||
* [CentOS 7.2](../appendix/Install-on-Centos-7.md)
|
||||
|
||||
These OS-specific instructions can also be found in executable form
|
||||
in the `vagrant/` directory.
|
||||
|
||||
Users have created instructions for other frameworks. We haven't tested those
|
||||
and can't offer support.
|
||||
|
||||
* [Docker](https://github.com/mediagis/nominatim-docker)
|
||||
* [Docker on Kubernetes](https://github.com/peter-evans/nominatim-k8s)
|
||||
* [Ansible](https://github.com/synthesio/infra-ansible-nominatim)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Software
|
||||
|
||||
For compiling:
|
||||
|
||||
* [cmake](https://cmake.org/)
|
||||
* [libxml2](http://xmlsoft.org/)
|
||||
* a recent C++ compiler
|
||||
|
||||
Nominatim comes with its own version of osm2pgsql. See the
|
||||
osm2pgsql README for additional dependencies required for compiling osm2pgsql.
|
||||
|
||||
For running tests:
|
||||
|
||||
* [behave](http://pythonhosted.org/behave/)
|
||||
* [Psycopg2](https://initd.org/psycopg)
|
||||
* [nose](https://nose.readthedocs.io)
|
||||
* [phpunit](https://phpunit.de)
|
||||
|
||||
For running Nominatim:
|
||||
|
||||
* [PostgreSQL](https://www.postgresql.org) (9.3 or later)
|
||||
* [PostGIS](https://postgis.org) (2.2 or later)
|
||||
* [PHP](https://php.net) (7.0 or later)
|
||||
* PHP-pgsql
|
||||
* PHP-intl (bundled with PHP)
|
||||
* [PEAR::DB](https://pear.php.net/package/DB)
|
||||
* a webserver (apache or nginx are recommended)
|
||||
|
||||
For running continuous updates:
|
||||
|
||||
* [pyosmium](https://osmcode.org/pyosmium/) (with Python 3)
|
||||
|
||||
### Hardware
|
||||
|
||||
A minimum of 2GB of RAM is required or installation will fail. For a full
|
||||
planet import 32GB of RAM or more are strongly recommended.
|
||||
|
||||
For a full planet install you will need at least 700GB of hard disk space
|
||||
(take into account that the OSM database is growing fast). SSD disks
|
||||
will help considerably to speed up import and queries.
|
||||
|
||||
On a 6-core machine with 32GB RAM and SSDs the import of a full planet takes
|
||||
a bit more than 2 days. Without SSDs 7-8 days are more realistic.
|
||||
|
||||
|
||||
## Setup of the server
|
||||
|
||||
### PostgreSQL tuning
|
||||
|
||||
You might want to tune your PostgreSQL installation so that the later steps
|
||||
make best use of your hardware. You should tune the following parameters in
|
||||
your `postgresql.conf` file.
|
||||
|
||||
shared_buffers (2GB)
|
||||
maintenance_work_mem (10GB)
|
||||
work_mem (50MB)
|
||||
effective_cache_size (24GB)
|
||||
synchronous_commit = off
|
||||
checkpoint_segments = 100 # only for postgresql <= 9.4
|
||||
checkpoint_timeout = 10min
|
||||
checkpoint_completion_target = 0.9
|
||||
|
||||
The numbers in brackets behind some parameters seem to work fine for
|
||||
32GB RAM machine. Adjust to your setup.
|
||||
|
||||
For the initial import, you should also set:
|
||||
|
||||
fsync = off
|
||||
full_page_writes = off
|
||||
|
||||
Don't forget to reenable them after the initial import or you risk database
|
||||
corruption. Autovacuum must not be switched off because it ensures that the
|
||||
tables are frequently analysed.
|
||||
|
||||
### Webserver setup
|
||||
|
||||
The `website/` directory in the build directory contains the configured
|
||||
website. Include the directory into your webbrowser to serve php files
|
||||
from there.
|
||||
|
||||
#### Configure for use with Apache
|
||||
|
||||
Make sure your Apache configuration contains the required permissions for the
|
||||
directory and create an alias:
|
||||
|
||||
<Directory "/srv/nominatim/build/website">
|
||||
Options FollowSymLinks MultiViews
|
||||
AddType text/html .php
|
||||
DirectoryIndex search.php
|
||||
Require all granted
|
||||
</Directory>
|
||||
Alias /nominatim /srv/nominatim/build/website
|
||||
|
||||
`/srv/nominatim/build` should be replaced with the location of your
|
||||
build directory.
|
||||
|
||||
After making changes in the apache config you need to restart apache.
|
||||
The website should now be available on http://localhost/nominatim.
|
||||
|
||||
#### Configure for use with Nginx
|
||||
|
||||
Use php-fpm as a deamon for serving PHP cgi. Install php-fpm together with nginx.
|
||||
|
||||
By default php listens on a network socket. If you want it to listen to a
|
||||
Unix socket instead, change the pool configuration (`pool.d/www.conf`) as
|
||||
follows:
|
||||
|
||||
; Comment out the tcp listener and add the unix socket
|
||||
;listen = 127.0.0.1:9000
|
||||
listen = /var/run/php5-fpm.sock
|
||||
|
||||
; Ensure that the daemon runs as the correct user
|
||||
listen.owner = www-data
|
||||
listen.group = www-data
|
||||
listen.mode = 0666
|
||||
|
||||
Tell nginx that php files are special and to fastcgi_pass to the php-fpm
|
||||
unix socket by adding the location definition to the default configuration.
|
||||
|
||||
root /srv/nominatim/build/website;
|
||||
index search.php index.html;
|
||||
location ~ [^/]\.php(/|$) {
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
if (!-f $document_root$fastcgi_script_name) {
|
||||
return 404;
|
||||
}
|
||||
fastcgi_pass unix:/var/run/php5-fpm.sock;
|
||||
fastcgi_index search.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
Restart the nginx and php5-fpm services and the website should now be available
|
||||
at `http://localhost/`.
|
||||
|
||||
|
||||
Now continue with [importing the database](Import-and-Update.md).
|
||||
173
docs/admin/Migration.md
Normal file
173
docs/admin/Migration.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Database Migrations
|
||||
|
||||
This page describes database migrations necessary to update existing databases
|
||||
to newer versions of Nominatim.
|
||||
|
||||
SQL statements should be executed from the PostgreSQL commandline. Execute
|
||||
`psql nominatim` to enter command line mode.
|
||||
|
||||
## 3.3.0 -> 3.4.0
|
||||
|
||||
### Reorganisation of location_area_country table
|
||||
|
||||
The table `location_area_country` has been optimized. You need to switch to the
|
||||
new format when you run updates. While updates are disabled, run the following
|
||||
SQL commands:
|
||||
|
||||
```sql
|
||||
CREATE TABLE location_area_country_new AS
|
||||
SELECT place_id, country_code, geometry FROM location_area_country;
|
||||
DROP TABLE location_area_country;
|
||||
ALTER TABLE location_area_country_new RENAME TO location_area_country;
|
||||
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry);
|
||||
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id);
|
||||
```
|
||||
|
||||
## 3.2.0 -> 3.3.0
|
||||
|
||||
### New database connection string (DSN) format
|
||||
|
||||
Previously database connection setting (`CONST_Database_DSN` in `settings/*.php`) had the format
|
||||
|
||||
* (simple) `pgsql://@/nominatim`
|
||||
* (complex) `pgsql://johndoe:secret@machine1.domain.com:1234/db1`
|
||||
|
||||
The new format is
|
||||
|
||||
* (simple) `pgsql:dbname=nominatim`
|
||||
* (complex) `pgsql:dbname=db1;host=machine1.domain.com;port=1234;user=johndoe;password=secret`
|
||||
|
||||
### Natural Earth country boundaries no longer needed as fallback
|
||||
|
||||
```
|
||||
DROP TABLE country_naturalearthdata;
|
||||
```
|
||||
|
||||
Finally, update all SQL functions:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
### Configurable Address Levels
|
||||
|
||||
The new configurable address levels require a new table. Create it with the
|
||||
following command:
|
||||
|
||||
```sh
|
||||
./utils/update.php --update-address-levels
|
||||
```
|
||||
|
||||
## 3.1.0 -> 3.2.0
|
||||
|
||||
### New reverse algorithm
|
||||
|
||||
The reverse algorithm has changed and requires new indexes. Run the following
|
||||
SQL statements to create the indexes:
|
||||
|
||||
```
|
||||
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
||||
ON placex USING gist (geometry)
|
||||
WHERE (name is not null or housenumber is not null or rank_address between 26 and 27)
|
||||
AND class not in ('railway','tunnel','bridge','man_made')
|
||||
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
||||
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
||||
ON placex USING gist (geometry)
|
||||
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
||||
AND rank_address between 4 and 25 AND type != 'postcode'
|
||||
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
||||
ON placex USING gist (geometry)
|
||||
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
||||
AND class = 'place' AND type != 'postcode'
|
||||
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||
```
|
||||
|
||||
You also need to grant the website user access to the `country_osm_grid` table:
|
||||
|
||||
```
|
||||
GRANT SELECT ON table country_osm_grid to "www-user";
|
||||
```
|
||||
|
||||
Replace the `www-user` with the user name of your website server if necessary.
|
||||
|
||||
You can now drop the unused indexes:
|
||||
|
||||
```
|
||||
DROP INDEX idx_placex_reverse_geometry;
|
||||
```
|
||||
|
||||
Finally, update all SQL functions:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
## 3.0.0 -> 3.1.0
|
||||
|
||||
### Postcode Table
|
||||
|
||||
A new separate table for artificially computed postcode centroids was introduced.
|
||||
Migration to the new format is possible but **not recommended**.
|
||||
|
||||
Create postcode table and indexes, running the following SQL statements:
|
||||
|
||||
```sql
|
||||
CREATE TABLE location_postcode
|
||||
(place_id BIGINT, parent_place_id BIGINT, rank_search SMALLINT,
|
||||
rank_address SMALLINT, indexed_status SMALLINT, indexed_date TIMESTAMP,
|
||||
country_code varchar(2), postcode TEXT,
|
||||
geometry GEOMETRY(Geometry, 4326));
|
||||
CREATE INDEX idx_postcode_geometry ON location_postcode USING GIST (geometry);
|
||||
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id);
|
||||
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode);
|
||||
GRANT SELECT ON location_postcode TO "www-data";
|
||||
drop type if exists nearfeaturecentr cascade;
|
||||
create type nearfeaturecentr as (
|
||||
place_id BIGINT,
|
||||
keywords int[],
|
||||
rank_address smallint,
|
||||
rank_search smallint,
|
||||
distance float,
|
||||
isguess boolean,
|
||||
postcode TEXT,
|
||||
centroid GEOMETRY
|
||||
);
|
||||
```
|
||||
|
||||
Add postcode column to `location_area` tables with SQL statement:
|
||||
|
||||
```sql
|
||||
ALTER TABLE location_area ADD COLUMN postcode TEXT;
|
||||
```
|
||||
|
||||
Then reimport the functions:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
Create appropriate triggers with SQL:
|
||||
|
||||
```sql
|
||||
CREATE TRIGGER location_postcode_before_update BEFORE UPDATE ON location_postcode
|
||||
FOR EACH ROW EXECUTE PROCEDURE postcode_update();
|
||||
```
|
||||
|
||||
Finally populate the postcode table (will take a while):
|
||||
|
||||
```sh
|
||||
./utils/setup.php --calculate-postcodes --index --index-noanalyse
|
||||
```
|
||||
|
||||
This will create a working database. You may also delete the old artificial
|
||||
postcodes now. Note that this may be expensive and is not absolutely necessary.
|
||||
The following SQL statement will remove them:
|
||||
|
||||
```sql
|
||||
DELETE FROM place_addressline a USING placex p
|
||||
WHERE a.address_place_id = p.place_id and p.osm_type = 'P';
|
||||
ALTER TABLE placex DISABLE TRIGGER USER;
|
||||
DELETE FROM placex WHERE osm_type = 'P';
|
||||
ALTER TABLE placex ENABLE TRIGGER USER;
|
||||
```
|
||||
152
docs/api/Details.md
Normal file
152
docs/api/Details.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Place details
|
||||
|
||||
Lookup details about a single place by id. The default output is HTML for debugging search logic and results.
|
||||
|
||||
**The details page (including JSON output) exists for debugging only and must not be downloaded automatically**, see [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/).
|
||||
|
||||
|
||||
## Parameters
|
||||
|
||||
The details API supports the following two request formats:
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/details?osmtype=[N|W|R]&osmid=<value>&class=<value>
|
||||
```
|
||||
|
||||
`osmtype` and `osmid` are required parameter. The type is one of node (N), way (W)
|
||||
or relation (R). The id must be a number. The `class` parameter is optional and
|
||||
allows to distinguish between entries, when the corresponding OSM object has more
|
||||
than one main tag. For example, when a place is tagged with `tourism=hotel` and
|
||||
`amenity=restaurant`, there will be two place entries in Nominatim, one for a
|
||||
restaurant, one for a hotel. You need to specify `class=tourism` or `class=amentity`
|
||||
to get exactly the one you want. If there are multiple places in the database
|
||||
but the `class` parameter is left out, then one of the places will be chosen
|
||||
at random and displayed.
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/details?place_id=<value>
|
||||
```
|
||||
|
||||
Placeids are assigned sequentially during Nominatim data import. The id for a place is different between Nominatim installation (servers) and changes when data gets reimported. Therefore it can't be used as permanent id and shouldn't be used in bug reports.
|
||||
|
||||
|
||||
Additional optional parameters are explained below.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[html|json]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
Wrap JSON output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||
Only has an effect for JSON output formats.
|
||||
|
||||
* `pretty=[0|1]`
|
||||
|
||||
For JSON output will add indentation to make it more human-readable. (Default: 0)
|
||||
|
||||
|
||||
### Output details
|
||||
|
||||
* `addressdetails=[0|1]`
|
||||
|
||||
Include a breakdown of the address into elements. (Default for JSON: 0, for HTML: 1)
|
||||
|
||||
* `keywords=[0|1]`
|
||||
|
||||
Include a list of name keywords and address keywords (word ids). (Default: 0)
|
||||
|
||||
* `linkedplaces=[0|1]`
|
||||
|
||||
Include details of places higher in the address hierarchy. E.g. for a street this is usually the city, state, postal code, country. (Default: 1)
|
||||
|
||||
* `hierarchy=[0|1]`
|
||||
|
||||
Include details of places lower in the address hierarchy. E.g. for a city this usually a list of streets, suburbs, rivers. (Default for JSON: 0, for HTML: 1)
|
||||
|
||||
* `group_hierarchy=[0|1]`
|
||||
|
||||
For JSON output will group the places by type. (Default: 0)
|
||||
|
||||
* `polygon_geojson=[0|1]`
|
||||
|
||||
Include geometry of result. (Default for JSON: 0, for HTML: 1)
|
||||
|
||||
### Language of results
|
||||
|
||||
* `accept-language=<browser language string>`
|
||||
|
||||
Preferred language order for showing result, overrides the value
|
||||
specified in the "Accept-Language" HTTP header.
|
||||
Either use a standard RFC2616 accept-language string or a simple
|
||||
comma-separated list of language codes.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
##### HTML
|
||||
|
||||
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407)
|
||||
|
||||
##### JSON
|
||||
|
||||
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json)
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"place_id": 85993608,
|
||||
"parent_place_id": 72765313,
|
||||
"osm_type": "W",
|
||||
"osm_id": 38210407,
|
||||
"category": "place",
|
||||
"type": "square",
|
||||
"admin_level": "15",
|
||||
"localname": "Pariser Platz",
|
||||
"names": {
|
||||
"name": "Pariser Platz",
|
||||
"name:be": "Парыжская плошча",
|
||||
"name:de": "Pariser Platz",
|
||||
"name:es": "Plaza de París",
|
||||
"name:he": "פאריזר פלאץ",
|
||||
"name:ko": "파리저 광장",
|
||||
"name:la": "Forum Parisinum",
|
||||
"name:ru": "Парижская площадь",
|
||||
"name:uk": "Паризька площа",
|
||||
"name:zh": "巴黎廣場"
|
||||
},
|
||||
"addresstags": {
|
||||
"postcode": "10117"
|
||||
},
|
||||
"housenumber": null,
|
||||
"calculated_postcode": "10117",
|
||||
"country_code": "de",
|
||||
"indexed_date": "2018-08-18T17:02:45+00:00",
|
||||
"importance": 0.339401620591472,
|
||||
"calculated_importance": 0.339401620591472,
|
||||
"extratags": {
|
||||
"wikidata": "Q156716",
|
||||
"wikipedia": "de:Pariser Platz"
|
||||
},
|
||||
"calculated_wikipedia": "de:Pariser_Platz",
|
||||
"rank_address": 30,
|
||||
"rank_search": 30,
|
||||
"isarea": true,
|
||||
"centroid": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
13.3786822618517,
|
||||
52.5163654
|
||||
]
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
13.3786822618517,
|
||||
52.5163654
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
61
docs/api/Faq.md
Normal file
61
docs/api/Faq.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## API Results
|
||||
|
||||
#### 1. The address of my search results contains far-away places that don't belong there.
|
||||
|
||||
Nominatim computes the address from two sources in the OpenStreetMap data:
|
||||
from administrative boundaries and from place nodes. Boundaries are the more
|
||||
useful source. They precisely describe an area. So it is very clear for
|
||||
Nominatim if a point belongs to an area or not. Place nodes are more complicated.
|
||||
These are only points without any precise extent. So Nominatim has to take a
|
||||
guess and assume that an address belongs to the closest place node it can find.
|
||||
In an ideal world, Nominatim would not need the place nodes but there are
|
||||
many places on earth where there are no precise boundaries available for
|
||||
all parts that make up an address. This is in particular true for the more
|
||||
local address parts, like villages and suburbs. Therefore it is not possible
|
||||
to completely dismiss place nodes. And sometimes they sneak in where they
|
||||
don't belong.
|
||||
|
||||
As a OpenStreetMap mapper, you can improve the situation in two ways: if you
|
||||
see a place node for which already an administrative area exists, then you
|
||||
should _link_ the two by adding the node with a 'label' role to the boundary
|
||||
relation. If there is no administrative area, you can add the approximate
|
||||
extent of the place and tag it place=<something> as well.
|
||||
|
||||
#### 2. When doing reverse search, the address details have parts that don't contain the point I was looking up.
|
||||
|
||||
There is a common misconception how the reverse API call works in Nominatim.
|
||||
Reverse does not give you the address of the point you asked for. Reverse
|
||||
returns the closest object to the point you asked for and then returns the
|
||||
address of that object. Now, if you are close to a border, then the closest
|
||||
object may be across that border. When Nominatim then returns the address,
|
||||
it contains the county/state/country across the border.
|
||||
|
||||
#### 3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible?
|
||||
|
||||
This is basically the same problem as in the previous answer.
|
||||
The zoom level influences at which [search rank](https://wiki.openstreetmap.org/wiki/Nominatim/Development_overview#Country_to_street_level) Nominatim starts looking
|
||||
for the closest object. So the closest house number maybe on one side of the
|
||||
border while the closest street is on the other. As the address details contain
|
||||
the address of the closest object found, you might sometimes get one result,
|
||||
sometimes the other for the closest point.
|
||||
|
||||
#### 4. Can you return the continent?
|
||||
|
||||
Nominatim assigns each map feature one country. Those outside any administrative
|
||||
boundaries are assigned a special no-country. Continents or other super-national
|
||||
administrations (e.g. European Union, NATO, Custom unions) are not supported,
|
||||
see also [Administrative Boundary](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#Super-national_administrations).
|
||||
|
||||
#### 5. Can you return the timezone?
|
||||
|
||||
See this separate OpenStreetMap-based project [Timezone Boundary Builder](https://github.com/evansiroky/timezone-boundary-builder).
|
||||
|
||||
#### 6. I want to download a list of streets/restaurants of a city/region
|
||||
|
||||
The [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) is more
|
||||
suited for these kinds of queries.
|
||||
|
||||
That said if you installed your own Nominatim instance you can use the
|
||||
`/utils/export.php` PHP script as basis to return such lists.
|
||||
147
docs/api/Lookup.md
Normal file
147
docs/api/Lookup.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Address lookup
|
||||
|
||||
The lookup API allows to query the address and other details of one or
|
||||
multiple OSM objects like node, way or relation.
|
||||
|
||||
## Parameters
|
||||
|
||||
The lookup API has the following format:
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/lookup?osm_ids=[N|W|R]<value>,…,…,&<params>
|
||||
```
|
||||
|
||||
`osm_ids` is mandatory and must contain a comma-separated list of OSM ids each
|
||||
prefixed with its type, one of node(N), way(W) or relation(R). Up to 50 ids
|
||||
can be queried at the same time.
|
||||
|
||||
Additional optional parameters are explained below.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
Wrap JSON output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||
Only has an effect for JSON output formats.
|
||||
|
||||
### Output details
|
||||
|
||||
* `addressdetails=[0|1]`
|
||||
|
||||
Include a breakdown of the address into elements. (Default: 0)
|
||||
|
||||
|
||||
* `extratags=[0|1]`
|
||||
|
||||
Include additional information in the result if available,
|
||||
e.g. wikipedia link, opening hours. (Default: 0)
|
||||
|
||||
|
||||
* `namedetails=[0|1]`
|
||||
|
||||
Include a list of alternative names in the results. These may include
|
||||
language variants, references, operator and brand. (Default: 0)
|
||||
|
||||
|
||||
### Language of results
|
||||
|
||||
* `accept-language=<browser language string>`
|
||||
|
||||
Preferred language order for showing search results, overrides the value
|
||||
specified in the "Accept-Language" HTTP header.
|
||||
Either use a standard RFC2616 accept-language string or a simple
|
||||
comma-separated list of language codes.
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* `email=<valid email address>`
|
||||
|
||||
If you are making large numbers of request please include an appropriate email
|
||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||
|
||||
* `debug=[0|1]`
|
||||
|
||||
Output assorted developer debug information. Data on internals of Nominatim's
|
||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||
This overrides the specified machine readable format. (Default: 0)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
##### XML
|
||||
|
||||
[https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189](https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189)
|
||||
|
||||
```xml
|
||||
<lookupresults timestamp="Mon, 29 Jun 15 18:01:33 +0000" attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright" querystring="R146656,W104393803,N240109189" polygon="false">
|
||||
<place place_id="127761056" osm_type="relation" osm_id="146656" place_rank="16" lat="53.4791466" lon="-2.2447445" display_name="Manchester, Greater Manchester, North West England, England, United Kingdom" class="boundary" type="administrative" importance="0.704893333438333">
|
||||
<city>Manchester</city>
|
||||
<county>Greater Manchester</county>
|
||||
<state_district>North West England</state_district>
|
||||
<state>England</state>
|
||||
<country>United Kingdom</country>
|
||||
<country_code>gb</country_code>
|
||||
</place>
|
||||
<place place_id="77769745" osm_type="way" osm_id="104393803" place_rank="30" lat="52.5162024" lon="13.3777343363579" display_name="Brandenburg Gate, 1, Pariser Platz, Mitte, Berlin, 10117, Germany" class="tourism" type="attraction" importance="0.443472858361592">
|
||||
<attraction>Brandenburg Gate</attraction>
|
||||
<house_number>1</house_number>
|
||||
<pedestrian>Pariser Platz</pedestrian>
|
||||
<suburb>Mitte</suburb>
|
||||
<city_district>Mitte</city_district>
|
||||
<city>Berlin</city>
|
||||
<state>Berlin</state>
|
||||
<postcode>10117</postcode>
|
||||
<country>Germany</country>
|
||||
<country_code>de</country_code>
|
||||
</place>
|
||||
<place place_id="2570600569" osm_type="node" osm_id="240109189" place_rank="15" lat="52.5170365" lon="13.3888599" display_name="Berlin, Germany" class="place" type="city" importance="0.822149797630868">
|
||||
<city>Berlin</city>
|
||||
<state>Berlin</state>
|
||||
<country>Germany</country>
|
||||
<country_code>de</country_code>
|
||||
</place>
|
||||
</lookupresults>
|
||||
```
|
||||
|
||||
##### JSON with extratags
|
||||
|
||||
[https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json](https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json)
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"place_id": "84271358",
|
||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"osm_type": "way",
|
||||
"osm_id": "50637691",
|
||||
"lat": "52.39955055",
|
||||
"lon": "13.04806574678",
|
||||
"display_name": "Brandenburger Tor, Brandenburger Straße, Nördliche Innenstadt, Innenstadt, Potsdam, Brandenburg, 14467, Germany",
|
||||
"class": "historic",
|
||||
"type": "city_gate",
|
||||
"importance": "0.221233780277011",
|
||||
"address": {
|
||||
"address29": "Brandenburger Tor",
|
||||
"pedestrian": "Brandenburger Straße",
|
||||
"suburb": "Nördliche Innenstadt",
|
||||
"city": "Potsdam",
|
||||
"state": "Brandenburg",
|
||||
"postcode": "14467",
|
||||
"country": "Germany",
|
||||
"country_code": "de"
|
||||
},
|
||||
"extratags": {
|
||||
"image": "http://commons.wikimedia.org/wiki/File:Potsdam_brandenburger_tor.jpg",
|
||||
"wikidata": "Q695045",
|
||||
"wikipedia": "de:Brandenburger Tor (Potsdam)",
|
||||
"wheelchair": "yes",
|
||||
"description": "Kleines Brandenburger Tor in Potsdam"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
246
docs/api/Output.md
Normal file
246
docs/api/Output.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# Place Output
|
||||
|
||||
The [/reverse](Reverse.md), [/search](Search.md) and [/lookup](Lookup.md)
|
||||
API calls produce very similar output which is explained in this section.
|
||||
There is one section for each format which is selectable via the `format`
|
||||
parameter.
|
||||
|
||||
## Formats
|
||||
|
||||
### JSON
|
||||
|
||||
The JSON format returns an array of places (for search and lookup) or
|
||||
a single place (for reverse) of the following format:
|
||||
|
||||
```
|
||||
{
|
||||
"place_id": "100149",
|
||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"osm_type": "node",
|
||||
"osm_id": "107775",
|
||||
"boundingbox": ["51.3473219", "51.6673219", "-0.2876474", "0.0323526"],
|
||||
"lat": "51.5073219",
|
||||
"lon": "-0.1276474",
|
||||
"display_name": "London, Greater London, England, SW1A 2DU, United Kingdom",
|
||||
"class": "place",
|
||||
"type": "city",
|
||||
"importance": 0.9654895765402,
|
||||
"icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
|
||||
"address": {
|
||||
"city": "London",
|
||||
"state_district": "Greater London",
|
||||
"state": "England",
|
||||
"postcode": "SW1A 2DU",
|
||||
"country": "United Kingdom",
|
||||
"country_code": "gb"
|
||||
},
|
||||
"extratags": {
|
||||
"capital": "yes",
|
||||
"website": "http://www.london.gov.uk",
|
||||
"wikidata": "Q84",
|
||||
"wikipedia": "en:London",
|
||||
"population": "8416535"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
The possible fields are:
|
||||
|
||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
||||
* `osm_type`, `osm_id` - reference to the OSM object
|
||||
* `boundingbox` - area of corner coordinates
|
||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||
* `display_name` - full comma-separated address
|
||||
* `class`, `type` - key and value of the main OSM tag
|
||||
* `importance` - computed importance rank
|
||||
* `icon` - link to class icon (if available)
|
||||
* `address` - dictionary of address details (only with `addressdetails=1`)
|
||||
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
||||
(only with `extratags=1`)
|
||||
* `namedetails` - dictionary with full list of available names including ref etc.
|
||||
* `geojson`, `svg`, `geotext`, `geokml` - full geometry
|
||||
(only with the appropriate `polygon_*` parameter)
|
||||
|
||||
### JSONv2
|
||||
|
||||
This is the same as the JSON format with two changes:
|
||||
|
||||
* `class` renamed to `category`
|
||||
* additional field `place_rank` with the search rank of the object
|
||||
|
||||
### GeoJSON
|
||||
|
||||
This format follows the [RFC7946](https://geojson.org). Every feature includes
|
||||
a bounding box (`bbox`).
|
||||
|
||||
The feature list has the following fields:
|
||||
|
||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
||||
* `osm_type`, `osm_id` - reference to the OSM object
|
||||
* `category`, `type` - key and value of the main OSM tag
|
||||
* `display_name` - full comma-separated address
|
||||
* `place_rank` - class search rank
|
||||
* `importance` - computed importance rank
|
||||
* `icon` - link to class icon (if available)
|
||||
* `address` - dictionary of address details (only with `addressdetails=1`)
|
||||
* `extratags` - dictionary with additional useful tags like `website` or `maxspeed`
|
||||
(only with `extratags=1`)
|
||||
* `namedetails` - dictionary with full list of available names including ref etc.
|
||||
|
||||
Use `polygon_geojson` to output the full geometry of the object instead
|
||||
of the centroid.
|
||||
|
||||
### GeocodeJSON
|
||||
|
||||
The GeocodeJSON format follows the
|
||||
[GeocodeJSON spec 0.1.0](https://github.com/geocoders/geocodejson-spec).
|
||||
The following feature attributes are implemented:
|
||||
|
||||
* `osm_type`, `osm_id` - reference to the OSM object (unofficial extension)
|
||||
* `type` - value of the main tag of the object (e.g. residential, restaurant, ...)
|
||||
* `label` - full comma-separated address
|
||||
* `name` - localised name of the place
|
||||
* `housenumber`, `street`, `locality`, `postcode`, `city`,
|
||||
`district`, `county`, `state`, `country` -
|
||||
provided when it can be determined from the address
|
||||
(see [this issue](https://github.com/openstreetmap/Nominatim/issues/1080) for
|
||||
current limitations on the correctness of the address) and `addressdetails=1`
|
||||
was given
|
||||
* `admin` - list of localised names of administrative boundaries (only with `addressdetails=1`)
|
||||
|
||||
Use `polygon_geojson` to output the full geometry of the object instead
|
||||
of the centroid.
|
||||
|
||||
### XML
|
||||
|
||||
The XML response returns one or more place objects in slightly different
|
||||
formats depending on the API call.
|
||||
|
||||
#### Reverse
|
||||
|
||||
```
|
||||
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
|
||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
|
||||
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
|
||||
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY"
|
||||
lat="48.9467562" lon="11.4038717"
|
||||
boundingbox="47.2701114,50.5647142,8.9763497,13.8396373">
|
||||
Bavaria, Germany
|
||||
</result>
|
||||
<addressparts>
|
||||
<state>Bavaria</state>
|
||||
<country>Germany</country>
|
||||
<country_code>de</country_code>
|
||||
</addressparts>
|
||||
<extratags>
|
||||
<tag key="place" value="state"/>
|
||||
<tag key="wikidata" value="Q980"/>
|
||||
<tag key="wikipedia" value="de:Bayern"/>
|
||||
<tag key="population" value="12520000"/>
|
||||
<tag key="name:prefix" value="Freistaat"/>
|
||||
</extratags>
|
||||
</reversegeocode>
|
||||
```
|
||||
|
||||
The attributes of the outer `reversegeocode` element return generic information
|
||||
about the query, including the time when the response was sent (in UTC),
|
||||
attribution to OSM and the original querystring.
|
||||
|
||||
The place information can be found in the `result` element. The attributes of that element contain:
|
||||
|
||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
||||
* `osm_type`, `osm_id` - reference to the OSM object
|
||||
* `ref` - content of `ref` tag if it exists
|
||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||
* `boundingbox` - comma-separated list of corner coordinates
|
||||
|
||||
The full address of the result can be found in the content of the
|
||||
`result` element as a comma-separated list.
|
||||
|
||||
Additional information requested with `addressdetails=1`, `extratags=1` and
|
||||
`namedetails=1` can be found in extra elements.
|
||||
|
||||
#### Search and Lookup
|
||||
|
||||
```
|
||||
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
|
||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
|
||||
querystring="london" polygon="false" exclude_place_ids="100149"
|
||||
more_url="https://nominatim.openstreetmap.org/search.php?q=london&addressdetails=1&extratags=1&exclude_place_ids=100149&format=xml&accept-language=en-US%2Cen%3Bq%3D0.7%2Cde%3Bq%3D0.3">
|
||||
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15"
|
||||
boundingbox="51.3473219,51.6673219,-0.2876474,0.0323526" lat="51.5073219" lon="-0.1276474"
|
||||
display_name="London, Greater London, England, SW1A 2DU, United Kingdom"
|
||||
class="place" type="city" importance="0.9654895765402"
|
||||
icon="https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png">
|
||||
<extratags>
|
||||
<tag key="capital" value="yes"/>
|
||||
<tag key="website" value="http://www.london.gov.uk"/>
|
||||
<tag key="wikidata" value="Q84"/>
|
||||
<tag key="wikipedia" value="en:London"/>
|
||||
<tag key="population" value="8416535"/>
|
||||
</extratags>
|
||||
<city>London</city>
|
||||
<state_district>Greater London</state_district>
|
||||
<state>England</state>
|
||||
<postcode>SW1A 2DU</postcode>
|
||||
<country>United Kingdom</country>
|
||||
<country_code>gb</country_code>
|
||||
</place>
|
||||
</searchresults>
|
||||
```
|
||||
|
||||
The attributes of the outer `searchresults` or `lookupresults` element return
|
||||
generic information about the query:
|
||||
|
||||
* `timestamp` - UTC time when the response was sent
|
||||
* `attribution` - OSM licensing information
|
||||
* `querystring` - original query
|
||||
* `polygon` - true when extra geometry information was requested
|
||||
* `exclude_place_ids` - IDs of places that should be ignored in a follow-up request
|
||||
* `more_url` - search call that will yield additional results for the query
|
||||
just sent
|
||||
|
||||
The place information can be found in the `place` elements, of which there may
|
||||
be more than one. The attributes of that element contain:
|
||||
|
||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
||||
* `osm_type`, `osm_id` - reference to the OSM object
|
||||
* `ref` - content of `ref` tag if it exists
|
||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||
* `boundingbox` - comma-separated list of corner coordinates
|
||||
* `place_rank` - class search rank
|
||||
* `display_name` - full comma-separated address
|
||||
* `class`, `type` - key and value of the main OSM tag
|
||||
* `importance` - computed importance rank
|
||||
* `icon` - link to class icon (if available)
|
||||
|
||||
When `addressdetails=1` is requested, the localised address parts appear
|
||||
as subelements with the type of the address part.
|
||||
|
||||
Additional information requested with `extratags=1` and `namedetails=1` can
|
||||
be found in extra elements as sub-element of each place.
|
||||
|
||||
|
||||
## Notes on field values
|
||||
|
||||
### place_id is not a persistent id
|
||||
|
||||
The `place_id` is created when a Nominatim database gets installed. A
|
||||
single place will have a different value on another server or even when
|
||||
the same data gets re-imported. It's thus not useful to treat it as
|
||||
permanent for later use.
|
||||
|
||||
The combination `osm_type`+`osm_id` is slighly better but remember in
|
||||
OpenStreetMap mappers can delete, split, recreate places (and those
|
||||
get a new `osm_id`), there is no link between those old and new ids.
|
||||
Places can also change their meaning without changing their `osm_id`,
|
||||
e.g. when a restaurant is retagged as supermarket. For a more in-depth
|
||||
discussion see [Permanent ID](https://wiki.openstreetmap.org/wiki/Permanent_ID).
|
||||
|
||||
Nominatim merges some places (e.g. center node of a city with the boundary
|
||||
relation) so `osm_type`+`osm_id`+`class_name` would be more unique.
|
||||
|
||||
### boundingbox
|
||||
|
||||
Comma separated list of min latitude, max latitude, min longitude, max longitude.
|
||||
The whole planet would be `-90,90,-180,180`.
|
||||
14
docs/api/Overview.md
Normal file
14
docs/api/Overview.md
Normal file
@@ -0,0 +1,14 @@
|
||||
### Nominatim API
|
||||
|
||||
Nominatim indexes named (or numbered) features within the OpenStreetMap (OSM) dataset and a subset of other unnamed features (pubs, hotels, churches, etc).
|
||||
|
||||
Its API has the following endpoints for querying the data:
|
||||
|
||||
* __[/search](Search.md)__ - search OSM objects by name or type
|
||||
* __[/reverse](Reverse.md)__ - search OSM object by their location
|
||||
* __[/lookup](Lookup.md)__ - look up address details for OSM objects by their ID
|
||||
* __/status__ - query the status of the server
|
||||
* __/deletable__ - list objects that have been deleted in OSM but are held
|
||||
back in Nominatim in case the deletion was accidental
|
||||
* __/polygons__ - list of broken polygons detected by Nominatim
|
||||
* __[/details](Details.md)__ - show internal details for an object (for debugging only)
|
||||
272
docs/api/Reverse.md
Normal file
272
docs/api/Reverse.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# Reverse Geocoding
|
||||
|
||||
Reverse geocoding generates an address from a latitude and longitude or from
|
||||
an OSM object.
|
||||
|
||||
## Parameters
|
||||
|
||||
The main format of the reverse API is
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/reverse?<query>
|
||||
```
|
||||
|
||||
There are two ways how the requested location can be specified:
|
||||
|
||||
* `lat=<value>` `lon=<value>`
|
||||
|
||||
A geographic location to generate an address for. The coordiantes must be
|
||||
in WGS84 format.
|
||||
|
||||
* `osm_type=[N|W|R]` `osm_id=<value>`
|
||||
|
||||
A specific OSM node(N), way(W) or relation(R) to return an address for.
|
||||
|
||||
In both cases exactly one object is returned. The two input parameters cannot
|
||||
be used at the same time. Both accept the additional optional parameters listed
|
||||
below.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
Wrap JSON output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
||||
Only has an effect for JSON output formats.
|
||||
|
||||
### Output details
|
||||
|
||||
* `addressdetails=[0|1]`
|
||||
|
||||
Include a breakdown of the address into elements. (Default: 1)
|
||||
|
||||
|
||||
* `extratags=[0|1]`
|
||||
|
||||
Include additional information in the result if available,
|
||||
e.g. wikipedia link, opening hours. (Default: 0)
|
||||
|
||||
|
||||
* `namedetails=[0|1]`
|
||||
|
||||
Include a list of alternative names in the results. These may include
|
||||
language variants, references, operator and brand. (Default: 0)
|
||||
|
||||
|
||||
### Language of results
|
||||
|
||||
* `accept-language=<browser language string>`
|
||||
|
||||
Preferred language order for showing search results, overrides the value
|
||||
specified in the "Accept-Language" HTTP header.
|
||||
Either use a standard RFC2616 accept-language string or a simple
|
||||
comma-separated list of language codes.
|
||||
|
||||
### Result limitation
|
||||
|
||||
* `zoom=[0-18]`
|
||||
|
||||
Level of detail required for the address. Default: 18. This is a number that corresponds
|
||||
roughly to the zoom level used in map frameworks like Leaflet.js, Openlayers etc.
|
||||
In terms of address details the zoom levels are as follows:
|
||||
|
||||
zoom | address detail
|
||||
-----|---------------
|
||||
3 | country
|
||||
5 | state
|
||||
8 | county
|
||||
10 | city
|
||||
14 | suburb
|
||||
16 | major streets
|
||||
17 | major and minor streets
|
||||
18 | building
|
||||
|
||||
|
||||
### Polygon output
|
||||
|
||||
* `polygon_geojson=1`
|
||||
* `polygon_kml=1`
|
||||
* `polygon_svg=1`
|
||||
* `polygon_text=1`
|
||||
|
||||
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
||||
options can be used at a time. (Default: 0)
|
||||
|
||||
* `polygon_threshold=0.0`
|
||||
|
||||
Simplify the output geometry before returning. The parameter is the
|
||||
tolerance in degrees with which the geometry may differ from the original
|
||||
geometry. Topology is preserved in the result. (Default: 0.0)
|
||||
|
||||
### Other
|
||||
|
||||
* `email=<valid email address>`
|
||||
|
||||
If you are making large numbers of request please include an appropriate email
|
||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||
|
||||
|
||||
* `debug=[0|1]`
|
||||
|
||||
Output assorted developer debug information. Data on internals of Nominatim's
|
||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||
This overrides the specified machine readable format. (Default: 0)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
* [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1)
|
||||
|
||||
```xml
|
||||
<reversegeocode timestamp="Fri, 06 Nov 09 16:33:54 +0000" querystring="...">
|
||||
<result place_id="1620612" osm_type="node" osm_id="452010817">
|
||||
135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom
|
||||
</result>
|
||||
<addressparts>
|
||||
<house_number>135</house_number>
|
||||
<road>Pilkington Avenue</road>
|
||||
<village>Wylde Green</village>
|
||||
<town>Sutton Coldfield</town>
|
||||
<city>City of Birmingham</city>
|
||||
<county>West Midlands (county)</county>
|
||||
<postcode>B72</postcode>
|
||||
<country>United Kingdom</country>
|
||||
<country_code>gb</country_code>
|
||||
</addressparts>
|
||||
</reversegeocode>
|
||||
```
|
||||
|
||||
##### Example with `format=jsonv2`
|
||||
|
||||
* [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521)
|
||||
|
||||
```json
|
||||
{
|
||||
"place_id":"134140761",
|
||||
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright",
|
||||
"osm_type":"way",
|
||||
"osm_id":"280940520",
|
||||
"lat":"-34.4391708",
|
||||
"lon":"-58.7064573",
|
||||
"place_rank":"26",
|
||||
"category":"highway",
|
||||
"type":"motorway",
|
||||
"importance":"0.1",
|
||||
"addresstype":"road",
|
||||
"display_name":"Autopista Pedro Eugenio Aramburu, El Triángulo, Partido de Malvinas Argentinas, Buenos Aires, 1.619, Argentina",
|
||||
"name":"Autopista Pedro Eugenio Aramburu",
|
||||
"address":{
|
||||
"road":"Autopista Pedro Eugenio Aramburu",
|
||||
"village":"El Triángulo",
|
||||
"state_district":"Partido de Malvinas Argentinas",
|
||||
"state":"Buenos Aires",
|
||||
"postcode":"1.619",
|
||||
"country":"Argentina",
|
||||
"country_code":"ar"
|
||||
},
|
||||
"boundingbox":["-34.44159","-34.4370994","-58.7086067","-58.7044712"]
|
||||
}
|
||||
```
|
||||
|
||||
##### Example with `format=geojson`
|
||||
|
||||
* [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"place_id": "18512203",
|
||||
"osm_type": "node",
|
||||
"osm_id": "1704756187",
|
||||
"place_rank": "30",
|
||||
"category": "place",
|
||||
"type": "house",
|
||||
"importance": "0",
|
||||
"addresstype": "place",
|
||||
"name": null,
|
||||
"display_name": "71, Via Guglielmo Marconi, Saragozza-Porto, Bologna, BO, Emilia-Romagna, 40122, Italy",
|
||||
"address": {
|
||||
"house_number": "71",
|
||||
"road": "Via Guglielmo Marconi",
|
||||
"suburb": "Saragozza-Porto",
|
||||
"city": "Bologna",
|
||||
"county": "BO",
|
||||
"state": "Emilia-Romagna",
|
||||
"postcode": "40122",
|
||||
"country": "Italy",
|
||||
"country_code": "it"
|
||||
}
|
||||
},
|
||||
"bbox": [
|
||||
11.3397676,
|
||||
44.5014307,
|
||||
11.3399676,
|
||||
44.5016307
|
||||
],
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
11.3398676,
|
||||
44.5015307
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### Example with `format=geocodejson`
|
||||
|
||||
[https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"geocoding": {
|
||||
"version": "0.1.0",
|
||||
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"licence": "ODbL",
|
||||
"query": "60.229917843587,11.16630979382"
|
||||
},
|
||||
"features": {
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"geocoding": {
|
||||
"place_id": "42700574",
|
||||
"osm_type": "node",
|
||||
"osm_id": "3110596255",
|
||||
"type": "house",
|
||||
"accuracy": 0,
|
||||
"label": "1, Løvenbergvegen, Mogreina, Ullensaker, Akershus, 2054, Norway",
|
||||
"name": null,
|
||||
"housenumber": "1",
|
||||
"street": "Løvenbergvegen",
|
||||
"postcode": "2054",
|
||||
"county": "Akershus",
|
||||
"country": "Norway",
|
||||
"admin": {
|
||||
"level7": "Ullensaker",
|
||||
"level4": "Akershus",
|
||||
"level2": "Norway"
|
||||
}
|
||||
}
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
11.1658572,
|
||||
60.2301296
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
350
docs/api/Search.md
Normal file
350
docs/api/Search.md
Normal file
@@ -0,0 +1,350 @@
|
||||
# Search queries
|
||||
|
||||
The search API allows you to look up a location from a textual description.
|
||||
Nominatim supports structured as well as free-form search queries.
|
||||
|
||||
The search query may also contain
|
||||
[special phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases)
|
||||
which are translated into specific OpenStreetMap (OSM) tags (e.g. Pub => `amenity=pub`).
|
||||
Note that this only limits the items to be found, it's not suited to return complete
|
||||
lists of OSM objects of a specific type. For those use [Overpass API](https://overpass-api.de/).
|
||||
|
||||
## Parameters
|
||||
|
||||
The search API has the following two formats:
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/search/<query>?<params>
|
||||
```
|
||||
|
||||
This format only accepts a free-form query string where the
|
||||
parts of the query are separated by slashes.
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/search?<params>
|
||||
```
|
||||
|
||||
In this form, the query may be given through two different sets of parameters:
|
||||
|
||||
* `q=<query>`
|
||||
|
||||
Free-form query string to search for.
|
||||
Free-form queries are processed first left-to-right and then right-to-left if that fails. So you may search for
|
||||
[pilkington avenue, birmingham](//nominatim.openstreetmap.org/search?q=pilkington+avenue,birmingham) as well as for
|
||||
[birmingham, pilkington avenue](//nominatim.openstreetmap.org/search?q=birmingham,+pilkington+avenue).
|
||||
Commas are optional, but improve performance by reducing the complexity of the search.
|
||||
|
||||
|
||||
* `street=<housenumber> <streetname>`
|
||||
* `city=<city>`
|
||||
* `county=<county>`
|
||||
* `state=<state>`
|
||||
* `country=<country>`
|
||||
* `postalcode=<postalcode>`
|
||||
|
||||
Alternative query string format split into several parameters for structured requests.
|
||||
Structured requests are faster but are less robust against alternative
|
||||
OSM tagging schemas. **Do not combine with** `q=<query>` **parameter**.
|
||||
|
||||
All three query forms accept the additional parameters listed below.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
Wrap JSON output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
||||
Only has an effect for JSON output formats.
|
||||
|
||||
### Output details
|
||||
|
||||
* `addressdetails=[0|1]`
|
||||
|
||||
Include a breakdown of the address into elements. (Default: 0)
|
||||
|
||||
|
||||
* `extratags=[0|1]`
|
||||
|
||||
Include additional information in the result if available,
|
||||
e.g. wikipedia link, opening hours. (Default: 0)
|
||||
|
||||
|
||||
* `namedetails=[0|1]`
|
||||
|
||||
Include a list of alternative names in the results. These may include
|
||||
language variants, references, operator and brand. (Default: 0)
|
||||
|
||||
|
||||
### Language of results
|
||||
|
||||
* `accept-language=<browser language string>`
|
||||
|
||||
Preferred language order for showing search results, overrides the value
|
||||
specified in the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language).
|
||||
Either use a standard RFC2616 accept-language string or a simple
|
||||
comma-separated list of language codes.
|
||||
|
||||
### Result limitation
|
||||
|
||||
* `countrycodes=<countrycode>[,<countrycode>][,<countrycode>]...`
|
||||
|
||||
Limit search results to one or more countries. `<countrycode>` must be the
|
||||
ISO 3166-1alpha2 code, e.g. `gb` for the United Kingdom, `de` for Germany.
|
||||
|
||||
|
||||
* `exclude_place_ids=<place_id,[place_id],[place_id]`
|
||||
|
||||
If you do not want certain OSM objects to appear in the search
|
||||
result, give a comma separated list of the `place_id`s you want to skip.
|
||||
This can be used to broaden search results. For example, if a previous
|
||||
query only returned a few results, then including those here would cause
|
||||
the search to return other, less accurate, matches (if possible).
|
||||
|
||||
|
||||
* `limit=<integer>`
|
||||
|
||||
Limit the number of returned results. (Default: 10, Maximum: 50)
|
||||
|
||||
|
||||
* `viewbox=<x1>,<y1>,<x2>,<y2>`
|
||||
|
||||
The preferred area to find search results. Any two corner points of the box
|
||||
are accepted in any order as long as they span a real box. `x` is longitude,
|
||||
`y` is latitude.
|
||||
|
||||
|
||||
* `bounded=[0|1]`
|
||||
|
||||
When a viewbox is given, restrict the result to items contained with that
|
||||
viewbox (see above). When `viewbox` and `bounded=1` are given, an amenity
|
||||
only search is allowed. In this case, give the special keyword for the
|
||||
amenity in square brackets, e.g. `[pub]`. (Default: 0)
|
||||
|
||||
|
||||
### Polygon output
|
||||
|
||||
* `polygon_geojson=1`
|
||||
* `polygon_kml=1`
|
||||
* `polygon_svg=1`
|
||||
* `polygon_text=1`
|
||||
|
||||
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
||||
options can be used at a time. (Default: 0)
|
||||
|
||||
* `polygon_threshold=0.0`
|
||||
|
||||
Simplify the output geometry before returning. The parameter is the
|
||||
tolerance in degrees with which the geometry may differ from the original
|
||||
geometry. Topology is preserved in the result. (Default: 0.0)
|
||||
|
||||
### Other
|
||||
|
||||
* `email=<valid email address>`
|
||||
|
||||
If you are making large numbers of request please include an appropriate email
|
||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||
|
||||
* `dedupe=[0|1]`
|
||||
|
||||
Sometimes you have several objects in OSM identifying the same place or
|
||||
object in reality. The simplest case is a street being split in many
|
||||
different OSM ways due to different characteristics. Nominatim will
|
||||
attempt to detect such duplicates and only return one match unless
|
||||
this parameter is set to 0. (Default: 1)
|
||||
|
||||
|
||||
|
||||
* `debug=[0|1]`
|
||||
|
||||
Output assorted developer debug information. Data on internals of Nominatim's
|
||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||
This overrides the specified machine readable format. (Default: 0)
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
##### XML with polygon points
|
||||
|
||||
* [https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1)
|
||||
* [https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1)
|
||||
* [https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1)
|
||||
|
||||
```xml
|
||||
<searchresults timestamp="Sat, 07 Nov 09 14:42:10 +0000" querystring="135 pilkington, avenue birmingham" polygon="true">
|
||||
<place
|
||||
place_id="1620612" osm_type="node" osm_id="452010817"
|
||||
boundingbox="52.548641204834,52.5488433837891,-1.81612110137939,-1.81592094898224"
|
||||
polygonpoints="[['-1.81592098644987','52.5487429714954'],['-1.81592290792183','52.5487234624632'],...]"
|
||||
lat="52.5487429714954" lon="-1.81602098644987"
|
||||
display_name="135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom"
|
||||
class="place" type="house">
|
||||
<house_number>135</house_number>
|
||||
<road>Pilkington Avenue</road>
|
||||
<village>Wylde Green</village>
|
||||
<town>Sutton Coldfield</town>
|
||||
<city>City of Birmingham</city>
|
||||
<county>West Midlands (county)</county>
|
||||
<postcode>B72</postcode>
|
||||
<country>United Kingdom</country>
|
||||
<country_code>gb</country_code>
|
||||
</place>
|
||||
</searchresults>
|
||||
```
|
||||
|
||||
##### JSON with SVG polygon
|
||||
|
||||
[https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1](https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1)
|
||||
|
||||
```json
|
||||
{
|
||||
"address": {
|
||||
"city": "Berlin",
|
||||
"city_district": "Mitte",
|
||||
"construction": "Unter den Linden",
|
||||
"continent": "European Union",
|
||||
"country": "Deutschland",
|
||||
"country_code": "de",
|
||||
"house_number": "1",
|
||||
"neighbourhood": "Scheunenviertel",
|
||||
"postcode": "10117",
|
||||
"public_building": "Kommandantenhaus",
|
||||
"state": "Berlin",
|
||||
"suburb": "Mitte"
|
||||
},
|
||||
"boundingbox": [
|
||||
"52.5170783996582",
|
||||
"52.5173187255859",
|
||||
"13.3975105285645",
|
||||
"13.3981599807739"
|
||||
],
|
||||
"class": "amenity",
|
||||
"display_name": "Kommandantenhaus, 1, Unter den Linden, Scheunenviertel, Mitte, Berlin, 10117, Deutschland, European Union",
|
||||
"importance": 0.73606775332943,
|
||||
"lat": "52.51719785",
|
||||
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
||||
"lon": "13.3978352028938",
|
||||
"osm_id": "15976890",
|
||||
"osm_type": "way",
|
||||
"place_id": "30848715",
|
||||
"svg": "M 13.397511 -52.517283599999999 L 13.397829400000001 -52.517299800000004 13.398131599999999 -52.517315099999998 13.398159400000001 -52.517112099999999 13.3975388 -52.517080700000001 Z",
|
||||
"type": "public_building"
|
||||
}
|
||||
```
|
||||
|
||||
##### JSON with address details
|
||||
|
||||
[https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1](https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1)
|
||||
|
||||
```json
|
||||
{
|
||||
"address": {
|
||||
"bakery": "B\u00e4cker Kamps",
|
||||
"city_district": "Mitte",
|
||||
"continent": "European Union",
|
||||
"country": "Deutschland",
|
||||
"country_code": "de",
|
||||
"footway": "Bahnsteig U6",
|
||||
"neighbourhood": "Sprengelkiez",
|
||||
"postcode": "13353",
|
||||
"state": "Berlin",
|
||||
"suburb": "Wedding"
|
||||
},
|
||||
"boundingbox": [
|
||||
"52.5460929870605",
|
||||
"52.5460968017578",
|
||||
"13.3591794967651",
|
||||
"13.3591804504395"
|
||||
],
|
||||
"class": "shop",
|
||||
"display_name": "B\u00e4cker Kamps, Bahnsteig U6, Sprengelkiez, Wedding, Mitte, Berlin, 13353, Deutschland, European Union",
|
||||
"icon": "https://nominatim.openstreetmap.org/images/mapicons/shopping_bakery.p.20.png",
|
||||
"importance": 0.201,
|
||||
"lat": "52.5460941",
|
||||
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
||||
"lon": "13.35918",
|
||||
"osm_id": "317179427",
|
||||
"osm_type": "node",
|
||||
"place_id": "1453068",
|
||||
"type": "bakery"
|
||||
}
|
||||
```
|
||||
|
||||
##### GeoJSON
|
||||
|
||||
[https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson](https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"place_id": "35811445",
|
||||
"osm_type": "node",
|
||||
"osm_id": "2846295644",
|
||||
"display_name": "17, Strada Pictor Alexandru Romano, Bukarest, Bucharest, Sector 2, Bucharest, 023964, Romania",
|
||||
"place_rank": "30",
|
||||
"category": "place",
|
||||
"type": "house",
|
||||
"importance": 0.62025
|
||||
},
|
||||
"bbox": [
|
||||
26.1156689,
|
||||
44.4354754,
|
||||
26.1157689,
|
||||
44.4355754
|
||||
],
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
26.1157189,
|
||||
44.4355254
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### GeocodeJSON
|
||||
|
||||
[https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson](https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"geocoding": {
|
||||
"version": "0.1.0",
|
||||
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||
"licence": "ODbL",
|
||||
"query": "Αγία Τριάδα, Αδωνιδος, Athens, Greece"
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"geocoding": {
|
||||
"type": "place_of_worship",
|
||||
"label": "Αγία Τριάδα, Αδωνιδος, Άγιος Νικόλαος, 5º Δημοτικό Διαμέρισμα Αθηνών, Athens, Municipality of Athens, Regional Unit of Central Athens, Region of Attica, Attica, 11472, Greece",
|
||||
"name": "Αγία Τριάδα",
|
||||
"admin": null
|
||||
}
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
23.72949633941,
|
||||
38.0051697
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
7
docs/bash2md.sh
Executable file
7
docs/bash2md.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Extract markdown-formatted documentation from a source file
|
||||
#
|
||||
# Usage: bash2md.sh <infile> <outfile>
|
||||
|
||||
sed '/^#!/d;s:^#\( \|$\)::;s/.*#DOCS://' $1 > $2
|
||||
4
docs/data-sources/overview.md
Normal file
4
docs/data-sources/overview.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Additional Data Sources
|
||||
|
||||
This guide explains how data sources other than OpenStreetMap mentioned in
|
||||
the install instructions got obtained and converted.
|
||||
36
docs/develop/Documentation.md
Normal file
36
docs/develop/Documentation.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Documentation Pages
|
||||
|
||||
The [Nominatim documentation](https://nominatim.org/release-docs/develop/) is built using the [MkDocs](https://www.mkdocs.org/) static site generation framework. The master branch is automatically deployed every night on under [https://nominatim.org/release-docs/develop/]()
|
||||
|
||||
To preview local changes:
|
||||
|
||||
1. Install MkDocs
|
||||
|
||||
```
|
||||
pip3 install --user mkdocs
|
||||
```
|
||||
|
||||
|
||||
2. In build directory run
|
||||
|
||||
```
|
||||
make doc
|
||||
INFO - Cleaning site directory
|
||||
INFO - Building documentation to directory: /home/vagrant/build/site-html
|
||||
```
|
||||
|
||||
This runs `mkdocs build` plus extra transformion of some files and adds symlinks (see `CMakeLists.txt` for the exact steps).
|
||||
|
||||
|
||||
3. Start webserver for local testing
|
||||
|
||||
```
|
||||
mkdocs serve
|
||||
[server:296] Serving on http://127.0.0.1:8000
|
||||
[handlers:62] Start watching changes
|
||||
```
|
||||
|
||||
If you develop inside a Vagrant virtual machine:
|
||||
* add port forwarding to your Vagrantfile, e.g. `config.vm.network "forwarded_port", guest: 8000, host: 8000`
|
||||
* use `mkdocs serve --dev-addr 0.0.0.0:8000` because the default localhost
|
||||
IP does not get forwarded.
|
||||
170
docs/develop/Import.md
Normal file
170
docs/develop/Import.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# OSM Data Import
|
||||
|
||||
OSM data is initially imported using osm2pgsql. Nominatim uses its own data
|
||||
output style 'gazetteer', which differs from the output style created for
|
||||
map rendering.
|
||||
|
||||
## Database Layout
|
||||
|
||||
The gazetteer style produces a single table `place` with the following rows:
|
||||
|
||||
* `osm_type` - kind of OSM object (**N** - node, **W** - way, **R** - relation)
|
||||
* `osm_id` - original OSM ID
|
||||
* `class` - key of principal tag defining the object type
|
||||
* `type` - value of principal tag defining the object type
|
||||
* `name` - collection of tags that contain a name or reference
|
||||
* `admin_level` - numerical value of the tagged administrative level
|
||||
* `address` - collection of tags defining the address of an object
|
||||
* `extratags` - collection of additional interesting tags that are not
|
||||
directly relevant for searching
|
||||
* `geometry` - geometry of the object (in WGS84)
|
||||
|
||||
A single OSM object may appear multiple times in this table when it is tagged
|
||||
with multiple tags that may constitute a principal tag. Take for example a
|
||||
motorway bridge. In OSM, this would be a way which is tagged with
|
||||
`highway=motorway` and `bridge=yes`. This way would appear in the `place` table
|
||||
once with `class` of `highway` and once with a `class` of `bridge`. Thus the
|
||||
*unique key* for `place` is (`osm_type`, `osm_id`, `class`).
|
||||
|
||||
## Configuring the Import
|
||||
|
||||
How tags are interpreted and assigned to the different `place` columns can be
|
||||
configured via the import style configuration file (`CONST_Import_style`). This
|
||||
is a JSON file which contains a list of rules which are matched against every
|
||||
tag of every object and then assign the tag its specific role.
|
||||
|
||||
### Configuration Rules
|
||||
|
||||
A single rule looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys" : ["key1", "key2", ...],
|
||||
"values" : {
|
||||
"value1" : "prop",
|
||||
"value2" : "prop1,prop2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A rule first defines a list of keys to apply the rule to. This is always a list
|
||||
of strings. The string may have four forms. An empty string matches against
|
||||
any key. A string that ends in an asterisk `*` is a prefix match and accordingly
|
||||
matches against any key that starts with the given string (minus the `*`). A
|
||||
suffix match can be defined similarly with a string that starts with a `*`. Any
|
||||
other string constitutes an exact match.
|
||||
|
||||
The second part of the rules defines a list of values and the properties that
|
||||
apply to a successful match. Value strings may be either empty, which
|
||||
means that they match any value, or describe an exact match. Prefix
|
||||
or suffix matching of values is not possible.
|
||||
|
||||
For a rule to match, it has to find a valid combination of keys and values. The
|
||||
resulting property is that of the matched values.
|
||||
|
||||
The rules in a configuration file are processed sequentially and the first
|
||||
match for each tag wins.
|
||||
|
||||
A rule where key and value are the empty string is special. This defines the
|
||||
fallback when none of the rules match. The fallback is always used as a last
|
||||
resort when nothing else matches, no matter where the rule appears in the file.
|
||||
Defining multiple fallback rules is not allowed. What happens in this case,
|
||||
is undefined.
|
||||
|
||||
### Tag Properties
|
||||
|
||||
One or more of the following properties may be given for each tag:
|
||||
|
||||
* `main`
|
||||
|
||||
A principal tag. A new row will be added for the object with key and value
|
||||
as `class` and `type`.
|
||||
|
||||
* `with_name`
|
||||
|
||||
When the tag is a principal tag (`main` property set): only really add a new
|
||||
row, if there is any name tag found (a reference tag is not sufficient, see
|
||||
below).
|
||||
|
||||
* `with_name_key`
|
||||
|
||||
When the tag is a principal tag (`main` property set): only really add a new
|
||||
row, if there is also a name tag that matches the key of the principal tag.
|
||||
For example, if the main tag is `bridge=yes`, then it will only be added as
|
||||
an extra row, if there is a tag `bridge:name[:XXX]` for the same object.
|
||||
If this property is set, all other names that are not domain-specific are
|
||||
ignored.
|
||||
|
||||
* `fallback`
|
||||
|
||||
When the tag is a principal tag (`main` property set): only really add a new
|
||||
row, when no other principal tags for this object have been found. Only one
|
||||
fallback tag can win for an object.
|
||||
|
||||
* `operator`
|
||||
|
||||
When the tag is a principal tag (`main` property set): also include the
|
||||
`operator` tag in the list of names. This is a special construct for an
|
||||
out-dated tagging practise in OSM. Fuel stations and chain restaurants
|
||||
in particular used to have the name of the chain tagged as `operator`.
|
||||
These days the chain can be more commonly found in the `brand` tag but
|
||||
there is still enough old data around to warrant this special case.
|
||||
|
||||
* `name`
|
||||
|
||||
Add tag to the list of names.
|
||||
|
||||
* `ref`
|
||||
|
||||
Add tag to the list of names as a reference. At the moment this only means
|
||||
that the object is not considered to be named for `with_name`.
|
||||
|
||||
* `address`
|
||||
|
||||
Add tag to the list of address tags. If the tag starts with `addr:` or
|
||||
`is_in:`, then this prefix is cut off before adding it to the list.
|
||||
|
||||
* `postcode`
|
||||
|
||||
Add the value as a postcode to the address tags. If multiple tags are
|
||||
candidate for postcodes, one wins out and the others are dropped.
|
||||
|
||||
* `country`
|
||||
|
||||
Add the value as a country code to the address tags. The value must be a
|
||||
two letter country code, otherwise it is ignored. If there are multiple
|
||||
tags that match, then one wins out and the others are dropped.
|
||||
|
||||
* `house`
|
||||
|
||||
If no principle tags can be found for the object, still add the object with
|
||||
`class`=`place` and `type`=`house`. Use this for address nodes that have no
|
||||
other function.
|
||||
|
||||
* `interpolation`
|
||||
|
||||
Add this object as an address interpolation (appears as `class`=`place` and
|
||||
`type`=`houses` in the database).
|
||||
|
||||
* `extra`
|
||||
|
||||
Add tag to the list of extra tags.
|
||||
|
||||
* `skip`
|
||||
|
||||
Skip the tag completely. Useful when a custom default fallback is defined
|
||||
or to define exceptions to rules.
|
||||
|
||||
A rule can define as many of these properties for one match as it likes. For
|
||||
example, if the property is `"main,extra"` then the tag will open a new row
|
||||
but also have the tag appear in the list of extra tags.
|
||||
|
||||
There are a number of pre-defined styles in the `settings/` directory. It is
|
||||
advisable to start from one of these styles when defining your own.
|
||||
|
||||
### Changing the Style of Existing Databases
|
||||
|
||||
There is normally no issue changing the style of a database that is already
|
||||
imported and now kept up-to-date with change files. Just be aware that any
|
||||
change in the style applies to updates only. If you want to change the data
|
||||
that is already in the database, then a reimport is necessary.
|
||||
90
docs/develop/Ranking.md
Normal file
90
docs/develop/Ranking.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Place Ranking in Nominatim
|
||||
|
||||
Nominatim uses two metrics to rank a place: search rank and address rank.
|
||||
Both can be assigned a value between 0 and 30. They serve slightly
|
||||
different purposes, which are explained in this chapter.
|
||||
|
||||
## Search rank
|
||||
|
||||
The search rank describes the extent and importance of a place. It is used
|
||||
when ranking search result. Simply put, if there are two results for a
|
||||
search query which are otherwise equal, then the result with the _lower_
|
||||
search rank will be appear higher in the result list.
|
||||
|
||||
Search ranks are not so important these days because many well-known
|
||||
places use the Wikipedia importance ranking instead.
|
||||
|
||||
## Address rank
|
||||
|
||||
The address rank describes where a place shows up in an address hierarchy.
|
||||
Usually only administrative boundaries and place nodes and areas are
|
||||
eligible to be part of an address. All other objects have an address rank
|
||||
of 0.
|
||||
|
||||
Note that the search rank of a place plays a role in the address computation
|
||||
as well. When collecting the places that should make up the address parts
|
||||
then only places are taken into account that have a lower address rank than
|
||||
the search rank of the base object.
|
||||
|
||||
## Rank configuration
|
||||
|
||||
Search and address ranks are assigned to a place when it is first imported
|
||||
into the database. There are a few hard-coded rules for the assignment:
|
||||
|
||||
* postcodes follow special rules according to their length
|
||||
* boundaries that are not areas and railway=rail are dropped completely
|
||||
* the following are always search rank 30 and address rank 0:
|
||||
* highway nodes
|
||||
* landuse that is not an area
|
||||
|
||||
Other than that, the ranks can be freely assigned via the JSON file
|
||||
defined with `CONST_Address_Level_Config` according to their type and
|
||||
the country they are in.
|
||||
|
||||
The address level configuration must consist of an array of configuration
|
||||
entries, each containing a tag definition and an optional country array:
|
||||
|
||||
```
|
||||
[ {
|
||||
"tags" : {
|
||||
"place" : {
|
||||
"county" : 12,
|
||||
"city" : 16,
|
||||
},
|
||||
"landuse" : {
|
||||
"residential" : 22,
|
||||
"" : 30
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"countries" : [ "ca", "us" ],
|
||||
"tags" : {
|
||||
"boundary" : {
|
||||
"administrative8" : 18,
|
||||
"administrative9" : 20
|
||||
},
|
||||
"landuse" : {
|
||||
"residential" : [22, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The `countries` field contains a list of countries (as ISO 3166-1 alpha 2 code)
|
||||
for which the definition applies. When the field is omitted, then the
|
||||
definition is used as a fallback, when nothing more specific for a given
|
||||
country exists.
|
||||
|
||||
`tags` contains the ranks for key/value pairs. The ranks can be either a
|
||||
single number, in which case they are the search and address rank, or an array
|
||||
of search and address rank (in that order). The value may be left empty.
|
||||
Then the rank is used when no more specific value is found for the given
|
||||
key.
|
||||
|
||||
Countries and key/value combination may appear in multiple definitions. Just
|
||||
make sure that each combination of counrty/key/value appears only once per
|
||||
file. Otherwise the import will fail with a UNIQUE INDEX constraint violation
|
||||
on import.
|
||||
|
||||
24
docs/develop/overview.md
Normal file
24
docs/develop/overview.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Basic Architecture
|
||||
|
||||
Nominatim provides geocoding based on OpenStreetMap data. It uses a PostgreSQL
|
||||
database as a backend for storing the data.
|
||||
|
||||
There are three basic parts to Nominatim's architecture: the data import,
|
||||
the address computation and the search frontend.
|
||||
|
||||
The __data import__ stage reads the raw OSM data and extracts all information
|
||||
that is useful for geocoding. This part is done by osm2pgsql, the same tool
|
||||
that can also be used to import a rendering database. It uses the special
|
||||
gazetteer output plugin in `osm2pgsql/output-gazetter.[ch]pp`. The result of
|
||||
the import can be found in the database table `place`.
|
||||
|
||||
The __address computation__ or __indexing__ stage takes the data from `place`
|
||||
and adds additional information needed for geocoding. It ranks the places by
|
||||
importance, links objects that belong together and computes addresses and
|
||||
the search index. Most of this work is done in PL/pgSQL via database triggers
|
||||
and can be found in the file `sql/functions.sql`.
|
||||
|
||||
The __search frontend__ implements the actual API. It takes search
|
||||
and reverse geocoding queries from the user, looks up the data and
|
||||
returns the results in the requested format. This part is written in PHP
|
||||
and can be found in the `lib/` and `website/` directories.
|
||||
3
docs/extra.css
Normal file
3
docs/extra.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.toctree-l3 {
|
||||
display: none!important
|
||||
}
|
||||
8
docs/index.md
Normal file
8
docs/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and address and to generate synthetic addresses of OSM points (reverse geocoding).
|
||||
|
||||
This guide comes in three parts:
|
||||
|
||||
* __[API reference](api/Overview.md)__ for users of Nominatim
|
||||
* __[Administration Guide](admin/Installation.md)__ for those who want
|
||||
to install their own Nominatim server
|
||||
* __[Developer's Guide](develop/overview.md)__ for developers of the software
|
||||
41
docs/mkdocs.yml
Normal file
41
docs/mkdocs.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
site_name: Nominatim Documentation
|
||||
theme: readthedocs
|
||||
docs_dir: ${CMAKE_CURRENT_BINARY_DIR}
|
||||
site_url: http://nominatim.org
|
||||
repo_url: https://github.com/openstreetmap/Nominatim
|
||||
pages:
|
||||
- 'Introduction' : 'index.md'
|
||||
- 'API Reference':
|
||||
- 'Overview': 'api/Overview.md'
|
||||
- 'Search': 'api/Search.md'
|
||||
- 'Reverse': 'api/Reverse.md'
|
||||
- 'Address Lookup': 'api/Lookup.md'
|
||||
- 'Details' : 'api/Details.md'
|
||||
- 'Place Output Formats': 'api/Output.md'
|
||||
- 'FAQ': 'api/Faq.md'
|
||||
- 'Administration Guide':
|
||||
- 'Basic Installation': 'admin/Installation.md'
|
||||
- 'Importing and Updating' : 'admin/Import-and-Update.md'
|
||||
- 'Migration from older Versions' : 'admin/Migration.md'
|
||||
- 'Troubleshooting' : 'admin/Faq.md'
|
||||
- 'Developers Guide':
|
||||
- 'Overview' : 'develop/overview.md'
|
||||
- 'OSM Data Import' : 'develop/Import.md'
|
||||
- 'Place Ranking' : 'develop/Ranking.md'
|
||||
- 'Documentation' : 'develop/Documentation.md'
|
||||
- 'External Data Sources':
|
||||
- 'Overview' : 'data-sources/overview.md'
|
||||
- 'US Census (Tiger)': 'data-sources/US-Tiger.md'
|
||||
- 'GB Postcodes': 'data-sources/GB-Postcodes.md'
|
||||
- 'Country Grid': 'data-sources/Country-Grid.md'
|
||||
- 'Wikipedia & Wikidata': 'data-sources/Wikipedia-Wikidata.md'
|
||||
- 'Appendix':
|
||||
- 'Installation on CentOS 7' : 'appendix/Install-on-Centos-7.md'
|
||||
- 'Installation on Ubuntu 16' : 'appendix/Install-on-Ubuntu-16.md'
|
||||
- 'Installation on Ubuntu 18' : 'appendix/Install-on-Ubuntu-18.md'
|
||||
markdown_extensions:
|
||||
- codehilite:
|
||||
use_pygments: False
|
||||
- toc:
|
||||
permalink:
|
||||
extra_css: [extra.css]
|
||||
122
lib/AddressDetails.php
Normal file
122
lib/AddressDetails.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/ClassTypes.php');
|
||||
|
||||
/**
|
||||
* Detailed list of address parts for a single result
|
||||
*/
|
||||
class AddressDetails
|
||||
{
|
||||
private $aAddressLines;
|
||||
|
||||
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
||||
{
|
||||
if (is_array($mLangPref)) {
|
||||
$mLangPref = $oDB->getArraySQL($oDB->getDBQuotedList($mLangPref));
|
||||
}
|
||||
|
||||
if (!isset($sHousenumber)) {
|
||||
$sHousenumber = -1;
|
||||
}
|
||||
|
||||
$sSQL = 'SELECT *,';
|
||||
$sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
|
||||
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
||||
$sSQL .= ' ORDER BY rank_address DESC, isaddress DESC';
|
||||
|
||||
$this->aAddressLines = $oDB->getAll($sSQL);
|
||||
}
|
||||
|
||||
private static function isAddress($aLine)
|
||||
{
|
||||
return $aLine['isaddress'] || $aLine['type'] == 'country_code';
|
||||
}
|
||||
|
||||
public function getAddressDetails($bAll = false)
|
||||
{
|
||||
if ($bAll) {
|
||||
return $this->aAddressLines;
|
||||
}
|
||||
|
||||
return array_filter($this->aAddressLines, array(__CLASS__, 'isAddress'));
|
||||
}
|
||||
|
||||
public function getLocaleAddress()
|
||||
{
|
||||
$aParts = array();
|
||||
$sPrevResult = '';
|
||||
|
||||
foreach ($this->aAddressLines as $aLine) {
|
||||
if ($aLine['isaddress'] && $sPrevResult != $aLine['localname']) {
|
||||
$sPrevResult = $aLine['localname'];
|
||||
$aParts[] = $sPrevResult;
|
||||
}
|
||||
}
|
||||
|
||||
return join(', ', $aParts);
|
||||
}
|
||||
|
||||
public function getAddressNames()
|
||||
{
|
||||
$aAddress = array();
|
||||
$aFallback = array();
|
||||
|
||||
foreach ($this->aAddressLines as $aLine) {
|
||||
if (!self::isAddress($aLine)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bFallback = false;
|
||||
$aTypeLabel = ClassTypes\getInfo($aLine);
|
||||
|
||||
if ($aTypeLabel === false) {
|
||||
$aTypeLabel = ClassTypes\getFallbackInfo($aLine);
|
||||
$bFallback = true;
|
||||
}
|
||||
|
||||
$sName = false;
|
||||
if (isset($aLine['localname']) && $aLine['localname']) {
|
||||
$sName = $aLine['localname'];
|
||||
} elseif (isset($aLine['housenumber']) && $aLine['housenumber']) {
|
||||
$sName = $aLine['housenumber'];
|
||||
}
|
||||
|
||||
if ($sName) {
|
||||
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel']) ? $aTypeLabel['simplelabel'] : $aTypeLabel['label']);
|
||||
$sTypeLabel = str_replace(' ', '_', $sTypeLabel);
|
||||
if (!isset($aAddress[$sTypeLabel])
|
||||
|| isset($aFallback[$sTypeLabel])
|
||||
|| $aLine['class'] == 'place'
|
||||
) {
|
||||
$aAddress[$sTypeLabel] = $sName;
|
||||
if ($bFallback) {
|
||||
$aFallback[$sTypeLabel] = $bFallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aAddress;
|
||||
}
|
||||
|
||||
public function getAdminLevels()
|
||||
{
|
||||
$aAddress = array();
|
||||
foreach (array_reverse($this->aAddressLines) as $aLine) {
|
||||
if (self::isAddress($aLine)
|
||||
&& isset($aLine['admin_level'])
|
||||
&& $aLine['admin_level'] < 15
|
||||
&& !isset($aAddress['level'.$aLine['admin_level']])
|
||||
) {
|
||||
$aAddress['level'.$aLine['admin_level']] = $aLine['localname'];
|
||||
}
|
||||
}
|
||||
return $aAddress;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return $this->aAddressLines;
|
||||
}
|
||||
}
|
||||
374
lib/ClassTypes.php
Normal file
374
lib/ClassTypes.php
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\ClassTypes;
|
||||
|
||||
function getInfo($aPlace)
|
||||
{
|
||||
$aClassType = getList();
|
||||
|
||||
if (isset($aPlace['admin_level'])) {
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||
if (isset($aClassType[$sName])) {
|
||||
return $aClassType[$sName];
|
||||
}
|
||||
}
|
||||
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||
if (isset($aClassType[$sName])) {
|
||||
return $aClassType[$sName];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFallbackInfo($aPlace)
|
||||
{
|
||||
$aClassType = getList();
|
||||
|
||||
$sFallback = 'boundary:administrative:'.((int)($aPlace['rank_address']/2));
|
||||
if (isset($aClassType[$sFallback])) {
|
||||
return $aClassType[$sFallback];
|
||||
}
|
||||
|
||||
return array('simplelabel' => 'address'.$aPlace['rank_address']);
|
||||
}
|
||||
|
||||
function getProperty($aPlace, $sProp, $mDefault = false)
|
||||
{
|
||||
$aClassType = getList();
|
||||
|
||||
if (isset($aPlace['admin_level'])) {
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
||||
return $aClassType[$sName][$sProp];
|
||||
}
|
||||
}
|
||||
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
||||
return $aClassType[$sName][$sProp];
|
||||
}
|
||||
|
||||
return $mDefault;
|
||||
}
|
||||
|
||||
function getListWithImportance()
|
||||
{
|
||||
static $aOrders = null;
|
||||
if ($aOrders === null) {
|
||||
$aOrders = getList();
|
||||
$i = 1;
|
||||
foreach ($aOrders as $sID => $a) {
|
||||
$aOrders[$sID]['importance'] = $i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $aOrders;
|
||||
}
|
||||
|
||||
function getList()
|
||||
{
|
||||
return array(
|
||||
'boundary:administrative:1' => array('label' => 'Continent', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:2' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:country' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 6, 'defdiameter' => 15),
|
||||
'boundary:administrative:3' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:4' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:state' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12),
|
||||
'boundary:administrative:5' => array('label' => 'State District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:6' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:7' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:county' => array('label' => 'County', 'frequency' => 108, 'icon' => 'poi_boundary_administrative', 'defzoom' => 10, 'defdiameter' => 1.28),
|
||||
'boundary:administrative:8' => array('label' => 'City', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:city' => array('label' => 'City', 'frequency' => 66, 'icon' => 'poi_place_city', 'defzoom' => 12, 'defdiameter' => 0.32),
|
||||
'boundary:administrative:9' => array('label' => 'City District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:10' => array('label' => 'Suburb', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:administrative:11' => array('label' => 'Neighbourhood', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:region' => array('label' => 'Region', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 0.04),
|
||||
'place:island' => array('label' => 'Island', 'frequency' => 288, 'defzoom' => 11, 'defdiameter' => 0.64),
|
||||
'boundary:administrative' => array('label' => 'Administrative', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'boundary:postal_code' => array('label' => 'Postcode', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||
'place:town' => array('label' => 'Town', 'frequency' => 1497, 'icon' => 'poi_place_town', 'defzoom' => 14, 'defdiameter' => 0.08),
|
||||
'place:village' => array('label' => 'Village', 'frequency' => 11230, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
||||
'place:hamlet' => array('label' => 'Hamlet', 'frequency' => 7075, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
||||
'place:suburb' => array('label' => 'Suburb', 'frequency' => 2528, 'icon' => 'poi_place_village', 'defdiameter' => 0.04),
|
||||
'place:locality' => array('label' => 'Locality', 'frequency' => 4113, 'icon' => 'poi_place_village', 'defdiameter' => 0.02),
|
||||
'landuse:farm' => array('label' => 'Farm', 'frequency' => 1201, 'defdiameter' => 0.02),
|
||||
'place:farm' => array('label' => 'Farm', 'frequency' => 1162, 'defdiameter' => 0.02),
|
||||
|
||||
'highway:motorway_junction' => array('label' => 'Motorway Junction', 'frequency' => 1126, 'simplelabel' => 'Junction'),
|
||||
'highway:motorway' => array('label' => 'Motorway', 'frequency' => 4627, 'simplelabel' => 'Road'),
|
||||
'highway:trunk' => array('label' => 'Trunk', 'frequency' => 23084, 'simplelabel' => 'Road'),
|
||||
'highway:primary' => array('label' => 'Primary', 'frequency' => 32138, 'simplelabel' => 'Road'),
|
||||
'highway:secondary' => array('label' => 'Secondary', 'frequency' => 25807, 'simplelabel' => 'Road'),
|
||||
'highway:tertiary' => array('label' => 'Tertiary', 'frequency' => 29829, 'simplelabel' => 'Road'),
|
||||
'highway:residential' => array('label' => 'Residential', 'frequency' => 361498, 'simplelabel' => 'Road'),
|
||||
'highway:unclassified' => array('label' => 'Unclassified', 'frequency' => 66441, 'simplelabel' => 'Road'),
|
||||
'highway:living_street' => array('label' => 'Living Street', 'frequency' => 710, 'simplelabel' => 'Road'),
|
||||
'highway:service' => array('label' => 'Service', 'frequency' => 9963, 'simplelabel' => 'Road'),
|
||||
'highway:track' => array('label' => 'Track', 'frequency' => 2565, 'simplelabel' => 'Road'),
|
||||
'highway:road' => array('label' => 'Road', 'frequency' => 591, 'simplelabel' => 'Road'),
|
||||
'highway:byway' => array('label' => 'Byway', 'frequency' => 346, 'simplelabel' => 'Road'),
|
||||
'highway:bridleway' => array('label' => 'Bridleway', 'frequency' => 1556),
|
||||
'highway:cycleway' => array('label' => 'Cycleway', 'frequency' => 2419),
|
||||
'highway:pedestrian' => array('label' => 'Pedestrian', 'frequency' => 2757),
|
||||
'highway:footway' => array('label' => 'Footway', 'frequency' => 15008),
|
||||
'highway:steps' => array('label' => 'Steps', 'frequency' => 444, 'simplelabel' => 'Footway'),
|
||||
'highway:motorway_link' => array('label' => 'Motorway Link', 'frequency' => 795, 'simplelabel' => 'Road'),
|
||||
'highway:trunk_link' => array('label' => 'Trunk Link', 'frequency' => 1258, 'simplelabel' => 'Road'),
|
||||
'highway:primary_link' => array('label' => 'Primary Link', 'frequency' => 313, 'simplelabel' => 'Road'),
|
||||
|
||||
'landuse:industrial' => array('label' => 'Industrial', 'frequency' => 1062),
|
||||
'landuse:residential' => array('label' => 'Residential', 'frequency' => 886),
|
||||
'landuse:retail' => array('label' => 'Retail', 'frequency' => 754),
|
||||
'landuse:commercial' => array('label' => 'Commercial', 'frequency' => 657),
|
||||
|
||||
'place:airport' => array('label' => 'Airport', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||
'aeroway:aerodrome' => array('label' => 'Aerodrome', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||
'aeroway' => array('label' => 'Aeroway', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||
'railway:station' => array('label' => 'Station', 'frequency' => 3431, 'icon' => 'transport_train_station2', 'defdiameter' => 0.01),
|
||||
'amenity:place_of_worship' => array('label' => 'Place Of Worship', 'frequency' => 9049, 'icon' => 'place_of_worship_unknown3'),
|
||||
'amenity:pub' => array('label' => 'Pub', 'frequency' => 18969, 'icon' => 'food_pub'),
|
||||
'amenity:bar' => array('label' => 'Bar', 'frequency' => 164, 'icon' => 'food_bar'),
|
||||
'amenity:university' => array('label' => 'University', 'frequency' => 607, 'icon' => 'education_university'),
|
||||
'tourism:museum' => array('label' => 'Museum', 'frequency' => 543, 'icon' => 'tourist_museum'),
|
||||
'amenity:arts_centre' => array('label' => 'Arts Centre', 'frequency' => 136, 'icon' => 'tourist_art_gallery2'),
|
||||
'tourism:zoo' => array('label' => 'Zoo', 'frequency' => 47, 'icon' => 'tourist_zoo'),
|
||||
'tourism:theme_park' => array('label' => 'Theme Park', 'frequency' => 24, 'icon' => 'poi_point_of_interest'),
|
||||
'tourism:attraction' => array('label' => 'Attraction', 'frequency' => 1463, 'icon' => 'poi_point_of_interest'),
|
||||
'leisure:golf_course' => array('label' => 'Golf Course', 'frequency' => 712, 'icon' => 'sport_golf'),
|
||||
'historic:castle' => array('label' => 'Castle', 'frequency' => 316, 'icon' => 'tourist_castle'),
|
||||
'amenity:hospital' => array('label' => 'Hospital', 'frequency' => 879, 'icon' => 'health_hospital'),
|
||||
'amenity:school' => array('label' => 'School', 'frequency' => 8192, 'icon' => 'education_school'),
|
||||
'amenity:theatre' => array('label' => 'Theatre', 'frequency' => 371, 'icon' => 'tourist_theatre'),
|
||||
'amenity:public_building' => array('label' => 'Public Building', 'frequency' => 985),
|
||||
'amenity:library' => array('label' => 'Library', 'frequency' => 794, 'icon' => 'amenity_library'),
|
||||
'amenity:townhall' => array('label' => 'Townhall', 'frequency' => 242),
|
||||
'amenity:community_centre' => array('label' => 'Community Centre', 'frequency' => 157),
|
||||
'amenity:fire_station' => array('label' => 'Fire Station', 'frequency' => 221, 'icon' => 'amenity_firestation3'),
|
||||
'amenity:police' => array('label' => 'Police', 'frequency' => 334, 'icon' => 'amenity_police2'),
|
||||
'amenity:bank' => array('label' => 'Bank', 'frequency' => 1248, 'icon' => 'money_bank2'),
|
||||
'amenity:post_office' => array('label' => 'Post Office', 'frequency' => 859, 'icon' => 'amenity_post_office'),
|
||||
'leisure:park' => array('label' => 'Park', 'frequency' => 2378),
|
||||
'amenity:park' => array('label' => 'Park', 'frequency' => 53),
|
||||
'landuse:park' => array('label' => 'Park', 'frequency' => 50),
|
||||
'landuse:recreation_ground' => array('label' => 'Recreation Ground', 'frequency' => 517),
|
||||
'tourism:hotel' => array('label' => 'Hotel', 'frequency' => 2150, 'icon' => 'accommodation_hotel2'),
|
||||
'tourism:motel' => array('label' => 'Motel', 'frequency' => 43),
|
||||
'amenity:cinema' => array('label' => 'Cinema', 'frequency' => 277, 'icon' => 'tourist_cinema'),
|
||||
'tourism:artwork' => array('label' => 'Artwork', 'frequency' => 171, 'icon' => 'tourist_art_gallery2'),
|
||||
'historic:archaeological_site' => array('label' => 'Archaeological Site', 'frequency' => 407, 'icon' => 'tourist_archaeological2'),
|
||||
'amenity:doctors' => array('label' => 'Doctors', 'frequency' => 581, 'icon' => 'health_doctors'),
|
||||
'leisure:sports_centre' => array('label' => 'Sports Centre', 'frequency' => 767, 'icon' => 'sport_leisure_centre'),
|
||||
'leisure:swimming_pool' => array('label' => 'Swimming Pool', 'frequency' => 24, 'icon' => 'sport_swimming_outdoor'),
|
||||
'shop:supermarket' => array('label' => 'Supermarket', 'frequency' => 2673, 'icon' => 'shopping_supermarket'),
|
||||
'shop:convenience' => array('label' => 'Convenience', 'frequency' => 1469, 'icon' => 'shopping_convenience'),
|
||||
'amenity:restaurant' => array('label' => 'Restaurant', 'frequency' => 3179, 'icon' => 'food_restaurant'),
|
||||
'amenity:fast_food' => array('label' => 'Fast Food', 'frequency' => 2289, 'icon' => 'food_fastfood'),
|
||||
'amenity:cafe' => array('label' => 'Cafe', 'frequency' => 1780, 'icon' => 'food_cafe'),
|
||||
'tourism:guest_house' => array('label' => 'Guest House', 'frequency' => 223, 'icon' => 'accommodation_bed_and_breakfast'),
|
||||
'amenity:pharmacy' => array('label' => 'Pharmacy', 'frequency' => 733, 'icon' => 'health_pharmacy_dispensing'),
|
||||
'amenity:fuel' => array('label' => 'Fuel', 'frequency' => 1308, 'icon' => 'transport_fuel'),
|
||||
'natural:peak' => array('label' => 'Peak', 'frequency' => 3212, 'icon' => 'poi_peak'),
|
||||
'waterway:waterfall' => array('label' => 'Waterfall', 'frequency' => 24),
|
||||
'natural:wood' => array('label' => 'Wood', 'frequency' => 1845, 'icon' => 'landuse_coniferous_and_deciduous'),
|
||||
'natural:water' => array('label' => 'Water', 'frequency' => 1790),
|
||||
'landuse:forest' => array('label' => 'Forest', 'frequency' => 467),
|
||||
'landuse:cemetery' => array('label' => 'Cemetery', 'frequency' => 463),
|
||||
'landuse:allotments' => array('label' => 'Allotments', 'frequency' => 408),
|
||||
'landuse:farmyard' => array('label' => 'Farmyard', 'frequency' => 397),
|
||||
'railway:rail' => array('label' => 'Rail', 'frequency' => 4894),
|
||||
'waterway:canal' => array('label' => 'Canal', 'frequency' => 1723),
|
||||
'waterway:river' => array('label' => 'River', 'frequency' => 4089),
|
||||
'waterway:stream' => array('label' => 'Stream', 'frequency' => 2684),
|
||||
'shop:bicycle' => array('label' => 'Bicycle', 'frequency' => 349, 'icon' => 'shopping_bicycle'),
|
||||
'shop:clothes' => array('label' => 'Clothes', 'frequency' => 315, 'icon' => 'shopping_clothes'),
|
||||
'shop:hairdresser' => array('label' => 'Hairdresser', 'frequency' => 312, 'icon' => 'shopping_hairdresser'),
|
||||
'shop:doityourself' => array('label' => 'Doityourself', 'frequency' => 247, 'icon' => 'shopping_diy'),
|
||||
'shop:estate_agent' => array('label' => 'Estate Agent', 'frequency' => 162, 'icon' => 'shopping_estateagent2'),
|
||||
'shop:car' => array('label' => 'Car', 'frequency' => 159, 'icon' => 'shopping_car'),
|
||||
'shop:garden_centre' => array('label' => 'Garden Centre', 'frequency' => 143, 'icon' => 'shopping_garden_centre'),
|
||||
'shop:car_repair' => array('label' => 'Car Repair', 'frequency' => 141, 'icon' => 'shopping_car_repair'),
|
||||
'shop:newsagent' => array('label' => 'Newsagent', 'frequency' => 132),
|
||||
'shop:bakery' => array('label' => 'Bakery', 'frequency' => 129, 'icon' => 'shopping_bakery'),
|
||||
'shop:furniture' => array('label' => 'Furniture', 'frequency' => 124),
|
||||
'shop:butcher' => array('label' => 'Butcher', 'frequency' => 105, 'icon' => 'shopping_butcher'),
|
||||
'shop:apparel' => array('label' => 'Apparel', 'frequency' => 98, 'icon' => 'shopping_clothes'),
|
||||
'shop:electronics' => array('label' => 'Electronics', 'frequency' => 96),
|
||||
'shop:department_store' => array('label' => 'Department Store', 'frequency' => 86),
|
||||
'shop:books' => array('label' => 'Books', 'frequency' => 85),
|
||||
'shop:yes' => array('label' => 'Shop', 'frequency' => 68),
|
||||
'shop:outdoor' => array('label' => 'Outdoor', 'frequency' => 67),
|
||||
'shop:mall' => array('label' => 'Mall', 'frequency' => 63),
|
||||
'shop:florist' => array('label' => 'Florist', 'frequency' => 61),
|
||||
'shop:charity' => array('label' => 'Charity', 'frequency' => 60),
|
||||
'shop:hardware' => array('label' => 'Hardware', 'frequency' => 59),
|
||||
'shop:laundry' => array('label' => 'Laundry', 'frequency' => 51, 'icon' => 'shopping_laundrette'),
|
||||
'shop:shoes' => array('label' => 'Shoes', 'frequency' => 49),
|
||||
'shop:beverages' => array('label' => 'Beverages', 'frequency' => 48, 'icon' => 'shopping_alcohol'),
|
||||
'shop:dry_cleaning' => array('label' => 'Dry Cleaning', 'frequency' => 46),
|
||||
'shop:carpet' => array('label' => 'Carpet', 'frequency' => 45),
|
||||
'shop:computer' => array('label' => 'Computer', 'frequency' => 44),
|
||||
'shop:alcohol' => array('label' => 'Alcohol', 'frequency' => 44, 'icon' => 'shopping_alcohol'),
|
||||
'shop:optician' => array('label' => 'Optician', 'frequency' => 55, 'icon' => 'health_opticians'),
|
||||
'shop:chemist' => array('label' => 'Chemist', 'frequency' => 42, 'icon' => 'health_pharmacy'),
|
||||
'shop:gallery' => array('label' => 'Gallery', 'frequency' => 38, 'icon' => 'tourist_art_gallery2'),
|
||||
'shop:mobile_phone' => array('label' => 'Mobile Phone', 'frequency' => 37),
|
||||
'shop:sports' => array('label' => 'Sports', 'frequency' => 37),
|
||||
'shop:jewelry' => array('label' => 'Jewelry', 'frequency' => 32, 'icon' => 'shopping_jewelry'),
|
||||
'shop:pet' => array('label' => 'Pet', 'frequency' => 29),
|
||||
'shop:beauty' => array('label' => 'Beauty', 'frequency' => 28),
|
||||
'shop:stationery' => array('label' => 'Stationery', 'frequency' => 25),
|
||||
'shop:shopping_centre' => array('label' => 'Shopping Centre', 'frequency' => 25),
|
||||
'shop:general' => array('label' => 'General', 'frequency' => 25),
|
||||
'shop:electrical' => array('label' => 'Electrical', 'frequency' => 25),
|
||||
'shop:toys' => array('label' => 'Toys', 'frequency' => 23),
|
||||
'shop:jeweller' => array('label' => 'Jeweller', 'frequency' => 23),
|
||||
'shop:betting' => array('label' => 'Betting', 'frequency' => 23),
|
||||
'shop:household' => array('label' => 'Household', 'frequency' => 21),
|
||||
'shop:travel_agency' => array('label' => 'Travel Agency', 'frequency' => 21),
|
||||
'shop:hifi' => array('label' => 'Hifi', 'frequency' => 21),
|
||||
'amenity:shop' => array('label' => 'Shop', 'frequency' => 61),
|
||||
'tourism:information' => array('label' => 'Information', 'frequency' => 224, 'icon' => 'amenity_information'),
|
||||
|
||||
'place:house' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
||||
'place:house_name' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
||||
'place:house_number' => array('label' => 'House Number', 'frequency' => 2086, 'defzoom' => 18),
|
||||
'place:country_code' => array('label' => 'Country Code', 'frequency' => 2086, 'defzoom' => 18),
|
||||
|
||||
//
|
||||
|
||||
'leisure:pitch' => array('label' => 'Pitch', 'frequency' => 762),
|
||||
'highway:unsurfaced' => array('label' => 'Unsurfaced', 'frequency' => 492),
|
||||
'historic:ruins' => array('label' => 'Ruins', 'frequency' => 483, 'icon' => 'tourist_ruin'),
|
||||
'amenity:college' => array('label' => 'College', 'frequency' => 473, 'icon' => 'education_school'),
|
||||
'historic:monument' => array('label' => 'Monument', 'frequency' => 470, 'icon' => 'tourist_monument'),
|
||||
'railway:subway' => array('label' => 'Subway', 'frequency' => 385),
|
||||
'historic:memorial' => array('label' => 'Memorial', 'frequency' => 382, 'icon' => 'tourist_monument'),
|
||||
'leisure:nature_reserve' => array('label' => 'Nature Reserve', 'frequency' => 342),
|
||||
'leisure:common' => array('label' => 'Common', 'frequency' => 322),
|
||||
'waterway:lock_gate' => array('label' => 'Lock Gate', 'frequency' => 321),
|
||||
'natural:fell' => array('label' => 'Fell', 'frequency' => 308),
|
||||
'amenity:nightclub' => array('label' => 'Nightclub', 'frequency' => 292),
|
||||
'highway:path' => array('label' => 'Path', 'frequency' => 287),
|
||||
'leisure:garden' => array('label' => 'Garden', 'frequency' => 285),
|
||||
'landuse:reservoir' => array('label' => 'Reservoir', 'frequency' => 276),
|
||||
'leisure:playground' => array('label' => 'Playground', 'frequency' => 264),
|
||||
'leisure:stadium' => array('label' => 'Stadium', 'frequency' => 212),
|
||||
'historic:mine' => array('label' => 'Mine', 'frequency' => 193, 'icon' => 'poi_mine'),
|
||||
'natural:cliff' => array('label' => 'Cliff', 'frequency' => 193),
|
||||
'tourism:caravan_site' => array('label' => 'Caravan Site', 'frequency' => 183, 'icon' => 'accommodation_caravan_park'),
|
||||
'amenity:bus_station' => array('label' => 'Bus Station', 'frequency' => 181, 'icon' => 'transport_bus_station'),
|
||||
'amenity:kindergarten' => array('label' => 'Kindergarten', 'frequency' => 179),
|
||||
'highway:construction' => array('label' => 'Construction', 'frequency' => 176),
|
||||
'amenity:atm' => array('label' => 'Atm', 'frequency' => 172, 'icon' => 'money_atm2'),
|
||||
'amenity:emergency_phone' => array('label' => 'Emergency Phone', 'frequency' => 164),
|
||||
'waterway:lock' => array('label' => 'Lock', 'frequency' => 146),
|
||||
'waterway:riverbank' => array('label' => 'Riverbank', 'frequency' => 143),
|
||||
'natural:coastline' => array('label' => 'Coastline', 'frequency' => 142),
|
||||
'tourism:viewpoint' => array('label' => 'Viewpoint', 'frequency' => 140, 'icon' => 'tourist_view_point'),
|
||||
'tourism:hostel' => array('label' => 'Hostel', 'frequency' => 140),
|
||||
'tourism:bed_and_breakfast' => array('label' => 'Bed And Breakfast', 'frequency' => 140, 'icon' => 'accommodation_bed_and_breakfast'),
|
||||
'railway:halt' => array('label' => 'Halt', 'frequency' => 135),
|
||||
'railway:platform' => array('label' => 'Platform', 'frequency' => 134),
|
||||
'railway:tram' => array('label' => 'Tram', 'frequency' => 130, 'icon' => 'transport_tram_stop'),
|
||||
'amenity:courthouse' => array('label' => 'Courthouse', 'frequency' => 129, 'icon' => 'amenity_court'),
|
||||
'amenity:recycling' => array('label' => 'Recycling', 'frequency' => 126, 'icon' => 'amenity_recycling'),
|
||||
'amenity:dentist' => array('label' => 'Dentist', 'frequency' => 124, 'icon' => 'health_dentist'),
|
||||
'natural:beach' => array('label' => 'Beach', 'frequency' => 121, 'icon' => 'tourist_beach'),
|
||||
'place:moor' => array('label' => 'Moor', 'frequency' => 118),
|
||||
'amenity:grave_yard' => array('label' => 'Grave Yard', 'frequency' => 110),
|
||||
'waterway:drain' => array('label' => 'Drain', 'frequency' => 108),
|
||||
'landuse:grass' => array('label' => 'Grass', 'frequency' => 106),
|
||||
'landuse:village_green' => array('label' => 'Village Green', 'frequency' => 106),
|
||||
'natural:bay' => array('label' => 'Bay', 'frequency' => 102),
|
||||
'railway:tram_stop' => array('label' => 'Tram Stop', 'frequency' => 101, 'icon' => 'transport_tram_stop'),
|
||||
'leisure:marina' => array('label' => 'Marina', 'frequency' => 98),
|
||||
'highway:stile' => array('label' => 'Stile', 'frequency' => 97),
|
||||
'natural:moor' => array('label' => 'Moor', 'frequency' => 95),
|
||||
'railway:light_rail' => array('label' => 'Light Rail', 'frequency' => 91),
|
||||
'railway:narrow_gauge' => array('label' => 'Narrow Gauge', 'frequency' => 90),
|
||||
'natural:land' => array('label' => 'Land', 'frequency' => 86),
|
||||
'amenity:village_hall' => array('label' => 'Village Hall', 'frequency' => 82),
|
||||
'waterway:dock' => array('label' => 'Dock', 'frequency' => 80),
|
||||
'amenity:veterinary' => array('label' => 'Veterinary', 'frequency' => 79),
|
||||
'landuse:brownfield' => array('label' => 'Brownfield', 'frequency' => 77),
|
||||
'leisure:track' => array('label' => 'Track', 'frequency' => 76),
|
||||
'railway:historic_station' => array('label' => 'Historic Station', 'frequency' => 74),
|
||||
'landuse:construction' => array('label' => 'Construction', 'frequency' => 72),
|
||||
'amenity:prison' => array('label' => 'Prison', 'frequency' => 71, 'icon' => 'amenity_prison'),
|
||||
'landuse:quarry' => array('label' => 'Quarry', 'frequency' => 71),
|
||||
'amenity:telephone' => array('label' => 'Telephone', 'frequency' => 70),
|
||||
'highway:traffic_signals' => array('label' => 'Traffic Signals', 'frequency' => 66),
|
||||
'natural:heath' => array('label' => 'Heath', 'frequency' => 62),
|
||||
'historic:house' => array('label' => 'House', 'frequency' => 61),
|
||||
'amenity:social_club' => array('label' => 'Social Club', 'frequency' => 61),
|
||||
'landuse:military' => array('label' => 'Military', 'frequency' => 61),
|
||||
'amenity:health_centre' => array('label' => 'Health Centre', 'frequency' => 59),
|
||||
'historic:building' => array('label' => 'Building', 'frequency' => 58),
|
||||
'amenity:clinic' => array('label' => 'Clinic', 'frequency' => 57),
|
||||
'highway:services' => array('label' => 'Services', 'frequency' => 56),
|
||||
'amenity:ferry_terminal' => array('label' => 'Ferry Terminal', 'frequency' => 55),
|
||||
'natural:marsh' => array('label' => 'Marsh', 'frequency' => 55),
|
||||
'natural:hill' => array('label' => 'Hill', 'frequency' => 54),
|
||||
'highway:raceway' => array('label' => 'Raceway', 'frequency' => 53),
|
||||
'amenity:taxi' => array('label' => 'Taxi', 'frequency' => 47),
|
||||
'amenity:take_away' => array('label' => 'Take Away', 'frequency' => 45),
|
||||
'amenity:car_rental' => array('label' => 'Car Rental', 'frequency' => 44),
|
||||
'place:islet' => array('label' => 'Islet', 'frequency' => 44),
|
||||
'amenity:nursery' => array('label' => 'Nursery', 'frequency' => 44),
|
||||
'amenity:nursing_home' => array('label' => 'Nursing Home', 'frequency' => 43),
|
||||
'amenity:toilets' => array('label' => 'Toilets', 'frequency' => 38),
|
||||
'amenity:hall' => array('label' => 'Hall', 'frequency' => 38),
|
||||
'waterway:boatyard' => array('label' => 'Boatyard', 'frequency' => 36),
|
||||
'highway:mini_roundabout' => array('label' => 'Mini Roundabout', 'frequency' => 35),
|
||||
'historic:manor' => array('label' => 'Manor', 'frequency' => 35),
|
||||
'tourism:chalet' => array('label' => 'Chalet', 'frequency' => 34),
|
||||
'amenity:bicycle_parking' => array('label' => 'Bicycle Parking', 'frequency' => 34),
|
||||
'amenity:hotel' => array('label' => 'Hotel', 'frequency' => 34),
|
||||
'waterway:weir' => array('label' => 'Weir', 'frequency' => 33),
|
||||
'natural:wetland' => array('label' => 'Wetland', 'frequency' => 33),
|
||||
'natural:cave_entrance' => array('label' => 'Cave Entrance', 'frequency' => 32),
|
||||
'amenity:crematorium' => array('label' => 'Crematorium', 'frequency' => 31),
|
||||
'tourism:picnic_site' => array('label' => 'Picnic Site', 'frequency' => 31),
|
||||
'landuse:wood' => array('label' => 'Wood', 'frequency' => 30),
|
||||
'landuse:basin' => array('label' => 'Basin', 'frequency' => 30),
|
||||
'natural:tree' => array('label' => 'Tree', 'frequency' => 30),
|
||||
'leisure:slipway' => array('label' => 'Slipway', 'frequency' => 29),
|
||||
'landuse:meadow' => array('label' => 'Meadow', 'frequency' => 29),
|
||||
'landuse:piste' => array('label' => 'Piste', 'frequency' => 28),
|
||||
'amenity:care_home' => array('label' => 'Care Home', 'frequency' => 28),
|
||||
'amenity:club' => array('label' => 'Club', 'frequency' => 28),
|
||||
'amenity:medical_centre' => array('label' => 'Medical Centre', 'frequency' => 27),
|
||||
'historic:roman_road' => array('label' => 'Roman Road', 'frequency' => 27),
|
||||
'historic:fort' => array('label' => 'Fort', 'frequency' => 26),
|
||||
'railway:subway_entrance' => array('label' => 'Subway Entrance', 'frequency' => 26),
|
||||
'historic:yes' => array('label' => 'Historic', 'frequency' => 25),
|
||||
'highway:gate' => array('label' => 'Gate', 'frequency' => 25),
|
||||
'leisure:fishing' => array('label' => 'Fishing', 'frequency' => 24),
|
||||
'historic:museum' => array('label' => 'Museum', 'frequency' => 24),
|
||||
'amenity:car_wash' => array('label' => 'Car Wash', 'frequency' => 24),
|
||||
'railway:level_crossing' => array('label' => 'Level Crossing', 'frequency' => 23),
|
||||
'leisure:bird_hide' => array('label' => 'Bird Hide', 'frequency' => 23),
|
||||
'natural:headland' => array('label' => 'Headland', 'frequency' => 21),
|
||||
'tourism:apartments' => array('label' => 'Apartments', 'frequency' => 21),
|
||||
'amenity:shopping' => array('label' => 'Shopping', 'frequency' => 21),
|
||||
'natural:scrub' => array('label' => 'Scrub', 'frequency' => 20),
|
||||
'natural:fen' => array('label' => 'Fen', 'frequency' => 20),
|
||||
'building:yes' => array('label' => 'Building', 'frequency' => 200),
|
||||
'mountain_pass:yes' => array('label' => 'Mountain Pass', 'frequency' => 200),
|
||||
|
||||
'amenity:parking' => array('label' => 'Parking', 'frequency' => 3157),
|
||||
'highway:bus_stop' => array('label' => 'Bus Stop', 'frequency' => 35777, 'icon' => 'transport_bus_stop2'),
|
||||
'place:postcode' => array('label' => 'Postcode', 'frequency' => 27267),
|
||||
'amenity:post_box' => array('label' => 'Post Box', 'frequency' => 9613),
|
||||
|
||||
'place:houses' => array('label' => 'Houses', 'frequency' => 85),
|
||||
'railway:preserved' => array('label' => 'Preserved', 'frequency' => 227),
|
||||
'waterway:derelict_canal' => array('label' => 'Derelict Canal', 'frequency' => 21),
|
||||
'amenity:dead_pub' => array('label' => 'Dead Pub', 'frequency' => 20),
|
||||
'railway:disused_station' => array('label' => 'Disused Station', 'frequency' => 114),
|
||||
'railway:abandoned' => array('label' => 'Abandoned', 'frequency' => 641),
|
||||
'railway:disused' => array('label' => 'Disused', 'frequency' => 72),
|
||||
);
|
||||
}
|
||||
298
lib/DB.php
Normal file
298
lib/DB.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/DatabaseError.php');
|
||||
|
||||
/**
|
||||
* Uses PDO to access the database specified in the CONST_Database_DSN
|
||||
* setting.
|
||||
*/
|
||||
class DB
|
||||
{
|
||||
protected $connection;
|
||||
|
||||
public function __construct($sDSN = CONST_Database_DSN)
|
||||
{
|
||||
$this->sDSN = $sDSN;
|
||||
}
|
||||
|
||||
public function connect($bNew = false, $bPersistent = true)
|
||||
{
|
||||
if (isset($this->connection) && !$bNew) {
|
||||
return true;
|
||||
}
|
||||
$aConnOptions = array(
|
||||
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
||||
\PDO::ATTR_PERSISTENT => $bPersistent
|
||||
);
|
||||
|
||||
// https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php
|
||||
try {
|
||||
$conn = new \PDO($this->sDSN, null, null, $aConnOptions);
|
||||
} catch (\PDOException $e) {
|
||||
$sMsg = 'Failed to establish database connection:' . $e->getMessage();
|
||||
throw new \Nominatim\DatabaseError($sMsg, 500, null, $e->getMessage());
|
||||
}
|
||||
|
||||
$conn->exec("SET DateStyle TO 'sql,european'");
|
||||
$conn->exec("SET client_encoding TO 'utf-8'");
|
||||
$iMaxExecution = ini_get('max_execution_time');
|
||||
if ($iMaxExecution > 0) $conn->setAttribute(\PDO::ATTR_TIMEOUT, $iMaxExecution); // seconds
|
||||
|
||||
$this->connection = $conn;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the number of rows that were modified or deleted by the SQL
|
||||
// statement. If no rows were affected returns 0.
|
||||
public function exec($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
$val = null;
|
||||
try {
|
||||
if (isset($aInputVars)) {
|
||||
$stmt = $this->connection->prepare($sSQL);
|
||||
$stmt->execute($aInputVars);
|
||||
} else {
|
||||
$val = $this->connection->exec($sSQL);
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns first row as array.
|
||||
* Returns false if no result found.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getRow($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
$row = $stmt->fetch();
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns first value of first result.
|
||||
* Returns false if no results found.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getOne($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
$row = $stmt->fetch(\PDO::FETCH_NUM);
|
||||
if ($row === false) return false;
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $row[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns array of results (arrays).
|
||||
* Returns empty array if no results found.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getAll($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
$rows = $stmt->fetchAll();
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns array of the first value of each result.
|
||||
* Returns empty array if no results found.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getCol($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
$aVals = array();
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
|
||||
while ($val = $stmt->fetchColumn(0)) { // returns first column or false
|
||||
$aVals[] = $val;
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $aVals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns associate array mapping first value to second value of each result.
|
||||
* Returns empty array if no results found.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getAssoc($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
|
||||
$aList = array();
|
||||
while ($aRow = $stmt->fetch(\PDO::FETCH_NUM)) {
|
||||
$aList[$aRow[0]] = $aRow[1];
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $aList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Returns a PDO statement to iterate over.
|
||||
*
|
||||
* @param string $sSQL
|
||||
*
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function getQueryStatement($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
||||
{
|
||||
try {
|
||||
if (isset($aInputVars)) {
|
||||
$stmt = $this->connection->prepare($sSQL);
|
||||
$stmt->execute($aInputVars);
|
||||
} else {
|
||||
$stmt = $this->connection->query($sSQL);
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
||||
}
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* St. John's Way => 'St. John\'s Way'
|
||||
*
|
||||
* @param string $sVal Text to be quoted.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDBQuoted($sVal)
|
||||
{
|
||||
return $this->connection->quote($sVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like getDBQuoted, but takes an array.
|
||||
*
|
||||
* @param array $aVals List of text to be quoted.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getDBQuotedList($aVals)
|
||||
{
|
||||
return array_map(function ($sVal) {
|
||||
return $this->getDBQuoted($sVal);
|
||||
}, $aVals);
|
||||
}
|
||||
|
||||
/**
|
||||
* [1,2,'b'] => 'ARRAY[1,2,'b']''
|
||||
*
|
||||
* @param array $aVals List of text to be quoted.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArraySQL($a)
|
||||
{
|
||||
return 'ARRAY['.join(',', $a).']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table exists in the database. Returns true if it does.
|
||||
*
|
||||
* @param string $sTableName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function tableExists($sTableName)
|
||||
{
|
||||
$sSQL = 'SELECT count(*) FROM pg_tables WHERE tablename = :tablename';
|
||||
return ($this->getOne($sSQL, array(':tablename' => $sTableName)) == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the DSN includes the database name, checks if the connection works.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function databaseExists()
|
||||
{
|
||||
$bExists = true;
|
||||
try {
|
||||
$this->connect(true);
|
||||
} catch (\Nominatim\DatabaseError $e) {
|
||||
$bExists = false;
|
||||
}
|
||||
return $bExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* e.g. 9.6, 10, 11.2
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPostgresVersion()
|
||||
{
|
||||
$sVersionString = $this->getOne('SHOW server_version_num');
|
||||
preg_match('#([0-9]?[0-9])([0-9][0-9])[0-9][0-9]#', $sVersionString, $aMatches);
|
||||
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* e.g. 2, 2.2
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPostgisVersion()
|
||||
{
|
||||
$sVersionString = $this->getOne('select postgis_lib_version()');
|
||||
preg_match('#^([0-9]+)[.]([0-9]+)[.]#', $sVersionString, $aMatches);
|
||||
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
||||
}
|
||||
|
||||
public static function parseDSN($sDSN)
|
||||
{
|
||||
// https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php
|
||||
$aInfo = array();
|
||||
if (preg_match('/^pgsql:(.+)/', $sDSN, $aMatches)) {
|
||||
foreach (explode(';', $aMatches[1]) as $sKeyVal) {
|
||||
list($sKey, $sVal) = explode('=', $sKeyVal, 2);
|
||||
if ($sKey == 'host') $sKey = 'hostspec';
|
||||
if ($sKey == 'dbname') $sKey = 'database';
|
||||
if ($sKey == 'user') $sKey = 'username';
|
||||
$aInfo[$sKey] = $sVal;
|
||||
}
|
||||
}
|
||||
return $aInfo;
|
||||
}
|
||||
}
|
||||
34
lib/DatabaseError.php
Normal file
34
lib/DatabaseError.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
class DatabaseError extends \Exception
|
||||
{
|
||||
|
||||
public function __construct($message, $code = 500, Exception $previous = null, $oPDOErr, $sSql = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
// https://secure.php.net/manual/en/class.pdoexception.php
|
||||
$this->oPDOErr = $oPDOErr;
|
||||
$this->sSql = $sSql;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
|
||||
}
|
||||
|
||||
public function getSqlError()
|
||||
{
|
||||
return $this->oPDOErr->getMessage();
|
||||
}
|
||||
|
||||
public function getSqlDebugDump()
|
||||
{
|
||||
if (CONST_Debug) {
|
||||
return var_export($this->oPDOErr, true);
|
||||
} else {
|
||||
return $this->sSql;
|
||||
}
|
||||
}
|
||||
}
|
||||
180
lib/DebugHtml.php
Normal file
180
lib/DebugHtml.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
class Debug
|
||||
{
|
||||
public static function newFunction($sHeading)
|
||||
{
|
||||
echo "<pre><h2>Debug output for $sHeading</h2></pre>\n";
|
||||
}
|
||||
|
||||
public static function newSection($sHeading)
|
||||
{
|
||||
echo "<hr><pre><h3>$sHeading</h3></pre>\n";
|
||||
}
|
||||
|
||||
public static function printVar($sHeading, $mVar)
|
||||
{
|
||||
echo '<pre><b>'.$sHeading. ':</b> ';
|
||||
Debug::outputVar($mVar, str_repeat(' ', strlen($sHeading) + 3));
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
public static function fmtArrayVals($aArr)
|
||||
{
|
||||
return array('__debug_format' => 'array_vals', 'data' => $aArr);
|
||||
}
|
||||
|
||||
public static function printDebugArray($sHeading, $oVar)
|
||||
{
|
||||
|
||||
if ($oVar === null) {
|
||||
Debug::printVar($sHeading, 'null');
|
||||
} else {
|
||||
Debug::printVar($sHeading, $oVar->debugInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public static function printDebugTable($sHeading, $aVar)
|
||||
{
|
||||
echo '<b>'.$sHeading.":</b>\n";
|
||||
echo "<table border='1'>\n";
|
||||
if (!empty($aVar)) {
|
||||
echo " <tr>\n";
|
||||
$aKeys = array();
|
||||
$aInfo = reset($aVar);
|
||||
if (!is_array($aInfo)) {
|
||||
$aInfo = $aInfo->debugInfo();
|
||||
}
|
||||
foreach ($aInfo as $sKey => $mVal) {
|
||||
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
||||
$aKeys[] = $sKey;
|
||||
}
|
||||
echo " </tr>\n";
|
||||
foreach ($aVar as $oRow) {
|
||||
$aInfo = $oRow;
|
||||
if (!is_array($oRow)) {
|
||||
$aInfo = $oRow->debugInfo();
|
||||
}
|
||||
echo " <tr>\n";
|
||||
foreach ($aKeys as $sKey) {
|
||||
echo ' <td><pre>';
|
||||
if (isset($aInfo[$sKey])) {
|
||||
Debug::outputVar($aInfo[$sKey], '');
|
||||
}
|
||||
echo '</pre></td>'."\n";
|
||||
}
|
||||
echo " </tr>\n";
|
||||
}
|
||||
}
|
||||
echo "</table>\n";
|
||||
}
|
||||
|
||||
public static function printGroupedSearch($aSearches, $aWordsIDs)
|
||||
{
|
||||
echo '<table border="1">';
|
||||
echo '<tr><th>rank</th><th>Name Tokens</th><th>Name Not</th>';
|
||||
echo '<th>Address Tokens</th><th>Address Not</th>';
|
||||
echo '<th>country</th><th>operator</th>';
|
||||
echo '<th>class</th><th>type</th><th>postcode</th><th>housenumber</th></tr>';
|
||||
foreach ($aSearches as $iRank => $aRankedSet) {
|
||||
foreach ($aRankedSet as $aRow) {
|
||||
$aRow->dumpAsHtmlTableRow($aWordsIDs);
|
||||
}
|
||||
}
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
public static function printGroupTable($sHeading, $aVar)
|
||||
{
|
||||
echo '<b>'.$sHeading.":</b>\n";
|
||||
echo "<table border='1'>\n";
|
||||
if (!empty($aVar)) {
|
||||
echo " <tr>\n";
|
||||
echo ' <th><small>Group</small></th>'."\n";
|
||||
$aKeys = array();
|
||||
$aInfo = reset($aVar)[0];
|
||||
if (!is_array($aInfo)) {
|
||||
$aInfo = $aInfo->debugInfo();
|
||||
}
|
||||
foreach ($aInfo as $sKey => $mVal) {
|
||||
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
||||
$aKeys[] = $sKey;
|
||||
}
|
||||
echo " </tr>\n";
|
||||
foreach ($aVar as $sGrpKey => $aGroup) {
|
||||
foreach ($aGroup as $oRow) {
|
||||
$aInfo = $oRow;
|
||||
if (!is_array($oRow)) {
|
||||
$aInfo = $oRow->debugInfo();
|
||||
}
|
||||
echo " <tr>\n";
|
||||
echo ' <td><pre>'.$sGrpKey.'</pre></td>'."\n";
|
||||
foreach ($aKeys as $sKey) {
|
||||
echo ' <td><pre>';
|
||||
if (!empty($aInfo[$sKey])) {
|
||||
Debug::outputVar($aInfo[$sKey], '');
|
||||
}
|
||||
echo '</pre></td>'."\n";
|
||||
}
|
||||
echo " </tr>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
echo "</table>\n";
|
||||
}
|
||||
|
||||
public static function printSQL($sSQL)
|
||||
{
|
||||
echo '<p><tt><font color="#aaa">'.$sSQL.'</font></tt></p>'."\n";
|
||||
}
|
||||
|
||||
private static function outputVar($mVar, $sPreNL)
|
||||
{
|
||||
if (is_array($mVar) && !isset($mVar['__debug_format'])) {
|
||||
$sPre = '';
|
||||
foreach ($mVar as $mKey => $aValue) {
|
||||
echo $sPre;
|
||||
$iKeyLen = Debug::outputSimpleVar($mKey);
|
||||
echo ' => ';
|
||||
Debug::outputVar(
|
||||
$aValue,
|
||||
$sPreNL.str_repeat(' ', $iKeyLen + 4)
|
||||
);
|
||||
$sPre = "\n".$sPreNL;
|
||||
}
|
||||
} elseif (is_array($mVar) && isset($mVar['__debug_format'])) {
|
||||
if (!empty($mVar['data'])) {
|
||||
$sPre = '';
|
||||
foreach ($mVar['data'] as $mValue) {
|
||||
echo $sPre;
|
||||
Debug::outputSimpleVar($mValue);
|
||||
$sPre = ', ';
|
||||
}
|
||||
}
|
||||
} elseif (is_object($mVar) && method_exists($mVar, 'debugInfo')) {
|
||||
Debug::outputVar($mVar->debugInfo(), $sPreNL);
|
||||
} elseif (is_a($mVar, 'stdClass')) {
|
||||
Debug::outputVar(json_decode(json_encode($mVar), true), $sPreNL);
|
||||
} else {
|
||||
Debug::outputSimpleVar($mVar);
|
||||
}
|
||||
}
|
||||
|
||||
private static function outputSimpleVar($mVar)
|
||||
{
|
||||
if (is_bool($mVar)) {
|
||||
echo '<i>'.($mVar ? 'True' : 'False').'</i>';
|
||||
return $mVar ? 4 : 5;
|
||||
}
|
||||
|
||||
if (is_string($mVar)) {
|
||||
echo "'$mVar'";
|
||||
return strlen($mVar) + 2;
|
||||
}
|
||||
|
||||
echo (string)$mVar;
|
||||
return strlen((string)$mVar);
|
||||
}
|
||||
}
|
||||
11
lib/DebugNone.php
Normal file
11
lib/DebugNone.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
class Debug
|
||||
{
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
2899
lib/Geocode.php
2899
lib/Geocode.php
File diff suppressed because it is too large
Load Diff
121
lib/ParameterParser.php
Normal file
121
lib/ParameterParser.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
class ParameterParser
|
||||
{
|
||||
private $aParams;
|
||||
|
||||
|
||||
public function __construct($aParams = null)
|
||||
{
|
||||
$this->aParams = ($aParams === null) ? $_GET : $aParams;
|
||||
}
|
||||
|
||||
public function getBool($sName, $bDefault = false)
|
||||
{
|
||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
||||
return $bDefault;
|
||||
}
|
||||
|
||||
return (bool) $this->aParams[$sName];
|
||||
}
|
||||
|
||||
public function getInt($sName, $bDefault = false)
|
||||
{
|
||||
if (!isset($this->aParams[$sName])) {
|
||||
return $bDefault;
|
||||
}
|
||||
|
||||
if (!preg_match('/^[+-]?[0-9]+$/', $this->aParams[$sName])) {
|
||||
userError("Integer number expected for parameter '$sName'");
|
||||
}
|
||||
|
||||
return (int) $this->aParams[$sName];
|
||||
}
|
||||
|
||||
public function getFloat($sName, $bDefault = false)
|
||||
{
|
||||
if (!isset($this->aParams[$sName])) {
|
||||
return $bDefault;
|
||||
}
|
||||
|
||||
if (!preg_match('/^[+-]?[0-9]*\.?[0-9]+$/', $this->aParams[$sName])) {
|
||||
userError("Floating-point number expected for parameter '$sName'");
|
||||
}
|
||||
|
||||
return (float) $this->aParams[$sName];
|
||||
}
|
||||
|
||||
public function getString($sName, $bDefault = false)
|
||||
{
|
||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
||||
return $bDefault;
|
||||
}
|
||||
|
||||
return $this->aParams[$sName];
|
||||
}
|
||||
|
||||
public function getSet($sName, $aValues, $sDefault = false)
|
||||
{
|
||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
||||
return $sDefault;
|
||||
}
|
||||
|
||||
if (!in_array($this->aParams[$sName], $aValues)) {
|
||||
userError("Parameter '$sName' must be one of: ".join(', ', $aValues));
|
||||
}
|
||||
|
||||
return $this->aParams[$sName];
|
||||
}
|
||||
|
||||
public function getStringList($sName, $aDefault = false)
|
||||
{
|
||||
$sValue = $this->getString($sName);
|
||||
|
||||
if ($sValue) {
|
||||
// removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values
|
||||
return array_values(array_filter(explode(',', $sValue), 'strlen'));
|
||||
}
|
||||
|
||||
return $aDefault;
|
||||
}
|
||||
|
||||
public function getPreferredLanguages($sFallback = null)
|
||||
{
|
||||
if ($sFallback === null && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$sFallback = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
||||
}
|
||||
|
||||
$aLanguages = array();
|
||||
$sLangString = $this->getString('accept-language', $sFallback);
|
||||
|
||||
if ($sLangString) {
|
||||
if (preg_match_all('/(([a-z]{1,8})([-_][a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER)) {
|
||||
foreach ($aLanguagesParse as $iLang => $aLanguage) {
|
||||
$aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100);
|
||||
if (!isset($aLanguages[$aLanguage[2]])) $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10;
|
||||
}
|
||||
arsort($aLanguages);
|
||||
}
|
||||
}
|
||||
if (empty($aLanguages) && CONST_Default_Language) {
|
||||
$aLanguages[CONST_Default_Language] = 1;
|
||||
}
|
||||
|
||||
foreach ($aLanguages as $sLanguage => $fLanguagePref) {
|
||||
$aLangPrefOrder['short_name:'.$sLanguage] = 'short_name:'.$sLanguage;
|
||||
$aLangPrefOrder['name:'.$sLanguage] = 'name:'.$sLanguage;
|
||||
}
|
||||
$aLangPrefOrder['short_name'] = 'short_name';
|
||||
$aLangPrefOrder['name'] = 'name';
|
||||
$aLangPrefOrder['brand'] = 'brand';
|
||||
foreach ($aLanguages as $sLanguage => $fLanguagePref) {
|
||||
$aLangPrefOrder['official_name:'.$sLanguage] = 'official_name:'.$sLanguage;
|
||||
}
|
||||
$aLangPrefOrder['official_name'] = 'official_name';
|
||||
$aLangPrefOrder['ref'] = 'ref';
|
||||
$aLangPrefOrder['type'] = 'type';
|
||||
return $aLangPrefOrder;
|
||||
}
|
||||
}
|
||||
160
lib/Phrase.php
Normal file
160
lib/Phrase.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
/**
|
||||
* Segment of a query string.
|
||||
*
|
||||
* The parts of a query strings are usually separated by commas.
|
||||
*/
|
||||
class Phrase
|
||||
{
|
||||
const MAX_WORDSET_LEN = 20;
|
||||
const MAX_WORDSETS = 100;
|
||||
|
||||
// Complete phrase as a string.
|
||||
private $sPhrase;
|
||||
// Element type for structured searches.
|
||||
private $sPhraseType;
|
||||
// Space-separated words of the phrase.
|
||||
private $aWords;
|
||||
// Possible segmentations of the phrase.
|
||||
private $aWordSets;
|
||||
|
||||
public static function cmpByArraylen($aA, $aB)
|
||||
{
|
||||
$iALen = count($aA);
|
||||
$iBLen = count($aB);
|
||||
|
||||
if ($iALen == $iBLen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($iALen < $iBLen) ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
public function __construct($sPhrase, $sPhraseType)
|
||||
{
|
||||
$this->sPhrase = trim($sPhrase);
|
||||
$this->sPhraseType = $sPhraseType;
|
||||
$this->aWords = explode(' ', $this->sPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element type of the phrase.
|
||||
*
|
||||
* @return string Pharse type if the phrase comes from a structured query
|
||||
* or empty string otherwise.
|
||||
*/
|
||||
public function getPhraseType()
|
||||
{
|
||||
return $this->sPhraseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of possible segmentations of the phrase.
|
||||
*
|
||||
* @return string[][] Array of segmentations, each consisting of an
|
||||
* array of terms.
|
||||
*/
|
||||
public function getWordSets()
|
||||
{
|
||||
return $this->aWordSets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the tokens from this phrase to the given list of tokens.
|
||||
*
|
||||
* @param string[] $aTokens List of tokens to append.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTokens(&$aTokens)
|
||||
{
|
||||
$iNumWords = count($this->aWords);
|
||||
|
||||
for ($i = 0; $i < $iNumWords; $i++) {
|
||||
$sPhrase = $this->aWords[$i];
|
||||
$aTokens[' '.$sPhrase] = ' '.$sPhrase;
|
||||
$aTokens[$sPhrase] = $sPhrase;
|
||||
|
||||
for ($j = $i + 1; $j < $iNumWords; $j++) {
|
||||
$sPhrase .= ' '.$this->aWords[$j];
|
||||
$aTokens[' '.$sPhrase] = ' '.$sPhrase;
|
||||
$aTokens[$sPhrase] = $sPhrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert the set of possible segmentations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function invertWordSets()
|
||||
{
|
||||
foreach ($this->aWordSets as $i => $aSet) {
|
||||
$this->aWordSets[$i] = array_reverse($aSet);
|
||||
}
|
||||
}
|
||||
|
||||
public function computeWordSets($oTokens)
|
||||
{
|
||||
$iNumWords = count($this->aWords);
|
||||
// Caches the word set for the partial phrase up to word i.
|
||||
$aSetCache = array_fill(0, $iNumWords, array());
|
||||
|
||||
// Initialise first element of cache. There can only be the word.
|
||||
if ($oTokens->containsAny($this->aWords[0])) {
|
||||
$aSetCache[0][] = array($this->aWords[0]);
|
||||
}
|
||||
|
||||
// Now do the next elements using what we already have.
|
||||
for ($i = 1; $i < $iNumWords; $i++) {
|
||||
for ($j = $i; $j > 0; $j--) {
|
||||
$sPartial = $j == $i ? $this->aWords[$j] : $this->aWords[$j].' '.$sPartial;
|
||||
if (!empty($aSetCache[$j - 1]) && $oTokens->containsAny($sPartial)) {
|
||||
$aPartial = array($sPartial);
|
||||
foreach ($aSetCache[$j - 1] as $aSet) {
|
||||
if (count($aSet) < Phrase::MAX_WORDSET_LEN) {
|
||||
$aSetCache[$i][] = array_merge($aSet, $aPartial);
|
||||
}
|
||||
}
|
||||
if (count($aSetCache[$i]) > 2 * Phrase::MAX_WORDSETS) {
|
||||
usort(
|
||||
$aSetCache[$i],
|
||||
array('\Nominatim\Phrase', 'cmpByArraylen')
|
||||
);
|
||||
$aSetCache[$i] = array_slice(
|
||||
$aSetCache[$i],
|
||||
0,
|
||||
Phrase::MAX_WORDSETS
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally the current full phrase
|
||||
$sPartial = $this->aWords[0].' '.$sPartial;
|
||||
if ($oTokens->containsAny($sPartial)) {
|
||||
$aSetCache[$i][] = array($sPartial);
|
||||
}
|
||||
}
|
||||
|
||||
$this->aWordSets = $aSetCache[$iNumWords - 1];
|
||||
usort($this->aWordSets, array('\Nominatim\Phrase', 'cmpByArraylen'));
|
||||
$this->aWordSets = array_slice($this->aWordSets, 0, Phrase::MAX_WORDSETS);
|
||||
}
|
||||
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'Type' => $this->sPhraseType,
|
||||
'Phrase' => $this->sPhrase,
|
||||
'Words' => $this->aWords,
|
||||
'WordSets' => $this->aWordSets
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,223 +1,575 @@
|
||||
<?php
|
||||
class PlaceLookup
|
||||
{
|
||||
protected $oDB;
|
||||
|
||||
protected $iPlaceID;
|
||||
namespace Nominatim;
|
||||
|
||||
protected $sType = false;
|
||||
require_once(CONST_BasePath.'/lib/AddressDetails.php');
|
||||
require_once(CONST_BasePath.'/lib/Result.php');
|
||||
|
||||
protected $aLangPrefOrder = array();
|
||||
class PlaceLookup
|
||||
{
|
||||
protected $oDB;
|
||||
|
||||
protected $bAddressDetails = false;
|
||||
protected $aLangPrefOrderSql = "''";
|
||||
|
||||
protected $bExtraTags = false;
|
||||
protected $bAddressDetails = false;
|
||||
protected $bExtraTags = false;
|
||||
protected $bNameDetails = false;
|
||||
|
||||
protected $bNameDetails = false;
|
||||
protected $bIncludePolygonAsPoints = false;
|
||||
protected $bIncludePolygonAsText = false;
|
||||
protected $bIncludePolygonAsGeoJSON = false;
|
||||
protected $bIncludePolygonAsKML = false;
|
||||
protected $bIncludePolygonAsSVG = false;
|
||||
protected $fPolygonSimplificationThreshold = 0.0;
|
||||
|
||||
function PlaceLookup(&$oDB)
|
||||
{
|
||||
$this->oDB =& $oDB;
|
||||
}
|
||||
|
||||
function setLanguagePreference($aLangPrefOrder)
|
||||
{
|
||||
$this->aLangPrefOrder = $aLangPrefOrder;
|
||||
}
|
||||
|
||||
function setIncludeAddressDetails($bAddressDetails = true)
|
||||
{
|
||||
$this->bAddressDetails = $bAddressDetails;
|
||||
}
|
||||
|
||||
function setIncludeExtraTags($bExtraTags = false)
|
||||
{
|
||||
if ((float) CONST_Postgresql_Version > 9.2)
|
||||
{
|
||||
$this->bExtraTags = $bExtraTags;
|
||||
}
|
||||
}
|
||||
|
||||
function setIncludeNameDetails($bNameDetails = false)
|
||||
{
|
||||
if ((float) CONST_Postgresql_Version > 9.2)
|
||||
{
|
||||
$this->bNameDetails = $bNameDetails;
|
||||
}
|
||||
}
|
||||
|
||||
function setPlaceID($iPlaceID)
|
||||
{
|
||||
$this->iPlaceID = $iPlaceID;
|
||||
}
|
||||
|
||||
function setOSMID($sType, $iID)
|
||||
{
|
||||
$sSQL = "select place_id from placex where osm_type = '".pg_escape_string($sType)."' and osm_id = ".(int)$iID." order by type = 'postcode' asc";
|
||||
$this->iPlaceID = $this->oDB->getOne($sSQL);
|
||||
}
|
||||
|
||||
function lookupPlace($details)
|
||||
{
|
||||
if (isset($details['place_id'])) $this->iPlaceID = $details['place_id'];
|
||||
if (isset($details['type'])) $this->sType = $details['type'];
|
||||
if (isset($details['osm_type']) && isset($details['osm_id']))
|
||||
{
|
||||
$this->setOSMID($details['osm_type'], $details['osm_id']);
|
||||
}
|
||||
|
||||
return $this->lookup();
|
||||
}
|
||||
|
||||
function lookup()
|
||||
{
|
||||
if (!$this->iPlaceID) return null;
|
||||
|
||||
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
|
||||
|
||||
if ($this->sType == 'tiger')
|
||||
{
|
||||
$sSQL = "select place_id,partition, 'T' as osm_type, place_id as osm_id, 'place' as class, 'house' as type, null as admin_level, housenumber, null as street, null as isin, postcode,";
|
||||
$sSQL .= " 'us' as country_code, parent_place_id, null as linked_place_id, 30 as rank_address, 30 as rank_search,";
|
||||
$sSQL .= " coalesce(null,0.75-(30::float/40)) as importance, null as indexed_status, null as indexed_date, null as wikipedia, 'us' as calculated_country_code, ";
|
||||
$sSQL .= " get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
|
||||
$sSQL .= " null as placename,";
|
||||
$sSQL .= " null as ref,";
|
||||
if ($this->bExtraTags) $sSQL .= " null as extra,";
|
||||
if ($this->bNameDetails) $sSQL .= " null as names,";
|
||||
$sSQL .= " st_y(centroid) as lat,";
|
||||
$sSQL .= " st_x(centroid) as lon";
|
||||
$sSQL .= " from location_property_tiger where place_id = ".(int)$this->iPlaceID;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = "select placex.place_id, partition, osm_type, osm_id, class, type, admin_level, housenumber, street, isin, postcode, country_code, parent_place_id, linked_place_id, rank_address, rank_search, ";
|
||||
$sSQL .= " coalesce(importance,0.75-(rank_search::float/40)) as importance, indexed_status, indexed_date, wikipedia, calculated_country_code, ";
|
||||
$sSQL .= " get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
|
||||
$sSQL .= " get_name_by_language(name, $sLanguagePrefArraySQL) as placename,";
|
||||
$sSQL .= " get_name_by_language(name, ARRAY['ref']) as ref,";
|
||||
if ($this->bExtraTags) $sSQL .= " hstore_to_json(extratags) as extra,";
|
||||
if ($this->bNameDetails) $sSQL .= " hstore_to_json(name) as names,";
|
||||
$sSQL .= " (case when centroid is null then st_y(st_centroid(geometry)) else st_y(centroid) end) as lat,";
|
||||
$sSQL .= " (case when centroid is null then st_x(st_centroid(geometry)) else st_x(centroid) end) as lon";
|
||||
$sSQL .= " from placex where place_id = ".(int)$this->iPlaceID;
|
||||
}
|
||||
|
||||
$aPlace = $this->oDB->getRow($sSQL);
|
||||
protected $sAnchorSql = null;
|
||||
protected $sAddressRankListSql = null;
|
||||
protected $sAllowedTypesSQLList = null;
|
||||
protected $bDeDupe = true;
|
||||
|
||||
|
||||
if (PEAR::IsError($aPlace))
|
||||
{
|
||||
failInternalError("Could not lookup place.", $sSQL, $aPlace);
|
||||
}
|
||||
public function __construct(&$oDB)
|
||||
{
|
||||
$this->oDB =& $oDB;
|
||||
}
|
||||
|
||||
if (!$aPlace['place_id']) return null;
|
||||
public function doDeDupe()
|
||||
{
|
||||
return $this->bDeDupe;
|
||||
}
|
||||
|
||||
if ($this->bAddressDetails)
|
||||
{
|
||||
$aAddress = $this->getAddressNames();
|
||||
$aPlace['aAddress'] = $aAddress;
|
||||
}
|
||||
public function setIncludePolygonAsPoints($b = true)
|
||||
{
|
||||
$this->bIncludePolygonAsPoints = $b;
|
||||
}
|
||||
|
||||
if ($this->bExtraTags)
|
||||
{
|
||||
if ($aPlace['extra'])
|
||||
{
|
||||
$aPlace['sExtraTags'] = json_decode($aPlace['extra']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPlace['sExtraTags'] = (object) array();
|
||||
}
|
||||
}
|
||||
public function setIncludeAddressDetails($b)
|
||||
{
|
||||
$this->bAddressDetails = $b;
|
||||
}
|
||||
|
||||
if ($this->bNameDetails)
|
||||
{
|
||||
if ($aPlace['names'])
|
||||
{
|
||||
$aPlace['sNameDetails'] = json_decode($aPlace['names']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPlace['sNameDetails'] = (object) array();
|
||||
}
|
||||
}
|
||||
public function loadParamArray($oParams, $sGeomType = null)
|
||||
{
|
||||
$aLangs = $oParams->getPreferredLanguages();
|
||||
$this->aLangPrefOrderSql =
|
||||
'ARRAY['.join(',', $this->oDB->getDBQuotedList($aLangs)).']';
|
||||
|
||||
$aClassType = getClassTypes();
|
||||
$sAddressType = '';
|
||||
$sClassType = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
|
||||
{
|
||||
$sAddressType = $aClassType[$aClassType]['simplelabel'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClassType = $aPlace['class'].':'.$aPlace['type'];
|
||||
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
|
||||
$sAddressType = $aClassType[$sClassType]['simplelabel'];
|
||||
else $sAddressType = $aPlace['class'];
|
||||
}
|
||||
$this->bExtraTags = $oParams->getBool('extratags', false);
|
||||
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
||||
|
||||
$aPlace['addresstype'] = $sAddressType;
|
||||
$this->bDeDupe = $oParams->getBool('dedupe', $this->bDeDupe);
|
||||
|
||||
return $aPlace;
|
||||
}
|
||||
if ($sGeomType === null || $sGeomType == 'geojson') {
|
||||
$this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson');
|
||||
$this->bIncludePolygonAsPoints = false;
|
||||
}
|
||||
|
||||
function getAddressDetails($bAll = false)
|
||||
{
|
||||
if (!$this->iPlaceID) return null;
|
||||
if ($oParams->getString('format', '') !== 'geojson') {
|
||||
if ($sGeomType === null || $sGeomType == 'text') {
|
||||
$this->bIncludePolygonAsText = $oParams->getBool('polygon_text');
|
||||
}
|
||||
if ($sGeomType === null || $sGeomType == 'kml') {
|
||||
$this->bIncludePolygonAsKML = $oParams->getBool('polygon_kml');
|
||||
}
|
||||
if ($sGeomType === null || $sGeomType == 'svg') {
|
||||
$this->bIncludePolygonAsSVG = $oParams->getBool('polygon_svg');
|
||||
}
|
||||
}
|
||||
$this->fPolygonSimplificationThreshold
|
||||
= $oParams->getFloat('polygon_threshold', 0.0);
|
||||
|
||||
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
|
||||
$iWantedTypes =
|
||||
($this->bIncludePolygonAsText ? 1 : 0) +
|
||||
($this->bIncludePolygonAsGeoJSON ? 1 : 0) +
|
||||
($this->bIncludePolygonAsKML ? 1 : 0) +
|
||||
($this->bIncludePolygonAsSVG ? 1 : 0);
|
||||
if ($iWantedTypes > CONST_PolygonOutput_MaximumTypes) {
|
||||
if (CONST_PolygonOutput_MaximumTypes) {
|
||||
userError('Select only '.CONST_PolygonOutput_MaximumTypes.' polgyon output option');
|
||||
} else {
|
||||
userError('Polygon output is disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sSQL = "select *,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata(".$this->iPlaceID.")";
|
||||
if (!$bAll) $sSQL .= " WHERE isaddress OR type = 'country_code'";
|
||||
$sSQL .= " order by rank_address desc,isaddress desc";
|
||||
public function getMoreUrlParams()
|
||||
{
|
||||
$aParams = array();
|
||||
|
||||
$aAddressLines = $this->oDB->getAll($sSQL);
|
||||
if (PEAR::IsError($aAddressLines))
|
||||
{
|
||||
var_dump($aAddressLines);
|
||||
exit;
|
||||
}
|
||||
return $aAddressLines;
|
||||
}
|
||||
if ($this->bAddressDetails) $aParams['addressdetails'] = '1';
|
||||
if ($this->bExtraTags) $aParams['extratags'] = '1';
|
||||
if ($this->bNameDetails) $aParams['namedetails'] = '1';
|
||||
|
||||
function getAddressNames()
|
||||
{
|
||||
$aAddressLines = $this->getAddressDetails(false);
|
||||
if ($this->bIncludePolygonAsPoints) $aParams['polygon'] = '1';
|
||||
if ($this->bIncludePolygonAsText) $aParams['polygon_text'] = '1';
|
||||
if ($this->bIncludePolygonAsGeoJSON) $aParams['polygon_geojson'] = '1';
|
||||
if ($this->bIncludePolygonAsKML) $aParams['polygon_kml'] = '1';
|
||||
if ($this->bIncludePolygonAsSVG) $aParams['polygon_svg'] = '1';
|
||||
|
||||
$aAddress = array();
|
||||
$aFallback = array();
|
||||
$aClassType = getClassTypes();
|
||||
foreach($aAddressLines as $aLine)
|
||||
{
|
||||
$bFallback = false;
|
||||
$aTypeLabel = false;
|
||||
if (isset($aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']];
|
||||
elseif (isset($aClassType[$aLine['class'].':'.$aLine['type']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type']];
|
||||
elseif (isset($aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))]))
|
||||
{
|
||||
$aTypeLabel = $aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))];
|
||||
$bFallback = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aTypeLabel = array('simplelabel'=>'address'.$aLine['rank_address']);
|
||||
$bFallback = true;
|
||||
}
|
||||
if ($aTypeLabel && ((isset($aLine['localname']) && $aLine['localname']) || (isset($aLine['housenumber']) && $aLine['housenumber'])))
|
||||
{
|
||||
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
|
||||
$sTypeLabel = str_replace(' ','_',$sTypeLabel);
|
||||
if (!isset($aAddress[$sTypeLabel]) || (isset($aFallback[$sTypeLabel]) && $aFallback[$sTypeLabel]) || $aLine['class'] == 'place')
|
||||
{
|
||||
$aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
|
||||
}
|
||||
$aFallback[$sTypeLabel] = $bFallback;
|
||||
}
|
||||
}
|
||||
return $aAddress;
|
||||
}
|
||||
if ($this->fPolygonSimplificationThreshold > 0.0) {
|
||||
$aParams['polygon_threshold'] = $this->fPolygonSimplificationThreshold;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
if (!$this->bDeDupe) $aParams['dedupe'] = '0';
|
||||
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
public function setAnchorSql($sPoint)
|
||||
{
|
||||
$this->sAnchorSql = $sPoint;
|
||||
}
|
||||
|
||||
public function setAddressRankList($aList)
|
||||
{
|
||||
$this->sAddressRankListSql = '('.join(',', $aList).')';
|
||||
}
|
||||
|
||||
public function setAllowedTypesSQLList($sSql)
|
||||
{
|
||||
$this->sAllowedTypesSQLList = $sSql;
|
||||
}
|
||||
|
||||
public function setLanguagePreference($aLangPrefOrder)
|
||||
{
|
||||
$this->aLangPrefOrderSql = $this->oDB->getArraySQL(
|
||||
$this->oDB->getDBQuotedList($aLangPrefOrder)
|
||||
);
|
||||
}
|
||||
|
||||
private function addressImportanceSql($sGeometry, $sPlaceId)
|
||||
{
|
||||
if ($this->sAnchorSql) {
|
||||
$sSQL = 'ST_Distance('.$this->sAnchorSql.','.$sGeometry.')';
|
||||
} else {
|
||||
$sSQL = '(SELECT max(ai_p.importance * (ai_p.rank_address + 2))';
|
||||
$sSQL .= ' FROM place_addressline ai_s, placex ai_p';
|
||||
$sSQL .= ' WHERE ai_s.place_id = '.$sPlaceId;
|
||||
$sSQL .= ' AND ai_p.place_id = ai_s.address_place_id ';
|
||||
$sSQL .= ' AND ai_s.isaddress ';
|
||||
$sSQL .= ' AND ai_p.importance is not null)';
|
||||
}
|
||||
|
||||
return $sSQL.' AS addressimportance,';
|
||||
}
|
||||
|
||||
private function langAddressSql($sHousenumber)
|
||||
{
|
||||
if ($this->bAddressDetails)
|
||||
return ''; // langaddress will be computed from address details
|
||||
|
||||
return 'get_address_by_language(place_id,'.$sHousenumber.','.$this->aLangPrefOrderSql.') AS langaddress,';
|
||||
}
|
||||
|
||||
public function lookupOSMID($sType, $iID)
|
||||
{
|
||||
$sSQL = 'select place_id from placex where osm_type = :type and osm_id = :id';
|
||||
$iPlaceID = $this->oDB->getOne($sSQL, array(':type' => $sType, ':id' => $iID));
|
||||
|
||||
if (!$iPlaceID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)));
|
||||
|
||||
return empty($aResults) ? null : reset($aResults);
|
||||
}
|
||||
|
||||
public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30)
|
||||
{
|
||||
Debug::newFunction('Place lookup');
|
||||
|
||||
if (empty($aResults)) {
|
||||
return array();
|
||||
}
|
||||
$aSubSelects = array();
|
||||
|
||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
||||
if ($sPlaceIDs) {
|
||||
Debug::printVar('Ids from placex', $sPlaceIDs);
|
||||
$sSQL = 'SELECT ';
|
||||
$sSQL .= ' osm_type,';
|
||||
$sSQL .= ' osm_id,';
|
||||
$sSQL .= ' class,';
|
||||
$sSQL .= ' type,';
|
||||
$sSQL .= ' admin_level,';
|
||||
$sSQL .= ' rank_search,';
|
||||
$sSQL .= ' rank_address,';
|
||||
$sSQL .= ' min(place_id) AS place_id,';
|
||||
$sSQL .= ' min(parent_place_id) AS parent_place_id,';
|
||||
$sSQL .= ' -1 as housenumber,';
|
||||
$sSQL .= ' country_code,';
|
||||
$sSQL .= $this->langAddressSql('-1');
|
||||
$sSQL .= ' get_name_by_language(name,'.$this->aLangPrefOrderSql.') AS placename,';
|
||||
$sSQL .= " get_name_by_language(name, ARRAY['ref']) AS ref,";
|
||||
if ($this->bExtraTags) {
|
||||
$sSQL .= 'hstore_to_json(extratags)::text AS extra,';
|
||||
}
|
||||
if ($this->bNameDetails) {
|
||||
$sSQL .= 'hstore_to_json(name)::text AS names,';
|
||||
}
|
||||
$sSQL .= ' avg(ST_X(centroid)) AS lon, ';
|
||||
$sSQL .= ' avg(ST_Y(centroid)) AS lat, ';
|
||||
$sSQL .= ' COALESCE(importance,0.75-(rank_search::float/40)) AS importance, ';
|
||||
$sSQL .= $this->addressImportanceSql(
|
||||
'ST_Collect(centroid)',
|
||||
'min(CASE WHEN placex.rank_search < 28 THEN placex.place_id ELSE placex.parent_place_id END)'
|
||||
);
|
||||
$sSQL .= " (extratags->'place') AS extra_place ";
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||
$sSQL .= ' AND (';
|
||||
$sSQL .= " placex.rank_address between $iMinRank and $iMaxRank ";
|
||||
if (14 >= $iMinRank && 14 <= $iMaxRank) {
|
||||
$sSQL .= " OR (extratags->'place') = 'city'";
|
||||
}
|
||||
if ($this->sAddressRankListSql) {
|
||||
$sSQL .= ' OR placex.rank_address in '.$this->sAddressRankListSql;
|
||||
}
|
||||
$sSQL .= ' ) ';
|
||||
if ($this->sAllowedTypesSQLList) {
|
||||
$sSQL .= 'AND placex.class in '.$this->sAllowedTypesSQLList;
|
||||
}
|
||||
$sSQL .= ' AND linked_place_id is null ';
|
||||
$sSQL .= ' GROUP BY ';
|
||||
$sSQL .= ' osm_type, ';
|
||||
$sSQL .= ' osm_id, ';
|
||||
$sSQL .= ' class, ';
|
||||
$sSQL .= ' type, ';
|
||||
$sSQL .= ' admin_level, ';
|
||||
$sSQL .= ' rank_search, ';
|
||||
$sSQL .= ' rank_address, ';
|
||||
$sSQL .= ' housenumber,';
|
||||
$sSQL .= ' country_code, ';
|
||||
$sSQL .= ' importance, ';
|
||||
if (!$this->bDeDupe) $sSQL .= 'place_id,';
|
||||
if (!$this->bAddressDetails) $sSQL .= 'langaddress, ';
|
||||
$sSQL .= ' placename, ';
|
||||
$sSQL .= ' ref, ';
|
||||
if ($this->bExtraTags) $sSQL .= 'extratags, ';
|
||||
if ($this->bNameDetails) $sSQL .= 'name, ';
|
||||
$sSQL .= " extratags->'place' ";
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
|
||||
// postcode table
|
||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE);
|
||||
if ($sPlaceIDs) {
|
||||
Debug::printVar('Ids from location_postcode', $sPlaceIDs);
|
||||
$sSQL = 'SELECT';
|
||||
$sSQL .= " 'P' as osm_type,";
|
||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,';
|
||||
$sSQL .= " 'place' as class, 'postcode' as type,";
|
||||
$sSQL .= ' null::smallint as admin_level, rank_search, rank_address,';
|
||||
$sSQL .= ' place_id, parent_place_id,';
|
||||
$sSQL .= ' -1 as housenumber,';
|
||||
$sSQL .= ' country_code,';
|
||||
$sSQL .= $this->langAddressSql('-1');
|
||||
$sSQL .= ' postcode as placename,';
|
||||
$sSQL .= ' postcode as ref,';
|
||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
||||
$sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,';
|
||||
$sSQL .= ' (0.75-(rank_search::float/40)) AS importance, ';
|
||||
$sSQL .= $this->addressImportanceSql('geometry', 'lp.parent_place_id');
|
||||
$sSQL .= ' null::text AS extra_place ';
|
||||
$sSQL .= 'FROM location_postcode lp';
|
||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||
$sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank";
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
|
||||
// All other tables are rank 30 only.
|
||||
if ($iMaxRank == 30) {
|
||||
// TIGER table
|
||||
if (CONST_Use_US_Tiger_Data) {
|
||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_TIGER);
|
||||
if ($sPlaceIDs) {
|
||||
Debug::printVar('Ids from Tiger table', $sPlaceIDs);
|
||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_TIGER);
|
||||
// Tiger search only if a housenumber was searched and if it was found
|
||||
// (realized through a join)
|
||||
$sSQL = ' SELECT ';
|
||||
$sSQL .= " 'T' AS osm_type, ";
|
||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, ';
|
||||
$sSQL .= " 'place' AS class, ";
|
||||
$sSQL .= " 'house' AS type, ";
|
||||
$sSQL .= ' null::smallint AS admin_level, ';
|
||||
$sSQL .= ' 30 AS rank_search, ';
|
||||
$sSQL .= ' 30 AS rank_address, ';
|
||||
$sSQL .= ' place_id, ';
|
||||
$sSQL .= ' parent_place_id, ';
|
||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
||||
$sSQL .= " 'us' AS country_code, ";
|
||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
||||
$sSQL .= ' null::text AS placename, ';
|
||||
$sSQL .= ' null::text AS ref, ';
|
||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
||||
$sSQL .= ' st_y(centroid) AS lat,';
|
||||
$sSQL .= ' -1.15 AS importance, ';
|
||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
||||
$sSQL .= ' null::text AS extra_place ';
|
||||
$sSQL .= ' FROM (';
|
||||
$sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here
|
||||
$sSQL .= ' ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) AS centroid, ';
|
||||
$sSQL .= ' parent_place_id, ';
|
||||
$sSQL .= ' housenumber_for_place';
|
||||
$sSQL .= ' FROM (';
|
||||
$sSQL .= ' location_property_tiger ';
|
||||
$sSQL .= ' JOIN (values '.$sHousenumbers.') AS housenumbers(place_id, housenumber_for_place) USING(place_id)) ';
|
||||
$sSQL .= ' WHERE ';
|
||||
$sSQL .= ' housenumber_for_place >= startnumber';
|
||||
$sSQL .= ' AND housenumber_for_place <= endnumber';
|
||||
$sSQL .= ' ) AS blub'; //postgres wants an alias here
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
}
|
||||
|
||||
// osmline - interpolated housenumbers
|
||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_OSMLINE);
|
||||
if ($sPlaceIDs) {
|
||||
Debug::printVar('Ids from interpolation', $sPlaceIDs);
|
||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_OSMLINE);
|
||||
// interpolation line search only if a housenumber was searched
|
||||
// (realized through a join)
|
||||
$sSQL = 'SELECT ';
|
||||
$sSQL .= " 'W' AS osm_type, ";
|
||||
$sSQL .= ' osm_id, ';
|
||||
$sSQL .= " 'place' AS class, ";
|
||||
$sSQL .= " 'house' AS type, ";
|
||||
$sSQL .= ' null::smallint AS admin_level, ';
|
||||
$sSQL .= ' 30 AS rank_search, ';
|
||||
$sSQL .= ' 30 AS rank_address, ';
|
||||
$sSQL .= ' place_id, ';
|
||||
$sSQL .= ' parent_place_id, ';
|
||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
||||
$sSQL .= ' country_code, ';
|
||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
||||
$sSQL .= ' null::text AS placename, ';
|
||||
$sSQL .= ' null::text AS ref, ';
|
||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
||||
$sSQL .= ' st_y(centroid) AS lat, ';
|
||||
// slightly smaller than the importance for normal houses
|
||||
$sSQL .= ' -0.1 AS importance, ';
|
||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
||||
$sSQL .= ' null::text AS extra_place ';
|
||||
$sSQL .= ' FROM (';
|
||||
$sSQL .= ' SELECT ';
|
||||
$sSQL .= ' osm_id, ';
|
||||
$sSQL .= ' place_id, ';
|
||||
$sSQL .= ' country_code, ';
|
||||
$sSQL .= ' CASE '; // interpolate the housenumbers here
|
||||
$sSQL .= ' WHEN startnumber != endnumber ';
|
||||
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) ';
|
||||
$sSQL .= ' ELSE ST_LineInterpolatePoint(linegeo, 0.5) ';
|
||||
$sSQL .= ' END as centroid, ';
|
||||
$sSQL .= ' parent_place_id, ';
|
||||
$sSQL .= ' housenumber_for_place ';
|
||||
$sSQL .= ' FROM (';
|
||||
$sSQL .= ' location_property_osmline ';
|
||||
$sSQL .= ' JOIN (values '.$sHousenumbers.') AS housenumbers(place_id, housenumber_for_place) USING(place_id)';
|
||||
$sSQL .= ' ) ';
|
||||
$sSQL .= ' WHERE housenumber_for_place >= 0 ';
|
||||
$sSQL .= ' ) as blub'; //postgres wants an alias here
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
|
||||
if (CONST_Use_Aux_Location_data) {
|
||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_AUX);
|
||||
if ($sPlaceIDs) {
|
||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_AUX);
|
||||
$sSQL = ' SELECT ';
|
||||
$sSQL .= " 'L' AS osm_type, ";
|
||||
$sSQL .= ' place_id AS osm_id, ';
|
||||
$sSQL .= " 'place' AS class,";
|
||||
$sSQL .= " 'house' AS type, ";
|
||||
$sSQL .= ' null::smallint AS admin_level, ';
|
||||
$sSQL .= ' 30 AS rank_search,';
|
||||
$sSQL .= ' 30 AS rank_address, ';
|
||||
$sSQL .= ' place_id,';
|
||||
$sSQL .= ' parent_place_id, ';
|
||||
$sSQL .= ' housenumber,';
|
||||
$sSQL .= " 'us' AS country_code, ";
|
||||
$sSQL .= $this->langAddressSql('-1');
|
||||
$sSQL .= ' null::text AS placename, ';
|
||||
$sSQL .= ' null::text AS ref, ';
|
||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
||||
$sSQL .= ' ST_X(centroid) AS lon, ';
|
||||
$sSQL .= ' ST_Y(centroid) AS lat, ';
|
||||
$sSQL .= ' -1.10 AS importance, ';
|
||||
$sSQL .= $this->addressImportanceSql(
|
||||
'centroid',
|
||||
'location_property_aux.parent_place_id'
|
||||
);
|
||||
$sSQL .= ' null::text AS extra_place ';
|
||||
$sSQL .= ' FROM location_property_aux ';
|
||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($aSubSelects)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sSQL = join(' UNION ', $aSubSelects);
|
||||
Debug::printSQL($sSQL);
|
||||
$aPlaces = $this->oDB->getAll($sSQL, null, 'Could not lookup place');
|
||||
|
||||
foreach ($aPlaces as &$aPlace) {
|
||||
$aPlace['importance'] = (float) $aPlace['importance'];
|
||||
if ($this->bAddressDetails) {
|
||||
// to get addressdetails for tiger data, the housenumber is needed
|
||||
$aPlace['address'] = new AddressDetails(
|
||||
$this->oDB,
|
||||
$aPlace['place_id'],
|
||||
$aPlace['housenumber'],
|
||||
$this->aLangPrefOrderSql
|
||||
);
|
||||
$aPlace['langaddress'] = $aPlace['address']->getLocaleAddress();
|
||||
}
|
||||
|
||||
if ($this->bExtraTags) {
|
||||
if ($aPlace['extra']) {
|
||||
$aPlace['sExtraTags'] = json_decode($aPlace['extra']);
|
||||
} else {
|
||||
$aPlace['sExtraTags'] = (object) array();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->bNameDetails) {
|
||||
if ($aPlace['names']) {
|
||||
$aPlace['sNameDetails'] = json_decode($aPlace['names']);
|
||||
} else {
|
||||
$aPlace['sNameDetails'] = (object) array();
|
||||
}
|
||||
}
|
||||
|
||||
$aPlace['addresstype'] = ClassTypes\getProperty(
|
||||
$aPlace,
|
||||
'simplelabel',
|
||||
$aPlace['class']
|
||||
);
|
||||
}
|
||||
|
||||
Debug::printVar('Places', $aPlaces);
|
||||
|
||||
return $aPlaces;
|
||||
}
|
||||
|
||||
/* returns an array which will contain the keys
|
||||
* aBoundingBox
|
||||
* and may also contain one or more of the keys
|
||||
* asgeojson
|
||||
* askml
|
||||
* assvg
|
||||
* astext
|
||||
* lat
|
||||
* lon
|
||||
*/
|
||||
|
||||
|
||||
public function getOutlines($iPlaceID, $fLon = null, $fLat = null, $fRadius = null, $fLonReverse = null, $fLatReverse = null)
|
||||
{
|
||||
|
||||
$aOutlineResult = array();
|
||||
if (!$iPlaceID) return $aOutlineResult;
|
||||
|
||||
if (CONST_Search_AreaPolygons) {
|
||||
// Get the bounding box and outline polygon
|
||||
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,';
|
||||
if ($fLonReverse != null && $fLatReverse != null) {
|
||||
$sSQL .= ' ST_Y(closest_point) as centrelat,';
|
||||
$sSQL .= ' ST_X(closest_point) as centrelon,';
|
||||
} else {
|
||||
$sSQL .= ' ST_Y(centroid) as centrelat, ST_X(centroid) as centrelon,';
|
||||
}
|
||||
$sSQL .= ' ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,';
|
||||
$sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon';
|
||||
if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ',ST_AsGeoJSON(geometry) as asgeojson';
|
||||
if ($this->bIncludePolygonAsKML) $sSQL .= ',ST_AsKML(geometry) as askml';
|
||||
if ($this->bIncludePolygonAsSVG) $sSQL .= ',ST_AsSVG(geometry) as assvg';
|
||||
if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ',ST_AsText(geometry) as astext';
|
||||
if ($fLonReverse != null && $fLatReverse != null) {
|
||||
$sFrom = ' from (SELECT * , CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN ';
|
||||
$sFrom .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))';
|
||||
$sFrom .=' ELSE centroid END AS closest_point';
|
||||
$sFrom .= ' from placex where place_id = '.$iPlaceID.') as plx';
|
||||
} else {
|
||||
$sFrom = ' from placex where place_id = '.$iPlaceID;
|
||||
}
|
||||
if ($this->fPolygonSimplificationThreshold > 0) {
|
||||
$sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'.$sFrom.') as plx';
|
||||
} else {
|
||||
$sSQL .= $sFrom;
|
||||
}
|
||||
|
||||
$aPointPolygon = $this->oDB->getRow($sSQL, null, 'Could not get outline');
|
||||
|
||||
if ($aPointPolygon && $aPointPolygon['place_id']) {
|
||||
if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) {
|
||||
$aOutlineResult['lat'] = $aPointPolygon['centrelat'];
|
||||
$aOutlineResult['lon'] = $aPointPolygon['centrelon'];
|
||||
}
|
||||
|
||||
if ($this->bIncludePolygonAsGeoJSON) $aOutlineResult['asgeojson'] = $aPointPolygon['asgeojson'];
|
||||
if ($this->bIncludePolygonAsKML) $aOutlineResult['askml'] = $aPointPolygon['askml'];
|
||||
if ($this->bIncludePolygonAsSVG) $aOutlineResult['assvg'] = $aPointPolygon['assvg'];
|
||||
if ($this->bIncludePolygonAsText) $aOutlineResult['astext'] = $aPointPolygon['astext'];
|
||||
if ($this->bIncludePolygonAsPoints) $aOutlineResult['aPolyPoints'] = geometryText2Points($aPointPolygon['astext'], $fRadius);
|
||||
|
||||
|
||||
if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) {
|
||||
$aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius;
|
||||
$aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius;
|
||||
}
|
||||
|
||||
if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) {
|
||||
$aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius;
|
||||
$aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
|
||||
}
|
||||
|
||||
$aOutlineResult['aBoundingBox'] = array(
|
||||
(string)$aPointPolygon['minlat'],
|
||||
(string)$aPointPolygon['maxlat'],
|
||||
(string)$aPointPolygon['minlon'],
|
||||
(string)$aPointPolygon['maxlon']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// as a fallback we generate a bounding box without knowing the size of the geometry
|
||||
if ((!isset($aOutlineResult['aBoundingBox'])) && isset($fLon)) {
|
||||
//
|
||||
if ($this->bIncludePolygonAsPoints) {
|
||||
$sGeometryText = 'POINT('.$fLon.','.$fLat.')';
|
||||
$aOutlineResult['aPolyPoints'] = geometryText2Points($sGeometryText, $fRadius);
|
||||
}
|
||||
|
||||
$aBounds = array();
|
||||
$aBounds['minlat'] = $fLat - $fRadius;
|
||||
$aBounds['maxlat'] = $fLat + $fRadius;
|
||||
$aBounds['minlon'] = $fLon - $fRadius;
|
||||
$aBounds['maxlon'] = $fLon + $fRadius;
|
||||
|
||||
$aOutlineResult['aBoundingBox'] = array(
|
||||
(string)$aBounds['minlat'],
|
||||
(string)$aBounds['maxlat'],
|
||||
(string)$aBounds['minlon'],
|
||||
(string)$aBounds['maxlon']
|
||||
);
|
||||
}
|
||||
return $aOutlineResult;
|
||||
}
|
||||
}
|
||||
|
||||
99
lib/Result.php
Normal file
99
lib/Result.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
/**
|
||||
* A single result of a search operation or a reverse lookup.
|
||||
*
|
||||
* This object only contains the id of the result. It does not yet
|
||||
* have any details needed to format the output document.
|
||||
*/
|
||||
class Result
|
||||
{
|
||||
const TABLE_PLACEX = 0;
|
||||
const TABLE_POSTCODE = 1;
|
||||
const TABLE_OSMLINE = 2;
|
||||
const TABLE_AUX = 3;
|
||||
const TABLE_TIGER = 4;
|
||||
|
||||
/// Database table that contains the result.
|
||||
public $iTable;
|
||||
/// Id of the result.
|
||||
public $iId;
|
||||
/// House number (only for interpolation results).
|
||||
public $iHouseNumber = -1;
|
||||
/// Number of exact matches in address (address searches only).
|
||||
public $iExactMatches = 0;
|
||||
/// Subranking within the results (the higher the worse).
|
||||
public $iResultRank = 0;
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'Table' => $this->iTable,
|
||||
'ID' => $this->iId,
|
||||
'House number' => $this->iHouseNumber,
|
||||
'Exact Matches' => $this->iExactMatches,
|
||||
'Result rank' => $this->iResultRank
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function __construct($sId, $iTable = Result::TABLE_PLACEX)
|
||||
{
|
||||
$this->iTable = $iTable;
|
||||
$this->iId = (int) $sId;
|
||||
}
|
||||
|
||||
public static function joinIdsByTable($aResults, $iTable)
|
||||
{
|
||||
return join(',', array_keys(array_filter(
|
||||
$aResults,
|
||||
function ($aValue) use ($iTable) {
|
||||
return $aValue->iTable == $iTable;
|
||||
}
|
||||
)));
|
||||
}
|
||||
public static function sqlHouseNumberTable($aResults, $iTable)
|
||||
{
|
||||
$sHousenumbers = '';
|
||||
$sSep = '';
|
||||
foreach ($aResults as $oResult) {
|
||||
if ($oResult->iTable == $iTable) {
|
||||
$sHousenumbers .= $sSep.'('.$oResult->iId.',';
|
||||
$sHousenumbers .= $oResult->iHouseNumber.')';
|
||||
$sSep = ',';
|
||||
}
|
||||
}
|
||||
|
||||
return $sHousenumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a result array into highest ranked result and the rest
|
||||
*
|
||||
* @param object[] $aResults List of results to split.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function splitResults($aResults)
|
||||
{
|
||||
$aHead = array();
|
||||
$aTail = array();
|
||||
$iMinRank = 10000;
|
||||
|
||||
foreach ($aResults as $oRes) {
|
||||
if ($oRes->iResultRank < $iMinRank) {
|
||||
$aTail = array_merge($aTail, $aHead);
|
||||
$aHead = array($oRes->iId => $oRes);
|
||||
$iMinRank = $oRes->iResultRank;
|
||||
} elseif ($oRes->iResultRank == $iMinRank) {
|
||||
$aHead[$oRes->iId] = $oRes;
|
||||
} else {
|
||||
$aTail[$oRes->iId] = $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
return array('head' => $aHead, 'tail' => $aTail);
|
||||
}
|
||||
}
|
||||
@@ -1,171 +1,347 @@
|
||||
<?php
|
||||
class ReverseGeocode
|
||||
{
|
||||
protected $oDB;
|
||||
|
||||
protected $fLat;
|
||||
protected $fLon;
|
||||
protected $iMaxRank = 28;
|
||||
namespace Nominatim;
|
||||
|
||||
protected $aLangPrefOrder = array();
|
||||
require_once(CONST_BasePath.'/lib/Result.php');
|
||||
|
||||
function ReverseGeocode(&$oDB)
|
||||
{
|
||||
$this->oDB =& $oDB;
|
||||
}
|
||||
|
||||
function setLanguagePreference($aLangPref)
|
||||
{
|
||||
$this->aLangPrefOrder = $aLangPref;
|
||||
}
|
||||
|
||||
function setLatLon($fLat, $fLon)
|
||||
{
|
||||
$this->fLat = (float)$fLat;
|
||||
$this->fLon = (float)$fLon;
|
||||
}
|
||||
|
||||
function setRank($iRank)
|
||||
{
|
||||
$this->iMaxRank = $iRank;
|
||||
}
|
||||
|
||||
function setZoom($iZoom)
|
||||
{
|
||||
// Zoom to rank, this could probably be calculated but a lookup gives fine control
|
||||
$aZoomRank = array(
|
||||
0 => 2, // Continent / Sea
|
||||
1 => 2,
|
||||
2 => 2,
|
||||
3 => 4, // Country
|
||||
4 => 4,
|
||||
5 => 8, // State
|
||||
6 => 10, // Region
|
||||
7 => 10,
|
||||
8 => 12, // County
|
||||
9 => 12,
|
||||
10 => 17, // City
|
||||
11 => 17,
|
||||
12 => 18, // Town / Village
|
||||
13 => 18,
|
||||
14 => 22, // Suburb
|
||||
15 => 22,
|
||||
16 => 26, // Street, TODO: major street?
|
||||
17 => 26,
|
||||
18 => 30, // or >, Building
|
||||
19 => 30, // or >, Building
|
||||
);
|
||||
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
|
||||
}
|
||||
|
||||
function lookup()
|
||||
{
|
||||
$sPointSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)';
|
||||
$iMaxRank = $this->iMaxRank;
|
||||
$iMaxRank_orig = $this->iMaxRank;
|
||||
|
||||
// Find the nearest point
|
||||
$fSearchDiam = 0.0004;
|
||||
$iPlaceID = null;
|
||||
$aArea = false;
|
||||
$fMaxAreaDistance = 1;
|
||||
$bIsInUnitedStates = false;
|
||||
$bPlaceIsTiger = false;
|
||||
while(!$iPlaceID && $fSearchDiam < $fMaxAreaDistance)
|
||||
{
|
||||
$fSearchDiam = $fSearchDiam * 2;
|
||||
|
||||
// If we have to expand the search area by a large amount then we need a larger feature
|
||||
// then there is a limit to how small the feature should be
|
||||
if ($fSearchDiam > 2 && $iMaxRank > 4) $iMaxRank = 4;
|
||||
if ($fSearchDiam > 1 && $iMaxRank > 9) $iMaxRank = 8;
|
||||
if ($fSearchDiam > 0.8 && $iMaxRank > 10) $iMaxRank = 10;
|
||||
if ($fSearchDiam > 0.6 && $iMaxRank > 12) $iMaxRank = 12;
|
||||
if ($fSearchDiam > 0.2 && $iMaxRank > 17) $iMaxRank = 17;
|
||||
if ($fSearchDiam > 0.1 && $iMaxRank > 18) $iMaxRank = 18;
|
||||
if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22;
|
||||
if ($fSearchDiam > 0.001 && $iMaxRank > 26) $iMaxRank = 26;
|
||||
|
||||
$sSQL = 'select place_id,parent_place_id,rank_search,calculated_country_code from placex';
|
||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
||||
$sSQL .= ' and rank_search != 28 and rank_search >= '.$iMaxRank;
|
||||
$sSQL .= ' and (name is not null or housenumber is not null)';
|
||||
$sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
||||
$sSQL .= ' and indexed_status = 0 ';
|
||||
$sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
|
||||
$sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
|
||||
$sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1';
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL);
|
||||
if (PEAR::IsError($aPlace))
|
||||
{
|
||||
failInternalError("Could not determine closest place.", $sSQL, $aPlace);
|
||||
}
|
||||
$iPlaceID = $aPlace['place_id'];
|
||||
$iParentPlaceID = $aPlace['parent_place_id'];
|
||||
$bIsInUnitedStates = ($aPlace['calculated_country_code'] == 'us');
|
||||
}
|
||||
|
||||
// Only street found? If it's in the US we can check TIGER data for nearest housenumber
|
||||
if ($bIsInUnitedStates && $iMaxRank_orig >= 28 && $iPlaceID && ($aPlace['rank_search'] == 26 || $aPlace['rank_search'] == 27 ))
|
||||
{
|
||||
$fSearchDiam = 0.001;
|
||||
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search ';
|
||||
if (CONST_Debug) { $sSQL .= ', housenumber, ST_distance('.$sPointSQL.', centroid) as distance, st_y(centroid) as lat, st_x(centroid) as lon'; }
|
||||
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$iPlaceID;
|
||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.')';
|
||||
$sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', centroid) ASC limit 1';
|
||||
class ReverseGeocode
|
||||
{
|
||||
protected $oDB;
|
||||
protected $iMaxRank = 28;
|
||||
|
||||
|
||||
// print all house numbers in the parent (street)
|
||||
if (CONST_Debug)
|
||||
{
|
||||
$sSQL = preg_replace('/limit 1/', 'limit 100', $sSQL);
|
||||
var_dump($sSQL);
|
||||
public function __construct(&$oDB)
|
||||
{
|
||||
$this->oDB =& $oDB;
|
||||
}
|
||||
|
||||
$aAllHouses = $this->oDB->getAll($sSQL);
|
||||
foreach($aAllHouses as $i)
|
||||
{
|
||||
echo $i['housenumber'] . ' | ' . $i['distance'] * 1000 . ' | ' . $i['lat'] . ' | ' . $i['lon']. ' | '. "<br>\n";
|
||||
}
|
||||
}
|
||||
|
||||
$aPlaceTiger = $this->oDB->getRow($sSQL);
|
||||
if (PEAR::IsError($aPlace))
|
||||
{
|
||||
failInternalError("Could not determine closest Tiger place.", $sSQL, $aPlaceTiger);
|
||||
}
|
||||
if ($aPlaceTiger)
|
||||
{
|
||||
if (CONST_Debug) var_dump('found Tiger place', $aPlaceTiger);
|
||||
$bPlaceIsTiger = true;
|
||||
$aPlace = $aPlaceTiger;
|
||||
$iPlaceID = $aPlaceTiger['place_id'];
|
||||
$iParentPlaceID = $aPlaceTiger['parent_place_id']; // the street
|
||||
}
|
||||
}
|
||||
public function setZoom($iZoom)
|
||||
{
|
||||
// Zoom to rank, this could probably be calculated but a lookup gives fine control
|
||||
$aZoomRank = array(
|
||||
0 => 2, // Continent / Sea
|
||||
1 => 2,
|
||||
2 => 2,
|
||||
3 => 4, // Country
|
||||
4 => 4,
|
||||
5 => 8, // State
|
||||
6 => 10, // Region
|
||||
7 => 10,
|
||||
8 => 12, // County
|
||||
9 => 12,
|
||||
10 => 17, // City
|
||||
11 => 17,
|
||||
12 => 18, // Town / Village
|
||||
13 => 18,
|
||||
14 => 22, // Suburb
|
||||
15 => 22,
|
||||
16 => 26, // major street
|
||||
17 => 27, // minor street
|
||||
18 => 30, // or >, Building
|
||||
19 => 30, // or >, Building
|
||||
);
|
||||
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
|
||||
}
|
||||
|
||||
// The point we found might be too small - use the address to find what it is a child of
|
||||
if ($iPlaceID && $iMaxRank < 28)
|
||||
{
|
||||
if ($aPlace['rank_search'] > 28 && $iParentPlaceID && !$bPlaceIsTiger)
|
||||
{
|
||||
$iPlaceID = $iParentPlaceID;
|
||||
}
|
||||
$sSQL = "select address_place_id from place_addressline where place_id = $iPlaceID order by abs(cached_rank_address - $iMaxRank) asc,cached_rank_address desc,isaddress desc,distance desc limit 1";
|
||||
$iPlaceID = $this->oDB->getOne($sSQL);
|
||||
if (PEAR::IsError($iPlaceID))
|
||||
{
|
||||
failInternalError("Could not get parent for place.", $sSQL, $iPlaceID);
|
||||
}
|
||||
if (!$iPlaceID)
|
||||
{
|
||||
$iPlaceID = $aPlace['place_id'];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Find the closest interpolation with the given search diameter.
|
||||
*
|
||||
* @param string $sPointSQL Reverse geocoding point as SQL
|
||||
* @param float $fSearchDiam Search diameter
|
||||
*
|
||||
* @return Record of the interpolation or null.
|
||||
*/
|
||||
protected function lookupInterpolation($sPointSQL, $fSearchDiam)
|
||||
{
|
||||
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
|
||||
$sSQL .= ' ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
||||
$sSQL .= ' startnumber, endnumber, interpolationtype,';
|
||||
$sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance';
|
||||
$sSQL .= ' FROM location_property_osmline';
|
||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
|
||||
$sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
|
||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||
|
||||
return array('place_id' => $iPlaceID,
|
||||
'type' => $bPlaceIsTiger ? 'tiger' : 'osm');
|
||||
}
|
||||
}
|
||||
?>
|
||||
return $this->oDB->getRow(
|
||||
$sSQL,
|
||||
null,
|
||||
'Could not determine closest housenumber on an osm interpolation line.'
|
||||
);
|
||||
}
|
||||
|
||||
protected function lookupLargeArea($sPointSQL, $iMaxRank)
|
||||
{
|
||||
$oResult = null;
|
||||
|
||||
if ($iMaxRank > 4) {
|
||||
$aPlace = $this->lookupPolygon($sPointSQL, $iMaxRank);
|
||||
if ($aPlace) {
|
||||
return new Result($aPlace['place_id']);
|
||||
}
|
||||
}
|
||||
|
||||
// If no polygon which contains the searchpoint is found,
|
||||
// searches in the country_osm_grid table for a polygon.
|
||||
return $this->lookupInCountry($sPointSQL, $iMaxRank);
|
||||
}
|
||||
|
||||
protected function lookupInCountry($sPointSQL, $iMaxRank)
|
||||
{
|
||||
// searches for polygon in table country_osm_grid which contains the searchpoint
|
||||
// and searches for the nearest place node to the searchpoint in this polygon
|
||||
$sSQL = 'SELECT country_code FROM country_osm_grid';
|
||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
|
||||
|
||||
$sCountryCode = $this->oDB->getOne(
|
||||
$sSQL,
|
||||
null,
|
||||
'Could not determine country polygon containing the point.'
|
||||
);
|
||||
if ($sCountryCode) {
|
||||
if ($iMaxRank > 4) {
|
||||
// look for place nodes with the given country code
|
||||
$sSQL = 'SELECT place_id FROM';
|
||||
$sSQL .= ' (SELECT place_id, rank_search,';
|
||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= ' WHERE osm_type = \'N\'';
|
||||
$sSQL .= ' AND country_code = \''.$sCountryCode.'\'';
|
||||
$sSQL .= ' AND rank_search between 5 and ' .min(25, $iMaxRank);
|
||||
$sSQL .= ' AND class = \'place\' AND type != \'postcode\'';
|
||||
$sSQL .= ' AND name IS NOT NULL ';
|
||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, 1.8)) p ';
|
||||
$sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
|
||||
$sSQL .= ' ORDER BY rank_search DESC, distance ASC';
|
||||
$sSQL .= ' LIMIT 1';
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
if ($aPlace) {
|
||||
return new Result($aPlace['place_id']);
|
||||
}
|
||||
}
|
||||
|
||||
// still nothing, then return the country object
|
||||
$sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', centroid) as distance';
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= ' WHERE country_code = \''.$sCountryCode.'\'';
|
||||
$sSQL .= ' AND rank_search = 4 AND rank_address = 4';
|
||||
$sSQL .= ' AND class in (\'boundary\', \'place\')';
|
||||
$sSQL .= ' AND linked_place_id is null';
|
||||
$sSQL .= ' ORDER BY distance ASC';
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
if ($aPlace) {
|
||||
return new Result($aPlace['place_id']);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for areas or nodes for areas or nodes between state and suburb level.
|
||||
*
|
||||
* @param string $sPointSQL Search point as SQL string.
|
||||
* @param int $iMaxRank Maximum address rank of the feature.
|
||||
*
|
||||
* @return Record of the found feature or null.
|
||||
*
|
||||
* Searches first for polygon that contains the search point.
|
||||
* If such a polygon is found, place nodes with a higher rank are
|
||||
* searched inside the polygon.
|
||||
*/
|
||||
protected function lookupPolygon($sPointSQL, $iMaxRank)
|
||||
{
|
||||
// polygon search begins at suburb-level
|
||||
if ($iMaxRank > 25) $iMaxRank = 25;
|
||||
// no polygon search over country-level
|
||||
if ($iMaxRank < 5) $iMaxRank = 5;
|
||||
// search for polygon
|
||||
$sSQL = 'SELECT place_id, parent_place_id, rank_address, rank_search FROM';
|
||||
$sSQL .= '(select place_id, parent_place_id, rank_address, rank_search, country_code, geometry';
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= ' WHERE ST_GeometryType(geometry) in (\'ST_Polygon\', \'ST_MultiPolygon\')';
|
||||
$sSQL .= ' AND rank_address Between 5 AND ' .$iMaxRank;
|
||||
$sSQL .= ' AND geometry && '.$sPointSQL;
|
||||
$sSQL .= ' AND type != \'postcode\' ';
|
||||
$sSQL .= ' AND name is not null';
|
||||
$sSQL .= ' AND indexed_status = 0 and linked_place_id is null';
|
||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 50 ) as a';
|
||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
||||
|
||||
$aPoly = $this->oDB->getRow($sSQL, null, 'Could not determine polygon containing the point.');
|
||||
|
||||
if ($aPoly) {
|
||||
// if a polygon is found, search for placenodes begins ...
|
||||
$iParentPlaceID = $aPoly['parent_place_id'];
|
||||
$iRankAddress = $aPoly['rank_address'];
|
||||
$iRankSearch = $aPoly['rank_search'];
|
||||
$iPlaceID = $aPoly['place_id'];
|
||||
|
||||
if ($iRankAddress != $iMaxRank) {
|
||||
$sSQL = 'SELECT place_id FROM ';
|
||||
$sSQL .= '(SELECT place_id, rank_search, country_code, geometry,';
|
||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= ' WHERE osm_type = \'N\'';
|
||||
// using rank_search because of a better differentiation
|
||||
// for place nodes at rank_address 16
|
||||
$sSQL .= ' AND rank_search > '.$iRankSearch;
|
||||
$sSQL .= ' AND rank_search <= '.$iMaxRank;
|
||||
$sSQL .= ' AND class = \'place\'';
|
||||
$sSQL .= ' AND type != \'postcode\'';
|
||||
$sSQL .= ' AND name IS NOT NULL ';
|
||||
$sSQL .= ' AND indexed_status = 0 AND linked_place_id is null';
|
||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, reverse_place_diameter('.$iRankSearch.'::smallint))';
|
||||
$sSQL .= ' ORDER BY distance ASC,';
|
||||
$sSQL .= ' rank_address DESC';
|
||||
$sSQL .= ' limit 500) as a';
|
||||
$sSQL .= ' WHERE ST_CONTAINS((SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.'), geometry )';
|
||||
$sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
|
||||
$sSQL .= ' ORDER BY distance ASC, rank_search DESC';
|
||||
$sSQL .= ' LIMIT 1';
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlacNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
if ($aPlacNode) {
|
||||
return $aPlacNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aPoly;
|
||||
}
|
||||
|
||||
|
||||
public function lookup($fLat, $fLon, $bDoInterpolation = true)
|
||||
{
|
||||
return $this->lookupPoint(
|
||||
'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)',
|
||||
$bDoInterpolation
|
||||
);
|
||||
}
|
||||
|
||||
public function lookupPoint($sPointSQL, $bDoInterpolation = true)
|
||||
{
|
||||
// starts if the search is on POI or street level,
|
||||
// searches for the nearest POI or street,
|
||||
// if a street is found and a POI is searched for,
|
||||
// the nearest POI which the found street is a parent of is choosen.
|
||||
$iMaxRank = $this->iMaxRank;
|
||||
|
||||
// Find the nearest point
|
||||
$fSearchDiam = 0.006;
|
||||
$oResult = null;
|
||||
$aPlace = null;
|
||||
|
||||
// for POI or street level
|
||||
if ($iMaxRank >= 26) {
|
||||
$sSQL = 'select place_id,parent_place_id,rank_address,country_code,';
|
||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||
$sSQL .= ' FROM ';
|
||||
$sSQL .= ' placex';
|
||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
||||
$sSQL .= ' AND';
|
||||
$sSQL .= ' rank_address between 26 and '.$iMaxRank;
|
||||
$sSQL .= ' and (name is not null or housenumber is not null';
|
||||
$sSQL .= ' or rank_address between 26 and 27)';
|
||||
$sSQL .= ' and (rank_address between 26 and 27';
|
||||
$sSQL .= ' or ST_GeometryType(geometry) != \'ST_LineString\')';
|
||||
$sSQL .= ' and class not in (\'boundary\')';
|
||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||
$sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
|
||||
$sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
|
||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
||||
|
||||
if (CONST_Debug) var_dump($aPlace);
|
||||
if ($aPlace) {
|
||||
$iPlaceID = $aPlace['place_id'];
|
||||
$oResult = new Result($iPlaceID);
|
||||
$iRankAddress = $aPlace['rank_address'];
|
||||
$iParentPlaceID = $aPlace['parent_place_id'];
|
||||
}
|
||||
|
||||
if ($bDoInterpolation && $iMaxRank >= 30) {
|
||||
$fDistance = $fSearchDiam;
|
||||
if ($aPlace) {
|
||||
// We can't reliably go from the closest street to an
|
||||
// interpolation line because the closest interpolation
|
||||
// may have a different street segments as a parent.
|
||||
// Therefore allow an interpolation line to take precendence
|
||||
// even when the street is closer.
|
||||
$fDistance = $iRankAddress < 28 ? 0.001 : $aPlace['distance'];
|
||||
}
|
||||
|
||||
$aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
|
||||
|
||||
if ($aHouse) {
|
||||
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
|
||||
$oResult->iHouseNumber = closestHouseNumber($aHouse);
|
||||
$aPlace = $aHouse;
|
||||
$iRankAddress = 30;
|
||||
}
|
||||
}
|
||||
|
||||
if ($aPlace) {
|
||||
// if street and maxrank > streetlevel
|
||||
if ($iRankAddress <= 27 && $iMaxRank > 27) {
|
||||
// find the closest object (up to a certain radius) of which the street is a parent of
|
||||
$sSQL = ' select place_id,';
|
||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||
$sSQL .= ' FROM ';
|
||||
$sSQL .= ' placex';
|
||||
// radius ?
|
||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
|
||||
$sSQL .= ' AND parent_place_id = '.$iPlaceID;
|
||||
$sSQL .= ' and rank_address > 28';
|
||||
$sSQL .= ' and ST_GeometryType(geometry) != \'ST_LineString\'';
|
||||
$sSQL .= ' and (name is not null or housenumber is not null)';
|
||||
$sSQL .= ' and class not in (\'boundary\')';
|
||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aStreet = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
||||
if ($aStreet) {
|
||||
if (CONST_Debug) var_dump($aStreet);
|
||||
$oResult = new Result($aStreet['place_id']);
|
||||
}
|
||||
}
|
||||
|
||||
// In the US we can check TIGER data for nearest housenumber
|
||||
if (CONST_Use_US_Tiger_Data
|
||||
&& $iRankAddress <= 27
|
||||
&& $aPlace['country_code'] == 'us'
|
||||
&& $this->iMaxRank >= 28
|
||||
) {
|
||||
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,';
|
||||
$sSQL .= 'ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
||||
$sSQL .= 'ST_distance('.$sPointSQL.', linegeo) as distance,';
|
||||
$sSQL .= 'startnumber,endnumber,interpolationtype';
|
||||
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId;
|
||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
|
||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlaceTiger = $this->oDB->getRow($sSQL, null, 'Could not determine closest Tiger place.');
|
||||
if ($aPlaceTiger) {
|
||||
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
||||
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
||||
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if no POI or street is found ...
|
||||
$oResult = $this->lookupLargeArea($sPointSQL, 25);
|
||||
}
|
||||
} else {
|
||||
// lower than street level ($iMaxRank < 26 )
|
||||
$oResult = $this->lookupLargeArea($sPointSQL, $iMaxRank);
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
}
|
||||
|
||||
284
lib/SearchContext.php
Normal file
284
lib/SearchContext.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/lib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Collection of search constraints that are independent of the
|
||||
* actual interpretation of the search query.
|
||||
*
|
||||
* The search context is shared between all SearchDescriptions. This
|
||||
* object mainly serves as context provider for the database queries.
|
||||
* Therefore most data is directly cached as SQL statements.
|
||||
*/
|
||||
class SearchContext
|
||||
{
|
||||
/// Search radius around a given Near reference point.
|
||||
private $fNearRadius = false;
|
||||
/// True if search must be restricted to viewbox only.
|
||||
public $bViewboxBounded = false;
|
||||
|
||||
/// Reference point for search (as SQL).
|
||||
public $sqlNear = '';
|
||||
/// Viewbox selected for search (as SQL).
|
||||
public $sqlViewboxSmall = '';
|
||||
/// Viewbox with a larger buffer around (as SQL).
|
||||
public $sqlViewboxLarge = '';
|
||||
/// Reference along a route (as SQL).
|
||||
public $sqlViewboxCentre = '';
|
||||
/// List of countries to restrict search to (as SQL).
|
||||
public $sqlCountryList = '';
|
||||
/// List of place IDs to exclude (as SQL).
|
||||
private $sqlExcludeList = '';
|
||||
|
||||
|
||||
/**
|
||||
* Check if a reference point is defined.
|
||||
*
|
||||
* @return bool True if a reference point is defined.
|
||||
*/
|
||||
public function hasNearPoint()
|
||||
{
|
||||
return $this->fNearRadius !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radius around reference point.
|
||||
*
|
||||
* @return float Search radius around reference point.
|
||||
*/
|
||||
public function nearRadius()
|
||||
{
|
||||
return $this->fNearRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search reference point in WGS84.
|
||||
*
|
||||
* If set, then only places around this point will be taken into account.
|
||||
*
|
||||
* @param float $fLat Latitude of point.
|
||||
* @param float $fLon Longitude of point.
|
||||
* @param float $fRadius Search radius around point.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
|
||||
{
|
||||
$this->fNearRadius = $fRadius;
|
||||
$this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the search is geographically restricted.
|
||||
*
|
||||
* Searches are restricted if a reference point is given or if
|
||||
* a bounded viewbox is set.
|
||||
*
|
||||
* @return bool True, if the search is geographically bounded.
|
||||
*/
|
||||
public function isBoundedSearch()
|
||||
{
|
||||
return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rectangular viewbox.
|
||||
*
|
||||
* The viewbox may be bounded which means that no search results
|
||||
* must be outside the viewbox.
|
||||
*
|
||||
* @param float[4] $aViewBox Coordinates of the viewbox.
|
||||
* @param bool $bBounded True if the viewbox is bounded.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setViewboxFromBox(&$aViewBox, $bBounded)
|
||||
{
|
||||
$this->bViewboxBounded = $bBounded;
|
||||
$this->sqlViewboxCentre = '';
|
||||
|
||||
$this->sqlViewboxSmall = sprintf(
|
||||
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
||||
$aViewBox[0],
|
||||
$aViewBox[1],
|
||||
$aViewBox[2],
|
||||
$aViewBox[3]
|
||||
);
|
||||
|
||||
$fHeight = abs($aViewBox[0] - $aViewBox[2]);
|
||||
$fWidth = abs($aViewBox[1] - $aViewBox[3]);
|
||||
|
||||
$this->sqlViewboxLarge = sprintf(
|
||||
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
||||
max($aViewBox[0], $aViewBox[2]) + $fHeight,
|
||||
max($aViewBox[1], $aViewBox[3]) + $fWidth,
|
||||
min($aViewBox[0], $aViewBox[2]) - $fHeight,
|
||||
min($aViewBox[1], $aViewBox[3]) - $fWidth
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set viewbox along a route.
|
||||
*
|
||||
* The viewbox may be bounded which means that no search results
|
||||
* must be outside the viewbox.
|
||||
*
|
||||
* @param object $oDB Nominatim::DB instance to use for computing the box.
|
||||
* @param string[] $aRoutePoints List of x,y coordinates along a route.
|
||||
* @param float $fRouteWidth Buffer around the route to use.
|
||||
* @param bool $bBounded True if the viewbox bounded.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded)
|
||||
{
|
||||
$this->bViewboxBounded = $bBounded;
|
||||
$this->sqlViewboxCentre = "ST_SetSRID('LINESTRING(";
|
||||
$sSep = '';
|
||||
foreach ($aRoutePoints as $aPoint) {
|
||||
$fPoint = (float)$aPoint;
|
||||
$this->sqlViewboxCentre .= $sSep.$fPoint;
|
||||
$sSep = ($sSep == ' ') ? ',' : ' ';
|
||||
}
|
||||
$this->sqlViewboxCentre .= ")'::geometry,4326)";
|
||||
|
||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
|
||||
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox');
|
||||
$this->sqlViewboxSmall = "'".$sGeom."'::geometry";
|
||||
|
||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
|
||||
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox');
|
||||
$this->sqlViewboxLarge = "'".$sGeom."'::geometry";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set list of excluded place IDs.
|
||||
*
|
||||
* @param integer[] $aExcluded List of IDs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setExcludeList($aExcluded)
|
||||
{
|
||||
$this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set list of countries to restrict search to.
|
||||
*
|
||||
* @param string[] $aCountries List of two-letter lower-case country codes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCountryList($aCountries)
|
||||
{
|
||||
$this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a reference point from a query string.
|
||||
*
|
||||
* @param string $sQuery Query to scan.
|
||||
*
|
||||
* @return string The remaining query string.
|
||||
*/
|
||||
public function setNearPointFromQuery($sQuery)
|
||||
{
|
||||
$aResult = parseLatLon($sQuery);
|
||||
|
||||
if ($aResult !== false
|
||||
&& $aResult[1] <= 90.1
|
||||
&& $aResult[1] >= -90.1
|
||||
&& $aResult[2] <= 180.1
|
||||
&& $aResult[2] >= -180.1
|
||||
) {
|
||||
$this->setNearPoint($aResult[1], $aResult[2]);
|
||||
$sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
|
||||
}
|
||||
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped for computing the distance from the reference point.
|
||||
*
|
||||
* @param string $sObj SQL variable name to compute the distance from.
|
||||
*
|
||||
* @return string An SQL string.
|
||||
*/
|
||||
public function distanceSQL($sObj)
|
||||
{
|
||||
return 'ST_Distance('.$this->sqlNear.", $sObj)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped for checking if something is within range of the
|
||||
* reference point.
|
||||
*
|
||||
* @param string $sObj SQL variable name to compute if it is within range.
|
||||
*
|
||||
* @return string An SQL string.
|
||||
*/
|
||||
public function withinSQL($sObj)
|
||||
{
|
||||
return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped of the importance factor of the viewbox.
|
||||
*
|
||||
* The importance factor is computed by checking if an object is within
|
||||
* the viewbox and/or the extended version of the viewbox.
|
||||
*
|
||||
* @param string $sObj SQL variable name of object to weight the importance
|
||||
*
|
||||
* @return string SQL snipped of the factor with a leading multiply sign.
|
||||
*/
|
||||
public function viewboxImportanceSQL($sObj)
|
||||
{
|
||||
$sSQL = '';
|
||||
|
||||
if ($this->sqlViewboxSmall) {
|
||||
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END";
|
||||
}
|
||||
if ($this->sqlViewboxLarge) {
|
||||
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END";
|
||||
}
|
||||
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL snipped checking if a place ID should be excluded.
|
||||
*
|
||||
* @param string $sVariable SQL variable name of place ID to check,
|
||||
* potentially prefixed with more SQL.
|
||||
*
|
||||
* @return string SQL snippet.
|
||||
*/
|
||||
public function excludeSQL($sVariable)
|
||||
{
|
||||
if ($this->sqlExcludeList) {
|
||||
return $sVariable.$this->sqlExcludeList;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'Near radius' => $this->fNearRadius,
|
||||
'Near point (SQL)' => $this->sqlNear,
|
||||
'Bounded viewbox' => $this->bViewboxBounded,
|
||||
'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
|
||||
'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
|
||||
'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
|
||||
'Countries (SQL)' => $this->sqlCountryList,
|
||||
'Excluded IDs (SQL)' => $this->sqlExcludeList
|
||||
);
|
||||
}
|
||||
}
|
||||
1052
lib/SearchDescription.php
Normal file
1052
lib/SearchDescription.php
Normal file
File diff suppressed because it is too large
Load Diff
44
lib/SpecialSearchOperator.php
Normal file
44
lib/SpecialSearchOperator.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
/**
|
||||
* Operators describing special searches.
|
||||
*/
|
||||
abstract class Operator
|
||||
{
|
||||
/// No operator selected.
|
||||
const NONE = 0;
|
||||
/// Search for POI of the given type.
|
||||
const TYPE = 1;
|
||||
/// Search for POIs near the given place.
|
||||
const NEAR = 2;
|
||||
/// Search for POIS in the given place.
|
||||
const IN = 3;
|
||||
/// Search for POIS named as given.
|
||||
const NAME = 4;
|
||||
/// Search for postcodes.
|
||||
const POSTCODE = 5;
|
||||
|
||||
private static $aConstantNames = null;
|
||||
|
||||
|
||||
public static function toString($iOperator)
|
||||
{
|
||||
if ($iOperator == Operator::NONE) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (Operator::$aConstantNames === null) {
|
||||
$oReflector = new \ReflectionClass('Nominatim\Operator');
|
||||
$aConstants = $oReflector->getConstants();
|
||||
|
||||
Operator::$aConstantNames = array();
|
||||
foreach ($aConstants as $sName => $iValue) {
|
||||
Operator::$aConstantNames[$iValue] = $sName;
|
||||
}
|
||||
}
|
||||
|
||||
return Operator::$aConstantNames[$iOperator];
|
||||
}
|
||||
}
|
||||
59
lib/Status.php
Normal file
59
lib/Status.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Status
|
||||
{
|
||||
protected $oDB;
|
||||
|
||||
public function __construct(&$oDB)
|
||||
{
|
||||
$this->oDB =& $oDB;
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
if (!$this->oDB) {
|
||||
throw new Exception('No database', 700);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->oDB->connect();
|
||||
} catch (\Nominatim\DatabaseError $e) {
|
||||
throw new Exception('Database connection failed', 700);
|
||||
}
|
||||
|
||||
$sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')");
|
||||
if ($sStandardWord === false) {
|
||||
throw new Exception('Module failed', 701);
|
||||
}
|
||||
|
||||
if ($sStandardWord != 'a') {
|
||||
throw new Exception('Module call failed', 702);
|
||||
}
|
||||
|
||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, ';
|
||||
$sSQL .= "operator, search_name_count FROM word WHERE word_token IN (' a')";
|
||||
$iWordID = $this->oDB->getOne($sSQL);
|
||||
if ($iWordID === false) {
|
||||
throw new Exception('Query failed', 703);
|
||||
}
|
||||
if (!$iWordID) {
|
||||
throw new Exception('No value', 704);
|
||||
}
|
||||
}
|
||||
|
||||
public function dataDate()
|
||||
{
|
||||
$sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1';
|
||||
$iDataDateEpoch = $this->oDB->getOne($sSQL);
|
||||
|
||||
if ($iDataDateEpoch === false) {
|
||||
throw Exception('Data date query failed '.$iDataDateEpoch->getMessage(), 705);
|
||||
}
|
||||
|
||||
return $iDataDateEpoch;
|
||||
}
|
||||
}
|
||||
29
lib/TokenCountry.php
Normal file
29
lib/TokenCountry.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Token;
|
||||
|
||||
/**
|
||||
* A country token.
|
||||
*/
|
||||
class Country
|
||||
{
|
||||
/// Database word id, if available.
|
||||
public $iId;
|
||||
/// Two-letter country code (lower-cased).
|
||||
public $sCountryCode;
|
||||
|
||||
public function __construct($iId, $sCountryCode)
|
||||
{
|
||||
$this->iId = $iId;
|
||||
$this->sCountryCode = $sCountryCode;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'ID' => $this->iId,
|
||||
'Type' => 'country',
|
||||
'Info' => $this->sCountryCode
|
||||
);
|
||||
}
|
||||
}
|
||||
29
lib/TokenHousenumber.php
Normal file
29
lib/TokenHousenumber.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Token;
|
||||
|
||||
/**
|
||||
* A house number token.
|
||||
*/
|
||||
class HouseNumber
|
||||
{
|
||||
/// Database word id, if available.
|
||||
public $iId;
|
||||
/// Normalized house number.
|
||||
public $sToken;
|
||||
|
||||
public function __construct($iId, $sToken)
|
||||
{
|
||||
$this->iId = $iId;
|
||||
$this->sToken = $sToken;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'ID' => $this->iId,
|
||||
'Type' => 'house number',
|
||||
'Info' => array('nr' => $this->sToken)
|
||||
);
|
||||
}
|
||||
}
|
||||
200
lib/TokenList.php
Normal file
200
lib/TokenList.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/TokenCountry.php');
|
||||
require_once(CONST_BasePath.'/lib/TokenHousenumber.php');
|
||||
require_once(CONST_BasePath.'/lib/TokenPostcode.php');
|
||||
require_once(CONST_BasePath.'/lib/TokenSpecialTerm.php');
|
||||
require_once(CONST_BasePath.'/lib/TokenWord.php');
|
||||
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
||||
|
||||
/**
|
||||
* Saves information about the tokens that appear in a search query.
|
||||
*
|
||||
* Tokens are sorted by their normalized form, the token word. There are different
|
||||
* kinds of tokens, represented by different Token* classes. Note that
|
||||
* tokens do not have a common base class. All tokens need to have a field
|
||||
* with the word id that points to an entry in the `word` database table
|
||||
* but otherwise the information saved about a token can be very different.
|
||||
*
|
||||
* There are two different kinds of token words: full words and partial terms.
|
||||
*
|
||||
* Full words start with a space. They represent a complete name of a place.
|
||||
* All special tokens are normally full words.
|
||||
*
|
||||
* Partial terms have no space at the beginning. They may represent a part of
|
||||
* a name of a place (e.g. in the name 'World Trade Center' a partial term
|
||||
* would be 'Trade' or 'Trade Center'). They are only used in TokenWord.
|
||||
*/
|
||||
class TokenList
|
||||
{
|
||||
// List of list of tokens indexed by their word_token.
|
||||
private $aTokens = array();
|
||||
|
||||
|
||||
/**
|
||||
* Return total number of tokens.
|
||||
*
|
||||
* @return Integer
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->aTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are tokens for the given token word.
|
||||
*
|
||||
* @param string $sWord Token word to look for.
|
||||
*
|
||||
* @return bool True if there is one or more token for the token word.
|
||||
*/
|
||||
public function contains($sWord)
|
||||
{
|
||||
return isset($this->aTokens[$sWord]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are partial or full tokens for the given word.
|
||||
*
|
||||
* @param string $sWord Token word to look for.
|
||||
*
|
||||
* @return bool True if there is one or more token for the token word.
|
||||
*/
|
||||
public function containsAny($sWord)
|
||||
{
|
||||
return isset($this->aTokens[$sWord]) || isset($this->aTokens[' '.$sWord]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of tokens for the given token word.
|
||||
*
|
||||
* @param string $sWord Token word to look for.
|
||||
*
|
||||
* @return object[] Array of tokens for the given token word or an
|
||||
* empty array if no tokens could be found.
|
||||
*/
|
||||
public function get($sWord)
|
||||
{
|
||||
return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add token information from the word table in the database.
|
||||
*
|
||||
* @param object $oDB Nominatim::DB instance.
|
||||
* @param string[] $aTokens List of tokens to look up in the database.
|
||||
* @param string[] $aCountryCodes List of country restrictions.
|
||||
* @param string $sNormQuery Normalized query string.
|
||||
* @param object $oNormalizer Normalizer function to use on tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTokensFromDB(&$oDB, &$aTokens, &$aCountryCodes, $sNormQuery, $oNormalizer)
|
||||
{
|
||||
// Check which tokens we have, get the ID numbers
|
||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
|
||||
$sSQL .= ' operator, coalesce(search_name_count, 0) as count';
|
||||
$sSQL .= ' FROM word WHERE word_token in (';
|
||||
$sSQL .= join(',', $oDB->getDBQuotedList($aTokens)).')';
|
||||
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$aDBWords = $oDB->getAll($sSQL, null, 'Could not get word tokens.');
|
||||
|
||||
foreach ($aDBWords as $aWord) {
|
||||
$oToken = null;
|
||||
$iId = (int) $aWord['word_id'];
|
||||
|
||||
if ($aWord['class']) {
|
||||
// Special terms need to appear in their normalized form.
|
||||
if ($aWord['word']) {
|
||||
$sNormWord = $aWord['word'];
|
||||
if ($oNormalizer != null) {
|
||||
$sNormWord = $oNormalizer->transliterate($aWord['word']);
|
||||
}
|
||||
if (strpos($sNormQuery, $sNormWord) === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($aWord['class'] == 'place' && $aWord['type'] == 'house') {
|
||||
$oToken = new Token\HouseNumber($iId, trim($aWord['word_token']));
|
||||
} elseif ($aWord['class'] == 'place' && $aWord['type'] == 'postcode') {
|
||||
if ($aWord['word']
|
||||
&& pg_escape_string($aWord['word']) == $aWord['word']
|
||||
) {
|
||||
$oToken = new Token\Postcode(
|
||||
$iId,
|
||||
$aWord['word'],
|
||||
$aWord['country_code']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// near and in operator the same at the moment
|
||||
$oToken = new Token\SpecialTerm(
|
||||
$iId,
|
||||
$aWord['class'],
|
||||
$aWord['type'],
|
||||
$aWord['operator'] ? Operator::NEAR : Operator::NONE
|
||||
);
|
||||
}
|
||||
} elseif ($aWord['country_code']) {
|
||||
// Filter country tokens that do not match restricted countries.
|
||||
if (!$aCountryCodes
|
||||
|| in_array($aWord['country_code'], $aCountryCodes)
|
||||
) {
|
||||
$oToken = new Token\Country($iId, $aWord['country_code']);
|
||||
}
|
||||
} else {
|
||||
$oToken = new Token\Word(
|
||||
$iId,
|
||||
$aWord['word_token'][0] != ' ',
|
||||
(int) $aWord['count']
|
||||
);
|
||||
}
|
||||
|
||||
if ($oToken) {
|
||||
$this->addToken($aWord['word_token'], $oToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new token for the given word.
|
||||
*
|
||||
* @param string $sWord Word the token describes.
|
||||
* @param object $oToken Token object to add.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addToken($sWord, $oToken)
|
||||
{
|
||||
if (isset($this->aTokens[$sWord])) {
|
||||
$this->aTokens[$sWord][] = $oToken;
|
||||
} else {
|
||||
$this->aTokens[$sWord] = array($oToken);
|
||||
}
|
||||
}
|
||||
|
||||
public function debugTokenByWordIdList()
|
||||
{
|
||||
$aWordsIDs = array();
|
||||
foreach ($this->aTokens as $sToken => $aWords) {
|
||||
foreach ($aWords as $aToken) {
|
||||
if ($aToken->iId !== null) {
|
||||
$aWordsIDs[$aToken->iId] =
|
||||
'#'.$sToken.'('.$aToken->iId.')#';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aWordsIDs;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return $this->aTokens;
|
||||
}
|
||||
}
|
||||
32
lib/TokenPostcode.php
Normal file
32
lib/TokenPostcode.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Token;
|
||||
|
||||
/**
|
||||
* A postcode token.
|
||||
*/
|
||||
class Postcode
|
||||
{
|
||||
/// Database word id, if available.
|
||||
public $iId;
|
||||
/// Full nomralized postcode (upper cased).
|
||||
public $sPostcode;
|
||||
// Optional country code the postcode belongs to (currently unused).
|
||||
public $sCountryCode;
|
||||
|
||||
public function __construct($iId, $sPostcode, $sCountryCode = '')
|
||||
{
|
||||
$this->iId = $iId;
|
||||
$this->sPostcode = $sPostcode;
|
||||
$this->sCountryCode = empty($sCountryCode) ? '' : $sCountryCode;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'ID' => $this->iId,
|
||||
'Type' => 'postcode',
|
||||
'Info' => $this->sPostcode.'('.$this->sCountryCode.')'
|
||||
);
|
||||
}
|
||||
}
|
||||
41
lib/TokenSpecialTerm.php
Normal file
41
lib/TokenSpecialTerm.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Token;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
||||
|
||||
/**
|
||||
* A word token describing a place type.
|
||||
*/
|
||||
class SpecialTerm
|
||||
{
|
||||
/// Database word id, if applicable.
|
||||
public $iId;
|
||||
/// Class (or OSM tag key) of the place to look for.
|
||||
public $sClass;
|
||||
/// Type (or OSM tag value) of the place to look for.
|
||||
public $sType;
|
||||
/// Relationship of the operator to the object (see Operator class).
|
||||
public $iOperator;
|
||||
|
||||
public function __construct($iID, $sClass, $sType, $iOperator)
|
||||
{
|
||||
$this->iId = $iID;
|
||||
$this->sClass = $sClass;
|
||||
$this->sType = $sType;
|
||||
$this->iOperator = $iOperator;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'ID' => $this->iId,
|
||||
'Type' => 'special term',
|
||||
'Info' => array(
|
||||
'class' => $this->sClass,
|
||||
'type' => $this->sType,
|
||||
'operator' => \Nominatim\Operator::toString($this->iOperator)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
35
lib/TokenWord.php
Normal file
35
lib/TokenWord.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Token;
|
||||
|
||||
/**
|
||||
* A standard word token.
|
||||
*/
|
||||
class Word
|
||||
{
|
||||
/// Database word id, if applicable.
|
||||
public $iId;
|
||||
/// If true, the word may represent only part of a place name.
|
||||
public $bPartial;
|
||||
/// Number of appearances in the database.
|
||||
public $iSearchNameCount;
|
||||
|
||||
public function __construct($iId, $bPartial, $iSearchNameCount)
|
||||
{
|
||||
$this->iId = $iId;
|
||||
$this->bPartial = $bPartial;
|
||||
$this->iSearchNameCount = $iSearchNameCount;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
{
|
||||
return array(
|
||||
'ID' => $this->iId,
|
||||
'Type' => 'word',
|
||||
'Info' => array(
|
||||
'partial' => $this->bPartial,
|
||||
'count' => $this->iSearchNameCount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
340
lib/cmd.php
340
lib/cmd.php
@@ -1,145 +1,213 @@
|
||||
<?php
|
||||
|
||||
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
|
||||
{
|
||||
$aQuick = array();
|
||||
$aCounts = array();
|
||||
|
||||
foreach($aSpec as $aLine)
|
||||
{
|
||||
if (is_array($aLine))
|
||||
{
|
||||
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
|
||||
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
|
||||
$aCounts[$aLine[0]] = 0;
|
||||
}
|
||||
}
|
||||
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
|
||||
{
|
||||
$aQuick = array();
|
||||
$aCounts = array();
|
||||
|
||||
$aResult = array();
|
||||
$bUnknown = false;
|
||||
$iSize = sizeof($aArg);
|
||||
for ($i = 1; $i < $iSize; $i++)
|
||||
{
|
||||
if (isset($aQuick[$aArg[$i]]))
|
||||
{
|
||||
$aLine = $aQuick[$aArg[$i]];
|
||||
$aCounts[$aLine[0]]++;
|
||||
$xVal = null;
|
||||
if ($aLine[4] == $aLine[5])
|
||||
{
|
||||
if ($aLine[4])
|
||||
{
|
||||
$xVal = array();
|
||||
for($n = $aLine[4]; $i < $iSize && $n; $n--)
|
||||
{
|
||||
$i++;
|
||||
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
|
||||
foreach ($aSpec as $aLine) {
|
||||
if (is_array($aLine)) {
|
||||
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
|
||||
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
|
||||
$aCounts[$aLine[0]] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($aLine[6])
|
||||
{
|
||||
case 'realpath':
|
||||
$xVal[] = realpath($aArg[$i]);
|
||||
break;
|
||||
case 'realdir':
|
||||
$sPath = realpath(dirname($aArg[$i]));
|
||||
if ($sPath)
|
||||
$xVal[] = $sPath . '/' . basename($aArg[$i]);
|
||||
else
|
||||
$xVal[] = $sPath;
|
||||
break;
|
||||
case 'bool':
|
||||
$xVal[] = (bool)$aArg[$i];
|
||||
break;
|
||||
case 'int':
|
||||
$xVal[] = (int)$aArg[$i];
|
||||
break;
|
||||
case 'float':
|
||||
$xVal[] = (float)$aArg[$i];
|
||||
break;
|
||||
default:
|
||||
$xVal[] = $aArg[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($aLine[4] == 1) $xVal = $xVal[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$xVal = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fail('Variable numbers of params not yet supported');
|
||||
}
|
||||
$aResult = array();
|
||||
$bUnknown = false;
|
||||
$iSize = count($aArg);
|
||||
for ($i = 1; $i < $iSize; $i++) {
|
||||
if (isset($aQuick[$aArg[$i]])) {
|
||||
$aLine = $aQuick[$aArg[$i]];
|
||||
$aCounts[$aLine[0]]++;
|
||||
$xVal = null;
|
||||
if ($aLine[4] == $aLine[5]) {
|
||||
if ($aLine[4]) {
|
||||
$xVal = array();
|
||||
for ($n = $aLine[4]; $i < $iSize && $n; $n--) {
|
||||
$i++;
|
||||
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
|
||||
|
||||
if ($aLine[3] > 1)
|
||||
{
|
||||
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
|
||||
$aResult[$aLine[0]][] = $xVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[$aLine[0]] = $xVal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$bUnknown = $aArg[$i];
|
||||
}
|
||||
}
|
||||
switch ($aLine[6]) {
|
||||
case 'realpath':
|
||||
$xVal[] = realpath($aArg[$i]);
|
||||
break;
|
||||
case 'realdir':
|
||||
$sPath = realpath(dirname($aArg[$i]));
|
||||
if ($sPath) {
|
||||
$xVal[] = $sPath . '/' . basename($aArg[$i]);
|
||||
} else {
|
||||
$xVal[] = $sPath;
|
||||
}
|
||||
break;
|
||||
case 'bool':
|
||||
$xVal[] = (bool)$aArg[$i];
|
||||
break;
|
||||
case 'int':
|
||||
$xVal[] = (int)$aArg[$i];
|
||||
break;
|
||||
case 'float':
|
||||
$xVal[] = (float)$aArg[$i];
|
||||
break;
|
||||
default:
|
||||
$xVal[] = $aArg[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($aLine[4] == 1) $xVal = $xVal[0];
|
||||
} else {
|
||||
$xVal = true;
|
||||
}
|
||||
} else {
|
||||
fail('Variable numbers of params not yet supported');
|
||||
}
|
||||
|
||||
if (array_key_exists('help', $aResult)) showUsage($aSpec);
|
||||
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
|
||||
if ($aLine[3] > 1) {
|
||||
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
|
||||
$aResult[$aLine[0]][] = $xVal;
|
||||
} else {
|
||||
$aResult[$aLine[0]] = $xVal;
|
||||
}
|
||||
} else {
|
||||
$bUnknown = $aArg[$i];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($aSpec as $aLine)
|
||||
{
|
||||
if (is_array($aLine))
|
||||
{
|
||||
if ($aCounts[$aLine[0]] < $aLine[2]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is missing');
|
||||
if ($aCounts[$aLine[0]] > $aLine[3]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is pressent too many times');
|
||||
switch ($aLine[6])
|
||||
{
|
||||
case 'bool':
|
||||
if (!array_key_exists($aLine[0], $aResult))
|
||||
$aResult[$aLine[0]] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bUnknown;
|
||||
}
|
||||
if (array_key_exists('help', $aResult)) showUsage($aSpec);
|
||||
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
|
||||
|
||||
function showUsage($aSpec, $bExit = false, $sError = false)
|
||||
{
|
||||
if ($sError)
|
||||
{
|
||||
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
|
||||
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
|
||||
exit;
|
||||
}
|
||||
echo "Usage: ".basename($_SERVER['argv'][0])."\n";
|
||||
$bFirst = true;
|
||||
foreach($aSpec as $aLine)
|
||||
{
|
||||
if (is_array($aLine))
|
||||
{
|
||||
if ($bFirst)
|
||||
{
|
||||
$bFirst = false;
|
||||
echo "\n";
|
||||
}
|
||||
$aNames = array();
|
||||
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
|
||||
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
|
||||
$sName = join(', ',$aNames);
|
||||
echo ' '.$sName.str_repeat(' ',30-strlen($sName)).$aLine[7]."\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo $aLine."\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
exit;
|
||||
}
|
||||
foreach ($aSpec as $aLine) {
|
||||
if (is_array($aLine)) {
|
||||
if ($aCounts[$aLine[0]] < $aLine[2]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is missing');
|
||||
if ($aCounts[$aLine[0]] > $aLine[3]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is pressent too many times');
|
||||
switch ($aLine[6]) {
|
||||
case 'bool':
|
||||
if (!array_key_exists($aLine[0], $aResult))
|
||||
$aResult[$aLine[0]] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bUnknown;
|
||||
}
|
||||
|
||||
function showUsage($aSpec, $bExit = false, $sError = false)
|
||||
{
|
||||
if ($sError) {
|
||||
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
|
||||
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
|
||||
exit;
|
||||
}
|
||||
echo 'Usage: '.basename($_SERVER['argv'][0])."\n";
|
||||
$bFirst = true;
|
||||
foreach ($aSpec as $aLine) {
|
||||
if (is_array($aLine)) {
|
||||
if ($bFirst) {
|
||||
$bFirst = false;
|
||||
echo "\n";
|
||||
}
|
||||
$aNames = array();
|
||||
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
|
||||
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
|
||||
$sName = join(', ', $aNames);
|
||||
echo ' '.$sName.str_repeat(' ', 30-strlen($sName)).$aLine[7]."\n";
|
||||
} else {
|
||||
echo $aLine."\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
function info($sMsg)
|
||||
{
|
||||
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
||||
}
|
||||
|
||||
$aWarnings = array();
|
||||
|
||||
|
||||
function warn($sMsg)
|
||||
{
|
||||
$GLOBALS['aWarnings'][] = $sMsg;
|
||||
echo date('Y-m-d H:i:s == ').'WARNING: '.$sMsg."\n";
|
||||
}
|
||||
|
||||
|
||||
function repeatWarnings()
|
||||
{
|
||||
foreach ($GLOBALS['aWarnings'] as $sMsg) {
|
||||
echo ' * ',$sMsg."\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreErrors = false)
|
||||
{
|
||||
// Convert database DSN to psql parameters
|
||||
$aDSNInfo = \Nominatim\DB::parseDSN(CONST_Database_DSN);
|
||||
if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
|
||||
$sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
|
||||
if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
|
||||
$sCMD .= ' -h ' . $aDSNInfo['hostspec'];
|
||||
}
|
||||
if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
|
||||
$sCMD .= ' -U ' . $aDSNInfo['username'];
|
||||
}
|
||||
$aProcEnv = null;
|
||||
if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
|
||||
$aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
|
||||
}
|
||||
if (!$bVerbose) {
|
||||
$sCMD .= ' -q';
|
||||
}
|
||||
if ($bfatal && !$bIgnoreErrors) {
|
||||
$sCMD .= ' -v ON_ERROR_STOP=1';
|
||||
}
|
||||
$aDescriptors = array(
|
||||
0 => array('pipe', 'r'),
|
||||
1 => STDOUT,
|
||||
2 => STDERR
|
||||
);
|
||||
$ahPipes = null;
|
||||
$hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
||||
if (!is_resource($hProcess)) {
|
||||
fail('unable to start pgsql');
|
||||
}
|
||||
|
||||
if (!$bVerbose) {
|
||||
fwrite($ahPipes[0], 'set client_min_messages to WARNING;');
|
||||
}
|
||||
|
||||
while (strlen($sScript)) {
|
||||
$iWritten = fwrite($ahPipes[0], $sScript);
|
||||
if ($iWritten <= 0) break;
|
||||
$sScript = substr($sScript, $iWritten);
|
||||
}
|
||||
fclose($ahPipes[0]);
|
||||
$iReturn = proc_close($hProcess);
|
||||
if ($bfatal && $iReturn > 0) {
|
||||
fail("pgsql returned with error code ($iReturn)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function runWithEnv($sCmd, $aEnv)
|
||||
{
|
||||
$aFDs = array(
|
||||
0 => array('pipe', 'r'),
|
||||
1 => STDOUT,
|
||||
2 => STDERR
|
||||
);
|
||||
$aPipes = null;
|
||||
$hProc = @proc_open($sCmd, $aFDs, $aPipes, null, $aEnv);
|
||||
if (!is_resource($hProc)) {
|
||||
fail('unable to run command:' . $sCmd);
|
||||
}
|
||||
|
||||
fclose($aPipes[0]); // no stdin
|
||||
|
||||
$iStat = proc_close($hProc);
|
||||
return $iStat;
|
||||
}
|
||||
|
||||
26
lib/db.php
26
lib/db.php
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
require_once('DB.php');
|
||||
|
||||
function &getDB($bNew = false, $bPersistent = false)
|
||||
{
|
||||
// Get the database object
|
||||
$oDB =& DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent);
|
||||
if (PEAR::IsError($oDB))
|
||||
{
|
||||
var_dump(CONST_Database_DSN);
|
||||
var_Dump($oDB);
|
||||
fail($oDB->getMessage());
|
||||
}
|
||||
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
|
||||
$oDB->query("SET DateStyle TO 'sql,european'");
|
||||
$oDB->query("SET client_encoding TO 'utf-8'");
|
||||
$iMaxExecution = ini_get('max_execution_time') * 1000;
|
||||
if ($iMaxExecution > 0) $oDB->query("SET statement_timeout TO $iMaxExecution");
|
||||
return $oDB;
|
||||
}
|
||||
|
||||
function getDBQuoted($s)
|
||||
{
|
||||
return "'".pg_escape_string($s)."'";
|
||||
}
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
<?php
|
||||
if (file_exists(getenv('NOMINATIM_SETTINGS')))
|
||||
{
|
||||
require_once(getenv('NOMINATIM_SETTINGS'));
|
||||
}
|
||||
|
||||
require_once('init.php');
|
||||
require_once('cmd.php');
|
||||
require_once('init.php');
|
||||
require_once('cmd.php');
|
||||
require_once('DebugNone.php');
|
||||
|
||||
// handle http proxy when using file_get_contents
|
||||
if (CONST_HTTP_Proxy) {
|
||||
$proxy = 'tcp://' . CONST_HTTP_Proxy_Host . ':' . CONST_HTTP_Proxy_Port;
|
||||
$aHeaders = array();
|
||||
if(CONST_HTTP_Proxy_Login != null && CONST_HTTP_Proxy_Login != '' && CONST_HTTP_Proxy_Password != null && CONST_HTTP_Proxy_Password != '') {
|
||||
$auth = base64_encode(CONST_HTTP_Proxy_Login . ':' . CONST_HTTP_Proxy_Password);
|
||||
$aHeaders = array("Proxy-Authorization: Basic $auth");
|
||||
}
|
||||
$aContext = array(
|
||||
'http' => array(
|
||||
'proxy' => $proxy,
|
||||
'request_fulluri' => true,
|
||||
'header' => $aHeaders
|
||||
),
|
||||
'https' => array(
|
||||
'proxy' => $proxy,
|
||||
'request_fulluri' => true,
|
||||
'header' => $aHeaders
|
||||
)
|
||||
);
|
||||
stream_context_set_default($aContext);
|
||||
}
|
||||
// handle http proxy when using file_get_contents
|
||||
if (CONST_HTTP_Proxy) {
|
||||
$proxy = 'tcp://' . CONST_HTTP_Proxy_Host . ':' . CONST_HTTP_Proxy_Port;
|
||||
$aHeaders = array();
|
||||
if (CONST_HTTP_Proxy_Login != null && CONST_HTTP_Proxy_Login != '' && CONST_HTTP_Proxy_Password != null && CONST_HTTP_Proxy_Password != '') {
|
||||
$auth = base64_encode(CONST_HTTP_Proxy_Login . ':' . CONST_HTTP_Proxy_Password);
|
||||
$aHeaders = array("Proxy-Authorization: Basic $auth");
|
||||
}
|
||||
$aContext = array(
|
||||
'http' => array(
|
||||
'proxy' => $proxy,
|
||||
'request_fulluri' => true,
|
||||
'header' => $aHeaders
|
||||
),
|
||||
'https' => array(
|
||||
'proxy' => $proxy,
|
||||
'request_fulluri' => true,
|
||||
'header' => $aHeaders
|
||||
)
|
||||
);
|
||||
stream_context_set_default($aContext);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,105 @@
|
||||
<?php
|
||||
require_once('init.php');
|
||||
|
||||
if (CONST_NoAccessControl)
|
||||
{
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: OPTIONS,GET");
|
||||
if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
|
||||
{
|
||||
header("Access-Control-Allow-Headers: ".$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
}
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
||||
require_once('init.php');
|
||||
require_once('ParameterParser.php');
|
||||
require_once(CONST_Debug ? 'DebugHtml.php' : 'DebugNone.php');
|
||||
|
||||
if (CONST_ClosedForIndexing && strpos(CONST_ClosedForIndexingExceptionIPs, ','.$_SERVER["REMOTE_ADDR"].',') === false)
|
||||
{
|
||||
echo "Closed for re-indexing...";
|
||||
exit;
|
||||
}
|
||||
/***************************************************************************
|
||||
*
|
||||
* Error handling functions
|
||||
*
|
||||
*/
|
||||
|
||||
$aBucketKeys = array();
|
||||
function userError($sMsg)
|
||||
{
|
||||
throw new Exception($sMsg, 400);
|
||||
}
|
||||
|
||||
if (isset($_SERVER["HTTP_REFERER"])) $aBucketKeys[] = str_replace('www.','',strtolower(parse_url($_SERVER["HTTP_REFERER"], PHP_URL_HOST)));
|
||||
if (isset($_SERVER["REMOTE_ADDR"])) $aBucketKeys[] = $_SERVER["REMOTE_ADDR"];
|
||||
if (isset($_GET["email"])) $aBucketKeys[] = $_GET["email"];
|
||||
|
||||
$fBucketVal = doBucket($aBucketKeys,
|
||||
(defined('CONST_ConnectionBucket_PageType')?constant('CONST_ConnectionBucket_Cost_'.CONST_ConnectionBucket_PageType):1) + user_busy_cost(),
|
||||
CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||
function exception_handler_html($exception)
|
||||
{
|
||||
http_response_code($exception->getCode());
|
||||
header('Content-type: text/html; charset=UTF-8');
|
||||
include(CONST_BasePath.'/lib/template/error-html.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($fBucketVal > CONST_ConnectionBucket_WaitLimit && $fBucketVal < CONST_ConnectionBucket_BlockLimit)
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
$iCurrentSleeping = $m->increment('sleepCounter');
|
||||
if (false === $iCurrentSleeping)
|
||||
{
|
||||
$m->add('sleepCounter', 0);
|
||||
$iCurrentSleeping = $m->increment('sleepCounter');
|
||||
}
|
||||
if ($iCurrentSleeping >= CONST_ConnectionBucket_MaxSleeping || isBucketSleeping($aBucketKeys))
|
||||
{
|
||||
// Too many threads sleeping already. This becomes a hard block.
|
||||
$fBucketVal = doBucket($aBucketKeys, CONST_ConnectionBucket_BlockLimit, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBucketSleeping($aBucketKeys, true);
|
||||
sleep(($fBucketVal - CONST_ConnectionBucket_WaitLimit)/CONST_ConnectionBucket_LeakRate);
|
||||
$fBucketVal = doBucket($aBucketKeys, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||
setBucketSleeping($aBucketKeys, false);
|
||||
}
|
||||
$m->decrement('sleepCounter');
|
||||
}
|
||||
function exception_handler_json($exception)
|
||||
{
|
||||
http_response_code($exception->getCode());
|
||||
header('Content-type: application/json; charset=utf-8');
|
||||
include(CONST_BasePath.'/lib/template/error-json.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if (strpos(CONST_BlockedIPs, ','.$_SERVER["REMOTE_ADDR"].',') !== false || $fBucketVal >= CONST_ConnectionBucket_BlockLimit)
|
||||
{
|
||||
header("HTTP/1.0 429 Too Many Requests");
|
||||
echo "Your IP has been blocked. \n";
|
||||
echo CONST_BlockMessage;
|
||||
exit;
|
||||
}
|
||||
function exception_handler_xml($exception)
|
||||
{
|
||||
http_response_code($exception->getCode());
|
||||
header('Content-type: text/xml; charset=utf-8');
|
||||
echo '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
|
||||
include(CONST_BasePath.'/lib/template/error-xml.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
header('Content-type: text/html; charset=utf-8');
|
||||
function shutdown_exception_handler_html()
|
||||
{
|
||||
$error = error_get_last();
|
||||
if ($error !== null && $error['type'] === E_ERROR) {
|
||||
exception_handler_html(new Exception($error['message'], 500));
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown_exception_handler_xml()
|
||||
{
|
||||
$error = error_get_last();
|
||||
if ($error !== null && $error['type'] === E_ERROR) {
|
||||
exception_handler_xml(new Exception($error['message'], 500));
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown_exception_handler_json()
|
||||
{
|
||||
$error = error_get_last();
|
||||
if ($error !== null && $error['type'] === E_ERROR) {
|
||||
exception_handler_json(new Exception($error['message'], 500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function set_exception_handler_by_format($sFormat = null)
|
||||
{
|
||||
// Multiple calls to register_shutdown_function will cause multiple callbacks
|
||||
// to be executed, we only want the last executed. Thus we don't want to register
|
||||
// one by default without an explicit $sFormat set.
|
||||
|
||||
if (!isset($sFormat)) {
|
||||
set_exception_handler('exception_handler_html');
|
||||
} elseif ($sFormat == 'html') {
|
||||
set_exception_handler('exception_handler_html');
|
||||
register_shutdown_function('shutdown_exception_handler_html');
|
||||
} elseif ($sFormat == 'xml') {
|
||||
set_exception_handler('exception_handler_xml');
|
||||
register_shutdown_function('shutdown_exception_handler_xml');
|
||||
} else {
|
||||
set_exception_handler('exception_handler_json');
|
||||
register_shutdown_function('shutdown_exception_handler_json');
|
||||
}
|
||||
}
|
||||
// set a default
|
||||
set_exception_handler_by_format();
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* HTTP Reply header setup
|
||||
*/
|
||||
|
||||
if (CONST_NoAccessControl) {
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: OPTIONS,GET');
|
||||
if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||
}
|
||||
}
|
||||
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
||||
|
||||
if (CONST_Debug) header('Content-type: text/html; charset=utf-8');
|
||||
|
||||
17
lib/init.php
17
lib/init.php
@@ -1,14 +1,9 @@
|
||||
<?php
|
||||
|
||||
@define('CONST_BasePath', dirname(dirname(__FILE__)));
|
||||
require_once(CONST_BasePath.'/lib/lib.php');
|
||||
require_once(CONST_BasePath.'/lib/DB.php');
|
||||
|
||||
require_once(CONST_BasePath.'/settings/settings.php');
|
||||
require_once(CONST_BasePath.'/lib/lib.php');
|
||||
require_once(CONST_BasePath.'/lib/leakybucket.php');
|
||||
require_once(CONST_BasePath.'/lib/db.php');
|
||||
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
echo "Please disable magic quotes in your php.ini configuration\n";
|
||||
exit;
|
||||
}
|
||||
if (get_magic_quotes_gpc()) {
|
||||
echo "Please disable magic quotes in your php.ini configuration\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
<?php
|
||||
|
||||
function getBucketMemcache()
|
||||
{
|
||||
static $m;
|
||||
|
||||
if (!CONST_ConnectionBucket_MemcacheServerAddress) return null;
|
||||
if (!isset($m))
|
||||
{
|
||||
$m = new Memcached();
|
||||
$m->addServer(CONST_ConnectionBucket_MemcacheServerAddress, CONST_ConnectionBucket_MemcacheServerPort);
|
||||
}
|
||||
return $m;
|
||||
}
|
||||
|
||||
function doBucket($asKey, $iRequestCost, $iLeakPerSecond, $iThreshold)
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
if (!$m) return 0;
|
||||
|
||||
$iMaxVal = 0;
|
||||
$t = time();
|
||||
|
||||
foreach($asKey as $sKey)
|
||||
{
|
||||
$aCurrentBlock = $m->get($sKey);
|
||||
if (!$aCurrentBlock)
|
||||
{
|
||||
$aCurrentBlock = array($iRequestCost, $t, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add RequestCost
|
||||
// remove leak * the time since the last request
|
||||
$aCurrentBlock[0] += $iRequestCost - ($t - $aCurrentBlock[1])*$iLeakPerSecond;
|
||||
$aCurrentBlock[1] = $t;
|
||||
}
|
||||
|
||||
if ($aCurrentBlock[0] <= 0)
|
||||
{
|
||||
$m->delete($sKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have hit the threshold stop and record this to the block list
|
||||
if ($aCurrentBlock[0] >= $iThreshold)
|
||||
{
|
||||
$aCurrentBlock[0] = $iThreshold;
|
||||
|
||||
// Make up to 10 attempts to record this to memcache (with locking to prevent conflicts)
|
||||
$i = 10;
|
||||
for($i = 0; $i < 10; $i++)
|
||||
{
|
||||
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||
if (!$aBlockedList)
|
||||
{
|
||||
$aBlockedList = array();
|
||||
$m->add('blockedList', $aBlockedList);
|
||||
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||
}
|
||||
if (!isset($aBlockedList[$sKey]))
|
||||
{
|
||||
$aBlockedList[$sKey] = array(1, $t);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aBlockedList[$sKey][0]++;
|
||||
$aBlockedList[$sKey][1] = $t;
|
||||
}
|
||||
if (sizeof($aBlockedList) > CONST_ConnectionBucket_MaxBlockList)
|
||||
{
|
||||
uasort($aBlockedList, 'byValue1');
|
||||
$aBlockedList = array_slice($aBlockedList, 0, CONST_ConnectionBucket_MaxBlockList);
|
||||
}
|
||||
$x = $m->cas($hCasToken, 'blockedList', $aBlockedList);
|
||||
if ($x) break;
|
||||
}
|
||||
}
|
||||
// Only keep in memcache until the time it would have expired (to avoid clutering memcache)
|
||||
$m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/$iLeakPerSecond);
|
||||
}
|
||||
|
||||
// Bucket result in the largest bucket we find
|
||||
$iMaxVal = max($iMaxVal, $aCurrentBlock[0]);
|
||||
}
|
||||
|
||||
return $iMaxVal;
|
||||
}
|
||||
|
||||
function isBucketSleeping($asKey)
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
if (!$m) return false;
|
||||
|
||||
foreach($asKey as $sKey)
|
||||
{
|
||||
$aCurrentBlock = $m->get($sKey);
|
||||
if ($aCurrentBlock[2]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setBucketSleeping($asKey, $bVal)
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
if (!$m) return false;
|
||||
|
||||
$iMaxVal = 0;
|
||||
$t = time();
|
||||
|
||||
foreach($asKey as $sKey)
|
||||
{
|
||||
$aCurrentBlock = $m->get($sKey);
|
||||
$aCurrentBlock[2] = $bVal;
|
||||
$m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/CONST_ConnectionBucket_LeakRate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function byValue1($a, $b)
|
||||
{
|
||||
if ($a[1] == $b[1])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ($a[1] > $b[1]) ? -1 : 1;
|
||||
}
|
||||
|
||||
function byLastBlockTime($a, $b)
|
||||
{
|
||||
if ($a['lastBlockTimestamp'] == $b['lastBlockTimestamp'])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ($a['lastBlockTimestamp'] > $b['lastBlockTimestamp']) ? -1 : 1;
|
||||
}
|
||||
|
||||
function getBucketBlocks()
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
if (!$m) return null;
|
||||
$t = time();
|
||||
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||
if (!$aBlockedList) $aBlockedList = array();
|
||||
foreach($aBlockedList as $sKey => $aDetails)
|
||||
{
|
||||
$aCurrentBlock = $m->get($sKey);
|
||||
if (!$aCurrentBlock) $aCurrentBlock = array(0, $t);
|
||||
$iCurrentBucketSize = max(0, $aCurrentBlock[0] - ($t - $aCurrentBlock[1])*CONST_ConnectionBucket_LeakRate);
|
||||
$aBlockedList[$sKey] = array(
|
||||
'totalBlocks' => $aDetails[0],
|
||||
'lastBlockTimestamp' => $aDetails[1],
|
||||
'isSleeping' => (isset($aCurrentBlock[2])?$aCurrentBlock[2]:false),
|
||||
'currentBucketSize' => $iCurrentBucketSize,
|
||||
'currentlyBlocked' => $iCurrentBucketSize + (CONST_ConnectionBucket_Cost_Reverse) >= CONST_ConnectionBucket_BlockLimit,
|
||||
);
|
||||
}
|
||||
uasort($aBlockedList, 'byLastBlockTime');
|
||||
return $aBlockedList;
|
||||
}
|
||||
|
||||
function clearBucketBlocks()
|
||||
{
|
||||
$m = getBucketMemcache();
|
||||
if (!$m) return false;
|
||||
$m->delete('blockedList');
|
||||
return true;
|
||||
}
|
||||
1272
lib/lib.php
1272
lib/lib.php
File diff suppressed because it is too large
Load Diff
150
lib/log.php
150
lib/log.php
@@ -1,90 +1,84 @@
|
||||
<?php
|
||||
|
||||
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
|
||||
{
|
||||
$aStartTime = explode('.',microtime(true));
|
||||
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
|
||||
|
||||
$sOutputFormat = '';
|
||||
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
|
||||
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
|
||||
{
|
||||
$fStartTime = microtime(true);
|
||||
$aStartTime = explode('.', $fStartTime);
|
||||
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
|
||||
|
||||
$hLog = array(
|
||||
date('Y-m-d H:i:s',$aStartTime[0]).'.'.$aStartTime[1],
|
||||
$_SERVER["REMOTE_ADDR"],
|
||||
$_SERVER['QUERY_STRING'],
|
||||
$sQuery
|
||||
);
|
||||
$sOutputFormat = '';
|
||||
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
|
||||
|
||||
if (CONST_Log_DB)
|
||||
{
|
||||
// Log
|
||||
if ($sType == 'search')
|
||||
{
|
||||
$oDB->query('insert into query_log values ('.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[3]).','.getDBQuoted($hLog[1]).')');
|
||||
}
|
||||
if ($sType == 'reverse') {
|
||||
$sOutQuery = (isset($_GET['lat'])?$_GET['lat']:'').'/';
|
||||
if (isset($_GET['lon'])) $sOutQuery .= $_GET['lon'];
|
||||
if (isset($_GET['zoom'])) $sOutQuery .= '/'.$_GET['zoom'];
|
||||
} else {
|
||||
$sOutQuery = $sQuery;
|
||||
}
|
||||
|
||||
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format)';
|
||||
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
|
||||
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($_SERVER['HTTP_USER_AGENT']).','.getDBQuoted(join(',',$aLanguageList)).','.getDBQuoted($sOutputFormat).')';
|
||||
$oDB->query($sSQL);
|
||||
}
|
||||
$hLog = array(
|
||||
date('Y-m-d H:i:s', $aStartTime[0]).'.'.$aStartTime[1],
|
||||
$_SERVER['REMOTE_ADDR'],
|
||||
$_SERVER['QUERY_STRING'],
|
||||
$sOutQuery,
|
||||
$sType,
|
||||
$fStartTime
|
||||
);
|
||||
|
||||
if (CONST_Log_File && CONST_Log_File_ReverseLog != '')
|
||||
{
|
||||
if ($sType == 'reverse')
|
||||
{
|
||||
$aStartTime = explode('.',$hLog[0]);
|
||||
file_put_contents(CONST_Log_File_ReverseLog,
|
||||
$aStartTime[0].','.$aStartTime[1].','.
|
||||
php_uname('n').','.
|
||||
'"'.addslashes(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'",'.
|
||||
'"'.addslashes($hLog[1]).'",'.
|
||||
$_GET['lat'].','.
|
||||
$_GET['lon'].','.
|
||||
$_GET['zoom'].','.
|
||||
'"'.addslashes($_SERVER['HTTP_USER_AGENT']).'",'.
|
||||
'"'.addslashes($sOutputFormat).'"'."\n",
|
||||
FILE_APPEND);
|
||||
}
|
||||
}
|
||||
if (CONST_Log_DB) {
|
||||
if (isset($_GET['email']))
|
||||
$sUserAgent = $_GET['email'];
|
||||
elseif (isset($_SERVER['HTTP_REFERER']))
|
||||
$sUserAgent = $_SERVER['HTTP_REFERER'];
|
||||
elseif (isset($_SERVER['HTTP_USER_AGENT']))
|
||||
$sUserAgent = $_SERVER['HTTP_USER_AGENT'];
|
||||
else $sUserAgent = '';
|
||||
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format,searchterm)';
|
||||
$sSQL .= ' values (';
|
||||
$sSQL .= join(',', $oDB->getDBQuotedList(array(
|
||||
$sType,
|
||||
$hLog[0],
|
||||
$hLog[2],
|
||||
$hLog[1],
|
||||
$sUserAgent,
|
||||
join(',', $aLanguageList),
|
||||
$sOutputFormat,
|
||||
$hLog[3]
|
||||
)));
|
||||
$sSQL .= ')';
|
||||
$oDB->exec($sSQL);
|
||||
}
|
||||
|
||||
return $hLog;
|
||||
}
|
||||
return $hLog;
|
||||
}
|
||||
|
||||
function logEnd(&$oDB, $hLog, $iNumResults)
|
||||
{
|
||||
$aEndTime = explode('.',microtime(true));
|
||||
if (!$aEndTime[1]) $aEndTime[1] = '0';
|
||||
$sEndTime = date('Y-m-d H:i:s',$aEndTime[0]).'.'.$aEndTime[1];
|
||||
function logEnd(&$oDB, $hLog, $iNumResults)
|
||||
{
|
||||
$fEndTime = microtime(true);
|
||||
|
||||
if (CONST_Log_DB)
|
||||
{
|
||||
$sSQL = 'update query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
||||
$sSQL .= ' and ipaddress = '.getDBQuoted($hLog[1]);
|
||||
$sSQL .= ' and query = '.getDBQuoted($hLog[3]);
|
||||
$oDB->query($sSQL);
|
||||
if (CONST_Log_DB) {
|
||||
$aEndTime = explode('.', $fEndTime);
|
||||
if (!$aEndTime[1]) $aEndTime[1] = '0';
|
||||
$sEndTime = date('Y-m-d H:i:s', $aEndTime[0]).'.'.$aEndTime[1];
|
||||
|
||||
$sSQL = 'update new_query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
||||
$sSQL .= ' and ipaddress = '.getDBQuoted($hLog[1]);
|
||||
$sSQL .= ' and query = '.getDBQuoted($hLog[2]);
|
||||
$oDB->query($sSQL);
|
||||
}
|
||||
$sSQL = 'update new_query_log set endtime = '.$oDB->getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||
$sSQL .= ' where starttime = '.$oDB->getDBQuoted($hLog[0]);
|
||||
$sSQL .= ' and ipaddress = '.$oDB->getDBQuoted($hLog[1]);
|
||||
$sSQL .= ' and query = '.$oDB->getDBQuoted($hLog[2]);
|
||||
$oDB->exec($sSQL);
|
||||
}
|
||||
|
||||
if (CONST_Log_File && CONST_Log_File_SearchLog != '')
|
||||
{
|
||||
$aStartTime = explode('.',$hLog[0]);
|
||||
file_put_contents(CONST_Log_File_SearchLog,
|
||||
$aStartTime[0].','.$aStartTime[1].','.
|
||||
php_uname('n').','.
|
||||
'"'.addslashes(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'",'.
|
||||
'"'.addslashes($hLog[1]).'",'.
|
||||
'"'.addslashes($hLog[3]).'",'.
|
||||
'"'.addslashes($_SERVER['HTTP_USER_AGENT']).'",'.
|
||||
'"'.addslashes((isset($_GET['format']))?$_GET['format']:'').'",'.
|
||||
$iNumResults."\n",
|
||||
FILE_APPEND);
|
||||
}
|
||||
|
||||
}
|
||||
if (CONST_Log_File) {
|
||||
$aOutdata = sprintf(
|
||||
"[%s] %.4f %d %s \"%s\"\n",
|
||||
$hLog[0],
|
||||
$fEndTime-$hLog[5],
|
||||
$iNumResults,
|
||||
$hLog[4],
|
||||
$hLog[2]
|
||||
);
|
||||
file_put_contents(CONST_Log_File, $aOutdata, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
52
lib/output.php
Normal file
52
lib/output.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
|
||||
function formatOSMType($sType, $bIncludeExternal = true)
|
||||
{
|
||||
if ($sType == 'N') return 'node';
|
||||
if ($sType == 'W') return 'way';
|
||||
if ($sType == 'R') return 'relation';
|
||||
|
||||
if (!$bIncludeExternal) return '';
|
||||
|
||||
if ($sType == 'T') return 'way';
|
||||
if ($sType == 'I') return 'way';
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function osmLink($aFeature, $sRefText = false)
|
||||
{
|
||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
||||
if ($sOSMType) {
|
||||
return '<a href="//www.openstreetmap.org/'.$sOSMType.'/'.$aFeature['osm_id'].'">'.$sOSMType.' '.($sRefText?$sRefText:$aFeature['osm_id']).'</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function wikipediaLink($aFeature)
|
||||
{
|
||||
if ($aFeature['wikipedia']) {
|
||||
list($sLanguage, $sArticle) = explode(':', $aFeature['wikipedia']);
|
||||
return '<a href="https://'.$sLanguage.'.wikipedia.org/wiki/'.urlencode($sArticle).'" target="_blank">'.$aFeature['wikipedia'].'</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function detailsLink($aFeature, $sTitle = false)
|
||||
{
|
||||
if (!$aFeature['place_id']) return '';
|
||||
|
||||
return '<a href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
||||
}
|
||||
|
||||
function detailsPermaLink($aFeature, $sRefText = false)
|
||||
{
|
||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
||||
|
||||
if ($sOSMType) {
|
||||
$sLabel = $sRefText ? $sRefText : $sOSMType.' '.$aFeature['osm_id'];
|
||||
return '<a href="details.php?osmtype='.$aFeature['osm_type'].'&osmid='.$aFeature['osm_id'].'&class='.$aFeature['class'].'">'.$sLabel.'</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
98
lib/setup/AddressLevelParser.php
Normal file
98
lib/setup/AddressLevelParser.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Setup;
|
||||
|
||||
/**
|
||||
* Parses an address level description.
|
||||
*/
|
||||
class AddressLevelParser
|
||||
{
|
||||
private $aLevels;
|
||||
|
||||
public function __construct($sDescriptionFile)
|
||||
{
|
||||
$sJson = file_get_contents($sDescriptionFile);
|
||||
$this->aLevels = json_decode($sJson, true);
|
||||
if (!$this->aLevels) {
|
||||
switch (json_last_error()) {
|
||||
case JSON_ERROR_NONE:
|
||||
break;
|
||||
case JSON_ERROR_DEPTH:
|
||||
fail('JSON error - Maximum stack depth exceeded');
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
fail('JSON error - Underflow or the modes mismatch');
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
fail('JSON error - Unexpected control character found');
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX:
|
||||
fail('JSON error - Syntax error, malformed JSON');
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
fail('JSON error - Malformed UTF-8 characters, possibly incorrectly encoded');
|
||||
break;
|
||||
default:
|
||||
fail('JSON error - Unknown error');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the description into a database table.
|
||||
*
|
||||
* @param object $oDB Database conneciton to use.
|
||||
* @param string $sTable Name of table to create.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
* A new table is created. Any previously existing table is dropped.
|
||||
* The table has the following columns:
|
||||
* country, class, type, rank_search, rank_address.
|
||||
*/
|
||||
public function createTable($oDB, $sTable)
|
||||
{
|
||||
$oDB->exec('DROP TABLE IF EXISTS '.$sTable);
|
||||
$sSql = 'CREATE TABLE '.$sTable;
|
||||
$sSql .= '(country_code varchar(2), class TEXT, type TEXT,';
|
||||
$sSql .= ' rank_search SMALLINT, rank_address SMALLINT)';
|
||||
$oDB->exec($sSql);
|
||||
|
||||
$sSql = 'CREATE UNIQUE INDEX ON '.$sTable.' (country_code, class, type)';
|
||||
$oDB->exec($sSql);
|
||||
|
||||
$sSql = 'INSERT INTO '.$sTable.' VALUES ';
|
||||
foreach ($this->aLevels as $aLevel) {
|
||||
$aCountries = array();
|
||||
if (isset($aLevel['countries'])) {
|
||||
foreach ($aLevel['countries'] as $sCountry) {
|
||||
$aCountries[$sCountry] = $oDB->getDBQuoted($sCountry);
|
||||
}
|
||||
} else {
|
||||
$aCountries['NULL'] = 'NULL';
|
||||
}
|
||||
foreach ($aLevel['tags'] as $sKey => $aValues) {
|
||||
foreach ($aValues as $sValue => $mRanks) {
|
||||
$aFields = array(
|
||||
$oDB->getDBQuoted($sKey),
|
||||
$sValue ? $oDB->getDBQuoted($sValue) : 'NULL'
|
||||
);
|
||||
if (is_array($mRanks)) {
|
||||
$aFields[] = (string) $mRanks[0];
|
||||
$aFields[] = (string) $mRanks[1];
|
||||
} else {
|
||||
$aFields[] = (string) $mRanks;
|
||||
$aFields[] = (string) $mRanks;
|
||||
}
|
||||
$sLine = ','.join(',', $aFields).'),';
|
||||
|
||||
foreach ($aCountries as $sCountries) {
|
||||
$sSql .= '('.$sCountries.$sLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$oDB->exec(rtrim($sSql, ','));
|
||||
}
|
||||
}
|
||||
913
lib/setup/SetupClass.php
Executable file
913
lib/setup/SetupClass.php
Executable file
@@ -0,0 +1,913 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim\Setup;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/setup/AddressLevelParser.php');
|
||||
|
||||
class SetupFunctions
|
||||
{
|
||||
protected $iCacheMemory;
|
||||
protected $iInstances;
|
||||
protected $sModulePath;
|
||||
protected $aDSNInfo;
|
||||
protected $bVerbose;
|
||||
protected $sIgnoreErrors;
|
||||
protected $bEnableDiffUpdates;
|
||||
protected $bEnableDebugStatements;
|
||||
protected $bNoPartitions;
|
||||
protected $oDB = null;
|
||||
|
||||
public function __construct(array $aCMDResult)
|
||||
{
|
||||
// by default, use all but one processor, but never more than 15.
|
||||
$this->iInstances = isset($aCMDResult['threads'])
|
||||
? $aCMDResult['threads']
|
||||
: (min(16, getProcessorCount()) - 1);
|
||||
|
||||
if ($this->iInstances < 1) {
|
||||
$this->iInstances = 1;
|
||||
warn('resetting threads to '.$this->iInstances);
|
||||
}
|
||||
|
||||
// Assume we can steal all the cache memory in the box (unless told otherwise)
|
||||
if (isset($aCMDResult['osm2pgsql-cache'])) {
|
||||
$this->iCacheMemory = $aCMDResult['osm2pgsql-cache'];
|
||||
} else {
|
||||
$this->iCacheMemory = getCacheMemoryMB();
|
||||
}
|
||||
|
||||
$this->sModulePath = CONST_Database_Module_Path;
|
||||
info('module path: ' . $this->sModulePath);
|
||||
|
||||
// parse database string
|
||||
$this->aDSNInfo = \Nominatim\DB::parseDSN(CONST_Database_DSN);
|
||||
if (!isset($this->aDSNInfo['port'])) {
|
||||
$this->aDSNInfo['port'] = 5432;
|
||||
}
|
||||
|
||||
// setting member variables based on command line options stored in $aCMDResult
|
||||
$this->bVerbose = $aCMDResult['verbose'];
|
||||
|
||||
//setting default values which are not set by the update.php array
|
||||
if (isset($aCMDResult['ignore-errors'])) {
|
||||
$this->sIgnoreErrors = $aCMDResult['ignore-errors'];
|
||||
} else {
|
||||
$this->sIgnoreErrors = false;
|
||||
}
|
||||
if (isset($aCMDResult['enable-debug-statements'])) {
|
||||
$this->bEnableDebugStatements = $aCMDResult['enable-debug-statements'];
|
||||
} else {
|
||||
$this->bEnableDebugStatements = false;
|
||||
}
|
||||
if (isset($aCMDResult['no-partitions'])) {
|
||||
$this->bNoPartitions = $aCMDResult['no-partitions'];
|
||||
} else {
|
||||
$this->bNoPartitions = false;
|
||||
}
|
||||
if (isset($aCMDResult['enable-diff-updates'])) {
|
||||
$this->bEnableDiffUpdates = $aCMDResult['enable-diff-updates'];
|
||||
} else {
|
||||
$this->bEnableDiffUpdates = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function createDB()
|
||||
{
|
||||
info('Create DB');
|
||||
$oDB = new \Nominatim\DB;
|
||||
|
||||
if ($oDB->databaseExists()) {
|
||||
fail('database already exists ('.CONST_Database_DSN.')');
|
||||
}
|
||||
|
||||
$sCreateDBCmd = 'createdb -E UTF-8 -p '.$this->aDSNInfo['port'].' '.$this->aDSNInfo['database'];
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sCreateDBCmd .= ' -U '.$this->aDSNInfo['username'];
|
||||
}
|
||||
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sCreateDBCmd .= ' -h '.$this->aDSNInfo['hostspec'];
|
||||
}
|
||||
|
||||
$result = $this->runWithPgEnv($sCreateDBCmd);
|
||||
if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$this->oDB = new \Nominatim\DB();
|
||||
$this->oDB->connect();
|
||||
}
|
||||
|
||||
public function setupDB()
|
||||
{
|
||||
info('Setup DB');
|
||||
|
||||
$fPostgresVersion = $this->oDB->getPostgresVersion();
|
||||
echo 'Postgres version found: '.$fPostgresVersion."\n";
|
||||
|
||||
if ($fPostgresVersion < 9.03) {
|
||||
fail('Minimum supported version of Postgresql is 9.3.');
|
||||
}
|
||||
|
||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
|
||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
|
||||
|
||||
$fPostgisVersion = $this->oDB->getPostgisVersion();
|
||||
echo 'Postgis version found: '.$fPostgisVersion."\n";
|
||||
|
||||
if ($fPostgisVersion < 2.2) {
|
||||
echo "Minimum required Postgis version 2.2\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$i = $this->oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'");
|
||||
if ($i == 0) {
|
||||
echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
|
||||
echo "\n createuser ".CONST_Database_Web_User."\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Try accessing the C module, so we know early if something is wrong
|
||||
checkModulePresence(); // raises exception on failure
|
||||
|
||||
if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
|
||||
echo 'Error: you need to download the country_osm_grid first:';
|
||||
echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
|
||||
exit(1);
|
||||
}
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode_table.sql');
|
||||
|
||||
$sPostcodeFilename = CONST_BasePath.'/data/gb_postcode_data.sql.gz';
|
||||
if (file_exists($sPostcodeFilename)) {
|
||||
$this->pgsqlRunScriptFile($sPostcodeFilename);
|
||||
} else {
|
||||
warn('optional external GB postcode table file ('.$sPostcodeFilename.') not found. Skipping.');
|
||||
}
|
||||
|
||||
$sPostcodeFilename = CONST_BasePath.'/data/us_postcode_data.sql.gz';
|
||||
if (file_exists($sPostcodeFilename)) {
|
||||
$this->pgsqlRunScriptFile($sPostcodeFilename);
|
||||
} else {
|
||||
warn('optional external US postcode table file ('.$sPostcodeFilename.') not found. Skipping.');
|
||||
}
|
||||
|
||||
if ($this->bNoPartitions) {
|
||||
$this->pgsqlRunScript('update country_name set partition = 0');
|
||||
}
|
||||
|
||||
// the following will be needed by createFunctions later but
|
||||
// is only defined in the subsequently called createTables
|
||||
// Create dummies here that will be overwritten by the proper
|
||||
// versions in create-tables.
|
||||
$this->pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
|
||||
$this->pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
|
||||
}
|
||||
|
||||
public function importData($sOSMFile)
|
||||
{
|
||||
info('Import data');
|
||||
|
||||
$osm2pgsql = CONST_Osm2pgsql_Binary;
|
||||
if (!file_exists($osm2pgsql)) {
|
||||
echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
|
||||
echo "Normally you should not need to set this manually.\n";
|
||||
fail("osm2pgsql not found in '$osm2pgsql'");
|
||||
}
|
||||
|
||||
$osm2pgsql .= ' -S '.CONST_Import_Style;
|
||||
|
||||
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
|
||||
$osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
|
||||
}
|
||||
|
||||
if (CONST_Tablespace_Osm2pgsql_Data)
|
||||
$osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
|
||||
if (CONST_Tablespace_Osm2pgsql_Index)
|
||||
$osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
|
||||
if (CONST_Tablespace_Place_Data)
|
||||
$osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
|
||||
if (CONST_Tablespace_Place_Index)
|
||||
$osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
|
||||
$osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
|
||||
$osm2pgsql .= ' -C '.$this->iCacheMemory;
|
||||
$osm2pgsql .= ' -P '.$this->aDSNInfo['port'];
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$osm2pgsql .= ' -U '.$this->aDSNInfo['username'];
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$osm2pgsql .= ' -H '.$this->aDSNInfo['hostspec'];
|
||||
}
|
||||
$osm2pgsql .= ' -d '.$this->aDSNInfo['database'].' '.$sOSMFile;
|
||||
|
||||
$this->runWithPgEnv($osm2pgsql);
|
||||
|
||||
if (!$this->sIgnoreErrors && !$this->oDB->getRow('select * from place limit 1')) {
|
||||
fail('No Data');
|
||||
}
|
||||
}
|
||||
|
||||
public function createFunctions()
|
||||
{
|
||||
info('Create Functions');
|
||||
|
||||
// Try accessing the C module, so we know early if something is wrong
|
||||
checkModulePresence(); // raises exception on failure
|
||||
|
||||
$this->createSqlFunctions();
|
||||
}
|
||||
|
||||
public function createTables($bReverseOnly = false)
|
||||
{
|
||||
info('Create Tables');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
|
||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:address-data}',
|
||||
CONST_Tablespace_Address_Data,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:address-index}',
|
||||
CONST_Tablespace_Address_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:search-data}',
|
||||
CONST_Tablespace_Search_Data,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:search-index}',
|
||||
CONST_Tablespace_Search_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-data}',
|
||||
CONST_Tablespace_Aux_Data,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-index}',
|
||||
CONST_Tablespace_Aux_Index,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
|
||||
if ($bReverseOnly) {
|
||||
$this->pgExec('DROP TABLE search_name');
|
||||
}
|
||||
|
||||
$oAlParser = new AddressLevelParser(CONST_Address_Level_Config);
|
||||
$oAlParser->createTable($this->oDB, 'address_levels');
|
||||
}
|
||||
|
||||
public function createPartitionTables()
|
||||
{
|
||||
info('Create Partition Tables');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:address-data}',
|
||||
CONST_Tablespace_Address_Data,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:address-index}',
|
||||
CONST_Tablespace_Address_Index,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:search-data}',
|
||||
CONST_Tablespace_Search_Data,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:search-index}',
|
||||
CONST_Tablespace_Search_Index,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-data}',
|
||||
CONST_Tablespace_Aux_Data,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-index}',
|
||||
CONST_Tablespace_Aux_Index,
|
||||
$sTemplate
|
||||
);
|
||||
|
||||
$this->pgsqlRunPartitionScript($sTemplate);
|
||||
}
|
||||
|
||||
public function createPartitionFunctions()
|
||||
{
|
||||
info('Create Partition Functions');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
|
||||
$this->pgsqlRunPartitionScript($sTemplate);
|
||||
}
|
||||
|
||||
public function importWikipediaArticles()
|
||||
{
|
||||
$sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
|
||||
$sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
|
||||
if (file_exists($sWikiArticlesFile)) {
|
||||
info('Importing wikipedia articles');
|
||||
$this->pgsqlRunDropAndRestore($sWikiArticlesFile);
|
||||
} else {
|
||||
warn('wikipedia article dump file not found - places will have default importance');
|
||||
}
|
||||
if (file_exists($sWikiRedirectsFile)) {
|
||||
info('Importing wikipedia redirects');
|
||||
$this->pgsqlRunDropAndRestore($sWikiRedirectsFile);
|
||||
} else {
|
||||
warn('wikipedia redirect dump file not found - some place importance values may be missing');
|
||||
}
|
||||
}
|
||||
|
||||
public function loadData($bDisableTokenPrecalc)
|
||||
{
|
||||
info('Drop old Data');
|
||||
|
||||
$this->pgExec('TRUNCATE word');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE placex');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE location_property_osmline');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE place_addressline');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE place_boundingbox');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE location_area');
|
||||
echo '.';
|
||||
if (!$this->dbReverseOnly()) {
|
||||
$this->pgExec('TRUNCATE search_name');
|
||||
echo '.';
|
||||
}
|
||||
$this->pgExec('TRUNCATE search_name_blank');
|
||||
echo '.';
|
||||
$this->pgExec('DROP SEQUENCE seq_place');
|
||||
echo '.';
|
||||
$this->pgExec('CREATE SEQUENCE seq_place start 100000');
|
||||
echo '.';
|
||||
|
||||
$sSQL = 'select distinct partition from country_name';
|
||||
$aPartitions = $this->oDB->getCol($sSQL);
|
||||
|
||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
||||
foreach ($aPartitions as $sPartition) {
|
||||
$this->pgExec('TRUNCATE location_road_'.$sPartition);
|
||||
echo '.';
|
||||
}
|
||||
|
||||
// used by getorcreate_word_id to ignore frequent partial words
|
||||
$sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
|
||||
$sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
|
||||
$this->pgExec($sSQL);
|
||||
echo ".\n";
|
||||
|
||||
// pre-create the word list
|
||||
if (!$bDisableTokenPrecalc) {
|
||||
info('Loading word list');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
|
||||
}
|
||||
|
||||
info('Load Data');
|
||||
$sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
|
||||
|
||||
$aDBInstances = array();
|
||||
$iLoadThreads = max(1, $this->iInstances - 1);
|
||||
for ($i = 0; $i < $iLoadThreads; $i++) {
|
||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
||||
$DSN = CONST_Database_DSN;
|
||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
||||
$aDBInstances[$i] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW);
|
||||
pg_ping($aDBInstances[$i]);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $iLoadThreads; $i++) {
|
||||
$sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
|
||||
$sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
|
||||
$sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
|
||||
$sSQL .= ' and ST_IsValid(geometry)';
|
||||
if ($this->bVerbose) echo "$sSQL\n";
|
||||
if (!pg_send_query($aDBInstances[$i], $sSQL)) {
|
||||
fail(pg_last_error($aDBInstances[$i]));
|
||||
}
|
||||
}
|
||||
|
||||
// last thread for interpolation lines
|
||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
||||
$DSN = CONST_Database_DSN;
|
||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
||||
$aDBInstances[$iLoadThreads] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW);
|
||||
pg_ping($aDBInstances[$iLoadThreads]);
|
||||
$sSQL = 'insert into location_property_osmline';
|
||||
$sSQL .= ' (osm_id, address, linegeo)';
|
||||
$sSQL .= ' SELECT osm_id, address, geometry from place where ';
|
||||
$sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
|
||||
if ($this->bVerbose) echo "$sSQL\n";
|
||||
if (!pg_send_query($aDBInstances[$iLoadThreads], $sSQL)) {
|
||||
fail(pg_last_error($aDBInstances[$iLoadThreads]));
|
||||
}
|
||||
|
||||
$bFailed = false;
|
||||
for ($i = 0; $i <= $iLoadThreads; $i++) {
|
||||
while (($hPGresult = pg_get_result($aDBInstances[$i])) !== false) {
|
||||
$resultStatus = pg_result_status($hPGresult);
|
||||
// PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
|
||||
// PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
|
||||
// PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
|
||||
// echo 'Query result ' . $i . ' is: ' . $resultStatus . "\n";
|
||||
if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
|
||||
$resultError = pg_result_error($hPGresult);
|
||||
echo '-- error text ' . $i . ': ' . $resultError . "\n";
|
||||
$bFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bFailed) {
|
||||
fail('SQL errors loading placex and/or location_property_osmline tables');
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
||||
pg_close($aDBInstances[$i]);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
info('Reanalysing database');
|
||||
$this->pgsqlRunScript('ANALYSE');
|
||||
|
||||
$sDatabaseDate = getDatabaseDate($this->oDB);
|
||||
$this->oDB->exec('TRUNCATE import_status');
|
||||
if (!$sDatabaseDate) {
|
||||
warn('could not determine database date.');
|
||||
} else {
|
||||
$sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
|
||||
$this->oDB->exec($sSQL);
|
||||
echo "Latest data imported from $sDatabaseDate.\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function importTigerData()
|
||||
{
|
||||
info('Import Tiger data');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
|
||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-data}',
|
||||
CONST_Tablespace_Aux_Data,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-index}',
|
||||
CONST_Tablespace_Aux_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
|
||||
$aDBInstances = array();
|
||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
||||
$DSN = CONST_Database_DSN;
|
||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
||||
$aDBInstances[$i] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW | PGSQL_CONNECT_ASYNC);
|
||||
pg_ping($aDBInstances[$i]);
|
||||
}
|
||||
|
||||
foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
|
||||
echo $sFile.': ';
|
||||
$hFile = fopen($sFile, 'r');
|
||||
$sSQL = fgets($hFile, 100000);
|
||||
$iLines = 0;
|
||||
while (true) {
|
||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
||||
if (!pg_connection_busy($aDBInstances[$i])) {
|
||||
while (pg_get_result($aDBInstances[$i]));
|
||||
$sSQL = fgets($hFile, 100000);
|
||||
if (!$sSQL) break 2;
|
||||
if (!pg_send_query($aDBInstances[$i], $sSQL)) fail(pg_last_error($aDBInstances[$i]));
|
||||
$iLines++;
|
||||
if ($iLines == 1000) {
|
||||
echo '.';
|
||||
$iLines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
fclose($hFile);
|
||||
|
||||
$bAnyBusy = true;
|
||||
while ($bAnyBusy) {
|
||||
$bAnyBusy = false;
|
||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
||||
if (pg_connection_busy($aDBInstances[$i])) $bAnyBusy = true;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
||||
pg_close($aDBInstances[$i]);
|
||||
}
|
||||
|
||||
info('Creating indexes on Tiger data');
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
|
||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-data}',
|
||||
CONST_Tablespace_Aux_Data,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-index}',
|
||||
CONST_Tablespace_Aux_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
}
|
||||
|
||||
public function calculatePostcodes($bCMDResultAll)
|
||||
{
|
||||
info('Calculate Postcodes');
|
||||
$this->pgExec('TRUNCATE location_postcode');
|
||||
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
||||
$sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
|
||||
$sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
|
||||
$sSQL .= ' ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
|
||||
$sSQL .= ' AND geometry IS NOT null';
|
||||
$sSQL .= ' GROUP BY country_code, pc';
|
||||
$this->pgExec($sSQL);
|
||||
|
||||
// only add postcodes that are not yet available in OSM
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
||||
$sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
|
||||
$sSQL .= ' ST_SetSRID(ST_Point(x,y),4326)';
|
||||
$sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
|
||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
||||
$sSQL .= " WHERE country_code = 'us')";
|
||||
$this->pgExec($sSQL);
|
||||
|
||||
// add missing postcodes for GB (if available)
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
||||
$sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
|
||||
$sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
|
||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
||||
$sSQL .= " WHERE country_code = 'gb')";
|
||||
$this->pgExec($sSQL);
|
||||
|
||||
if (!$bCMDResultAll) {
|
||||
$sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
|
||||
$sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
|
||||
$this->pgExec($sSQL);
|
||||
}
|
||||
|
||||
$sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
|
||||
$sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
|
||||
$this->pgExec($sSQL);
|
||||
}
|
||||
|
||||
public function index($bIndexNoanalyse)
|
||||
{
|
||||
$sOutputFile = '';
|
||||
$sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$this->aDSNInfo['database'].' -P '
|
||||
.$this->aDSNInfo['port'].' -t '.$this->iInstances.$sOutputFile;
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sBaseCmd .= ' -H '.$this->aDSNInfo['hostspec'];
|
||||
}
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sBaseCmd .= ' -U '.$this->aDSNInfo['username'];
|
||||
}
|
||||
|
||||
info('Index ranks 0 - 4');
|
||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -R 4');
|
||||
if ($iStatus != 0) {
|
||||
fail('error status ' . $iStatus . ' running nominatim!');
|
||||
}
|
||||
if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
|
||||
|
||||
info('Index ranks 5 - 25');
|
||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -r 5 -R 25');
|
||||
if ($iStatus != 0) {
|
||||
fail('error status ' . $iStatus . ' running nominatim!');
|
||||
}
|
||||
if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
|
||||
|
||||
info('Index ranks 26 - 30');
|
||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -r 26');
|
||||
if ($iStatus != 0) {
|
||||
fail('error status ' . $iStatus . ' running nominatim!');
|
||||
}
|
||||
|
||||
info('Index postcodes');
|
||||
$sSQL = 'UPDATE location_postcode SET indexed_status = 0';
|
||||
$this->pgExec($sSQL);
|
||||
}
|
||||
|
||||
public function createSearchIndices()
|
||||
{
|
||||
info('Create Search indices');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
|
||||
if (!$this->dbReverseOnly()) {
|
||||
$sTemplate .= file_get_contents(CONST_BasePath.'/sql/indices_search.src.sql');
|
||||
}
|
||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:address-index}',
|
||||
CONST_Tablespace_Address_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:search-index}',
|
||||
CONST_Tablespace_Search_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$sTemplate = $this->replaceTablespace(
|
||||
'{ts:aux-index}',
|
||||
CONST_Tablespace_Aux_Index,
|
||||
$sTemplate
|
||||
);
|
||||
$this->pgsqlRunScript($sTemplate);
|
||||
}
|
||||
|
||||
public function createCountryNames()
|
||||
{
|
||||
info('Create search index for default country names');
|
||||
|
||||
$this->pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
|
||||
$this->pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
|
||||
$this->pgsqlRunScript('select count(*) from (select getorcreate_country(make_standard_name(country_code), country_code) from country_name where country_code is not null) as x');
|
||||
$this->pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
|
||||
$sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v),'
|
||||
.'country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
|
||||
if (CONST_Languages) {
|
||||
$sSQL .= 'in ';
|
||||
$sDelim = '(';
|
||||
foreach (explode(',', CONST_Languages) as $sLang) {
|
||||
$sSQL .= $sDelim."'name:$sLang'";
|
||||
$sDelim = ',';
|
||||
}
|
||||
$sSQL .= ')';
|
||||
} else {
|
||||
// all include all simple name tags
|
||||
$sSQL .= "like 'name:%'";
|
||||
}
|
||||
$sSQL .= ') v';
|
||||
$this->pgsqlRunScript($sSQL);
|
||||
}
|
||||
|
||||
public function drop()
|
||||
{
|
||||
info('Drop tables only required for updates');
|
||||
|
||||
// The implementation is potentially a bit dangerous because it uses
|
||||
// a positive selection of tables to keep, and deletes everything else.
|
||||
// Including any tables that the unsuspecting user might have manually
|
||||
// created. USE AT YOUR OWN PERIL.
|
||||
// tables we want to keep. everything else goes.
|
||||
$aKeepTables = array(
|
||||
'*columns',
|
||||
'import_polygon_*',
|
||||
'import_status',
|
||||
'place_addressline',
|
||||
'location_postcode',
|
||||
'location_property*',
|
||||
'placex',
|
||||
'search_name',
|
||||
'seq_*',
|
||||
'word',
|
||||
'query_log',
|
||||
'new_query_log',
|
||||
'spatial_ref_sys',
|
||||
'country_name',
|
||||
'place_classtype_*',
|
||||
'country_osm_grid'
|
||||
);
|
||||
|
||||
$aDropTables = array();
|
||||
$aHaveTables = $this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'");
|
||||
|
||||
foreach ($aHaveTables as $sTable) {
|
||||
$bFound = false;
|
||||
foreach ($aKeepTables as $sKeep) {
|
||||
if (fnmatch($sKeep, $sTable)) {
|
||||
$bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$bFound) array_push($aDropTables, $sTable);
|
||||
}
|
||||
foreach ($aDropTables as $sDrop) {
|
||||
if ($this->bVerbose) echo "Dropping table $sDrop\n";
|
||||
$this->oDB->exec("DROP TABLE IF EXISTS $sDrop CASCADE");
|
||||
}
|
||||
|
||||
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
|
||||
if (file_exists(CONST_Osm2pgsql_Flatnode_File)) {
|
||||
if ($this->bVerbose) echo 'Deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
|
||||
unlink(CONST_Osm2pgsql_Flatnode_File);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function pgsqlRunDropAndRestore($sDumpFile)
|
||||
{
|
||||
$sCMD = 'pg_restore -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'].' --no-owner -Fc --clean '.$sDumpFile;
|
||||
if ($this->oDB->getPostgresVersion() >= 9.04) {
|
||||
$sCMD .= ' --if-exists';
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
|
||||
}
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sCMD .= ' -U '.$this->aDSNInfo['username'];
|
||||
}
|
||||
|
||||
$this->runWithPgEnv($sCMD);
|
||||
}
|
||||
|
||||
private function pgsqlRunScript($sScript, $bfatal = true)
|
||||
{
|
||||
runSQLScript(
|
||||
$sScript,
|
||||
$bfatal,
|
||||
$this->bVerbose,
|
||||
$this->sIgnoreErrors
|
||||
);
|
||||
}
|
||||
|
||||
private function createSqlFunctions()
|
||||
{
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
|
||||
$sTemplate = str_replace('{modulepath}', $this->sModulePath, $sTemplate);
|
||||
if ($this->bEnableDiffUpdates) {
|
||||
$sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
|
||||
}
|
||||
if ($this->bEnableDebugStatements) {
|
||||
$sTemplate = str_replace('--DEBUG:', '', $sTemplate);
|
||||
}
|
||||
if (CONST_Limit_Reindexing) {
|
||||
$sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
|
||||
}
|
||||
if (!CONST_Use_US_Tiger_Data) {
|
||||
$sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
|
||||
}
|
||||
if (!CONST_Use_Aux_Location_data) {
|
||||
$sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
|
||||
}
|
||||
|
||||
$sReverseOnly = $this->dbReverseOnly() ? 'true' : 'false';
|
||||
$sTemplate = str_replace('%REVERSE-ONLY%', $sReverseOnly, $sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate);
|
||||
}
|
||||
|
||||
private function pgsqlRunPartitionScript($sTemplate)
|
||||
{
|
||||
$sSQL = 'select distinct partition from country_name';
|
||||
$aPartitions = $this->oDB->getCol($sSQL);
|
||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
||||
|
||||
preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
|
||||
foreach ($aMatches as $aMatch) {
|
||||
$sResult = '';
|
||||
foreach ($aPartitions as $sPartitionName) {
|
||||
$sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
|
||||
}
|
||||
$sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
|
||||
}
|
||||
|
||||
$this->pgsqlRunScript($sTemplate);
|
||||
}
|
||||
|
||||
private function pgsqlRunScriptFile($sFilename)
|
||||
{
|
||||
if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
|
||||
|
||||
$sCMD = 'psql -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'];
|
||||
if (!$this->bVerbose) {
|
||||
$sCMD .= ' -q';
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
|
||||
}
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sCMD .= ' -U '.$this->aDSNInfo['username'];
|
||||
}
|
||||
$aProcEnv = null;
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
|
||||
}
|
||||
$ahGzipPipes = null;
|
||||
if (preg_match('/\\.gz$/', $sFilename)) {
|
||||
$aDescriptors = array(
|
||||
0 => array('pipe', 'r'),
|
||||
1 => array('pipe', 'w'),
|
||||
2 => array('file', '/dev/null', 'a')
|
||||
);
|
||||
$hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
|
||||
if (!is_resource($hGzipProcess)) fail('unable to start zcat');
|
||||
$aReadPipe = $ahGzipPipes[1];
|
||||
fclose($ahGzipPipes[0]);
|
||||
} else {
|
||||
$sCMD .= ' -f '.$sFilename;
|
||||
$aReadPipe = array('pipe', 'r');
|
||||
}
|
||||
$aDescriptors = array(
|
||||
0 => $aReadPipe,
|
||||
1 => array('pipe', 'w'),
|
||||
2 => array('file', '/dev/null', 'a')
|
||||
);
|
||||
$ahPipes = null;
|
||||
$hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
||||
if (!is_resource($hProcess)) fail('unable to start pgsql');
|
||||
// TODO: error checking
|
||||
while (!feof($ahPipes[1])) {
|
||||
echo fread($ahPipes[1], 4096);
|
||||
}
|
||||
fclose($ahPipes[1]);
|
||||
$iReturn = proc_close($hProcess);
|
||||
if ($iReturn > 0) {
|
||||
fail("pgsql returned with error code ($iReturn)");
|
||||
}
|
||||
if ($ahGzipPipes) {
|
||||
fclose($ahGzipPipes[1]);
|
||||
proc_close($hGzipProcess);
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceTablespace($sTemplate, $sTablespace, $sSql)
|
||||
{
|
||||
if ($sTablespace) {
|
||||
$sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
|
||||
} else {
|
||||
$sSql = str_replace($sTemplate, '', $sSql);
|
||||
}
|
||||
return $sSql;
|
||||
}
|
||||
|
||||
private function runWithPgEnv($sCmd)
|
||||
{
|
||||
if ($this->bVerbose) {
|
||||
echo "Execute: $sCmd\n";
|
||||
}
|
||||
|
||||
$aProcEnv = null;
|
||||
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
|
||||
}
|
||||
|
||||
return runWithEnv($sCmd, $aProcEnv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the SQL command on the open database.
|
||||
*
|
||||
* @param string $sSQL SQL command to execute.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
* @pre connect() must have been called.
|
||||
*/
|
||||
private function pgExec($sSQL)
|
||||
{
|
||||
$this->oDB->exec($sSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is in reverse-only mode.
|
||||
*
|
||||
* @return True if there is no search_name table and infrastructure.
|
||||
*/
|
||||
private function dbReverseOnly()
|
||||
{
|
||||
return !($this->oDB->tableExists('search_name'));
|
||||
}
|
||||
}
|
||||
31
lib/setup_functions.php
Executable file
31
lib/setup_functions.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
function checkInFile($sOSMFile)
|
||||
{
|
||||
if (!isset($sOSMFile)) {
|
||||
fail('missing --osm-file for data import');
|
||||
}
|
||||
|
||||
if (!file_exists($sOSMFile)) {
|
||||
fail('the path supplied to --osm-file does not exist');
|
||||
}
|
||||
|
||||
if (!is_readable($sOSMFile)) {
|
||||
fail('osm-file "' . $aCMDResult['osm-file'] . '" not readable');
|
||||
}
|
||||
}
|
||||
|
||||
function checkModulePresence()
|
||||
{
|
||||
// Try accessing the C module, so we know early if something is wrong.
|
||||
// Raises Nominatim\DatabaseError on failure
|
||||
|
||||
$sModulePath = CONST_Database_Module_Path;
|
||||
$sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
|
||||
$sSQL .= $sModulePath . "/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
|
||||
$sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
|
||||
|
||||
$oDB = new \Nominatim\DB();
|
||||
$oDB->connect();
|
||||
$oDB->exec($sSQL, null, 'Database server failed to load '.$sModulePath.'/nominatim.so module');
|
||||
}
|
||||
81
lib/template/address-geocodejson.php
Normal file
81
lib/template/address-geocodejson.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
// https://github.com/geocoders/geocodejson-spec/
|
||||
|
||||
$aFilteredPlaces = array();
|
||||
|
||||
if (empty($aPlace)) {
|
||||
if (isset($sError))
|
||||
$aFilteredPlaces['error'] = $sError;
|
||||
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
||||
javascript_renderData($aFilteredPlaces);
|
||||
} else {
|
||||
$aFilteredPlaces = array(
|
||||
'type' => 'Feature',
|
||||
'properties' => array(
|
||||
'geocoding' => array()
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['geocoding']['place_id'] = $aPlace['place_id'];
|
||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||
if ($sOSMType) {
|
||||
$aFilteredPlaces['properties']['geocoding']['osm_type'] = $sOSMType;
|
||||
$aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id'];
|
||||
}
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['type'] = $aPlace['type'];
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance;
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['label'] = $aPlace['langaddress'];
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['name'] = $aPlace['placename'];
|
||||
|
||||
if (isset($aPlace['address'])) {
|
||||
$aFieldMappings = array(
|
||||
'house_number' => 'housenumber',
|
||||
'road' => 'street',
|
||||
'locality' => 'locality',
|
||||
'postcode' => 'postcode',
|
||||
'city' => 'city',
|
||||
'district' => 'district',
|
||||
'county' => 'county',
|
||||
'state' => 'state',
|
||||
'country' => 'country'
|
||||
);
|
||||
|
||||
$aAddressNames = $aPlace['address']->getAddressNames();
|
||||
foreach ($aFieldMappings as $sFrom => $sTo) {
|
||||
if (isset($aAddressNames[$sFrom])) {
|
||||
$aFilteredPlaces['properties']['geocoding'][$sTo] = $aAddressNames[$sFrom];
|
||||
}
|
||||
}
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['admin']
|
||||
= $aPlace['address']->getAdminLevels();
|
||||
}
|
||||
|
||||
if (isset($aPlace['asgeojson'])) {
|
||||
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
||||
} else {
|
||||
$aFilteredPlaces['geometry'] = array(
|
||||
'type' => 'Point',
|
||||
'coordinates' => array(
|
||||
(float) $aPlace['lon'],
|
||||
(float) $aPlace['lat']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
javascript_renderData(array(
|
||||
'type' => 'FeatureCollection',
|
||||
'geocoding' => array(
|
||||
'version' => '0.1.0',
|
||||
'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||
'licence' => 'ODbL',
|
||||
'query' => $sQuery
|
||||
),
|
||||
'features' => array($aFilteredPlaces)
|
||||
));
|
||||
}
|
||||
69
lib/template/address-geojson.php
Normal file
69
lib/template/address-geojson.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
$aFilteredPlaces = array();
|
||||
|
||||
if (empty($aPlace)) {
|
||||
if (isset($sError))
|
||||
$aFilteredPlaces['error'] = $sError;
|
||||
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
||||
javascript_renderData($aFilteredPlaces);
|
||||
} else {
|
||||
$aFilteredPlaces = array(
|
||||
'type' => 'Feature',
|
||||
'properties' => array()
|
||||
);
|
||||
|
||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['place_id'] = $aPlace['place_id'];
|
||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||
if ($sOSMType) {
|
||||
$aFilteredPlaces['properties']['osm_type'] = $sOSMType;
|
||||
$aFilteredPlaces['properties']['osm_id'] = $aPlace['osm_id'];
|
||||
}
|
||||
|
||||
$aFilteredPlaces['properties']['place_rank'] = $aPlace['rank_search'];
|
||||
|
||||
$aFilteredPlaces['properties']['category'] = $aPlace['class'];
|
||||
$aFilteredPlaces['properties']['type'] = $aPlace['type'];
|
||||
|
||||
$aFilteredPlaces['properties']['importance'] = $aPlace['importance'];
|
||||
|
||||
$aFilteredPlaces['properties']['addresstype'] = strtolower($aPlace['addresstype']);
|
||||
|
||||
$aFilteredPlaces['properties']['name'] = $aPlace['placename'];
|
||||
|
||||
$aFilteredPlaces['properties']['display_name'] = $aPlace['langaddress'];
|
||||
|
||||
if (isset($aPlace['address'])) {
|
||||
$aFilteredPlaces['properties']['address'] = $aPlace['address']->getAddressNames();
|
||||
}
|
||||
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['properties']['extratags'] = $aPlace['sExtraTags'];
|
||||
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['properties']['namedetails'] = $aPlace['sNameDetails'];
|
||||
|
||||
if (isset($aPlace['aBoundingBox'])) {
|
||||
$aFilteredPlaces['bbox'] = array(
|
||||
(float) $aPlace['aBoundingBox'][2], // minlon
|
||||
(float) $aPlace['aBoundingBox'][0], // minlat
|
||||
(float) $aPlace['aBoundingBox'][3], // maxlon
|
||||
(float) $aPlace['aBoundingBox'][1] // maxlat
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($aPlace['asgeojson'])) {
|
||||
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
||||
} else {
|
||||
$aFilteredPlaces['geometry'] = array(
|
||||
'type' => 'Point',
|
||||
'coordinates' => array(
|
||||
(float) $aPlace['lon'],
|
||||
(float) $aPlace['lat']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
javascript_renderData(array(
|
||||
'type' => 'FeatureCollection',
|
||||
'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||
'features' => array($aFilteredPlaces)
|
||||
));
|
||||
}
|
||||
@@ -1,106 +1,112 @@
|
||||
<?php
|
||||
header("content-type: text/html; charset=UTF-8");
|
||||
header("content-type: text/html; charset=UTF-8");
|
||||
?>
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-header.php'); ?>
|
||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
||||
<link href="css/search.css" rel="stylesheet" type="text/css" />
|
||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
||||
<link href="css/search.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body id="reverse-page">
|
||||
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
||||
|
||||
<form class="form-inline" role="search" accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>reverse.php">
|
||||
<div class="form-group">
|
||||
<input name="format" type="hidden" value="html">
|
||||
<input name="lat" type="text" class="form-control input-sm" placeholder="latitude" value="<?php echo htmlspecialchars($_GET['lat']); ?>" >
|
||||
<input name="lon" type="text" class="form-control input-sm" placeholder="longitude" value="<?php echo htmlspecialchars($_GET['lon']); ?>" >
|
||||
max zoom
|
||||
<form class="form-inline" role="search" accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>reverse.php">
|
||||
<div class="form-group">
|
||||
<input name="format" type="hidden" value="html">
|
||||
lat
|
||||
<input name="lat" type="text" class="form-control input-sm" placeholder="latitude" value="<?php echo $fLat; ?>" >
|
||||
<a href="#" class="btn btn-default btn-xs" id="switch-coords" title="switch lat and lon"><></a>
|
||||
lon
|
||||
<input name="lon" type="text" class="form-control input-sm" placeholder="longitude" value="<?php echo $fLon; ?>" >
|
||||
max zoom
|
||||
|
||||
<select name="zoom" class="form-control input-sm" value="<?php echo htmlspecialchars($_GET['zoom']); ?>">
|
||||
<option value="" <?php echo $_GET['zoom']==''?'selected':'' ?> >--</option>
|
||||
<select name="zoom" class="form-control input-sm">
|
||||
<option value="" <?php if ($iZoom === false) echo 'selected="selected"' ?> >--</option>
|
||||
<?php
|
||||
|
||||
$aZoomLevels = array(
|
||||
0 => "Continent / Sea",
|
||||
1 => "",
|
||||
2 => "",
|
||||
3 => "Country",
|
||||
4 => "",
|
||||
5 => "State",
|
||||
6 => "Region",
|
||||
7 => "",
|
||||
8 => "County",
|
||||
9 => "",
|
||||
10 => "City",
|
||||
11 => "",
|
||||
12 => "Town / Village",
|
||||
13 => "",
|
||||
14 => "Suburb",
|
||||
15 => "",
|
||||
16 => "Street",
|
||||
17 => "",
|
||||
18 => "Building",
|
||||
19 => "",
|
||||
20 => "",
|
||||
21 => "",
|
||||
);
|
||||
|
||||
foreach($aZoomLevels as $iZoomLevel => $sLabel)
|
||||
{
|
||||
$bSel = $iZoom === $iZoomLevel;
|
||||
echo '<option value="'.$iZoomLevel.'"'.($bSel?' selected="selected"':'').'>'.$iZoomLevel.' '.$sLabel.'</option>'."\n";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group search-button-group">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Search</button>
|
||||
</div>
|
||||
<div class="search-type-link">
|
||||
<a href="<?php echo CONST_Website_BaseURL; ?>search.php">forward search</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div id="content">
|
||||
|
||||
<?php if (count($aPlace)>0) { ?>
|
||||
|
||||
<div id="searchresults" class="sidebar">
|
||||
<?php
|
||||
$aResult = $aPlace;
|
||||
|
||||
$aZoomLevels = array(
|
||||
0 => "Continent / Sea",
|
||||
1 => "",
|
||||
2 => "",
|
||||
3 => "Country",
|
||||
4 => "",
|
||||
5 => "State",
|
||||
6 => "Region",
|
||||
7 => "",
|
||||
8 => "County",
|
||||
9 => "",
|
||||
10 => "City",
|
||||
11 => "",
|
||||
12 => "Town / Village",
|
||||
13 => "",
|
||||
14 => "Suburb",
|
||||
15 => "",
|
||||
16 => "Street",
|
||||
17 => "",
|
||||
18 => "Building",
|
||||
19 => "",
|
||||
20 => "",
|
||||
21 => "",
|
||||
);
|
||||
echo '<div class="result" data-position="0">';
|
||||
|
||||
foreach($aZoomLevels as $iZoomLevel => $sLabel)
|
||||
{
|
||||
$bSel = isset($_GET['zoom']) && ($_GET['zoom'] == (string)$iZoomLevel);
|
||||
echo '<option value="'.$iZoomLevel.'"'.($bSel?'selected':'').'>'.$iZoomLevel.' '.$sLabel.'</option>'."\n";
|
||||
}
|
||||
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
||||
echo ' <span class="name">'.htmlspecialchars($aResult['langaddress']).'</span>';
|
||||
if (isset($aResult['label']))
|
||||
echo ' <span class="type">('.$aResult['label'].')</span>';
|
||||
else if ($aResult['type'] == 'yes')
|
||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['class'])).')</span>';
|
||||
else
|
||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
|
||||
echo '<p>'.$aResult['lat'].','.$aResult['lon'].'</p>';
|
||||
echo ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
|
||||
echo '</div>';
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group search-button-group">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Search</button>
|
||||
</div>
|
||||
<div class="search-type-link">
|
||||
<a href="<?php echo CONST_Website_BaseURL; ?>search.php">forward search</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div id="content">
|
||||
|
||||
<?php if ($aPlace) { ?>
|
||||
|
||||
<div id="searchresults" class="sidebar">
|
||||
<?php
|
||||
$aResult = $aPlace;
|
||||
|
||||
echo '<div class="result" data-position="0">';
|
||||
|
||||
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
||||
echo ' <span class="name">'.htmlspecialchars($aResult['langaddress']).'</span>';
|
||||
if (isset($aResult['label']))
|
||||
echo ' <span class="type">('.$aResult['label'].')</span>';
|
||||
else if ($aResult['type'] == 'yes')
|
||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['class'])).')</span>';
|
||||
else
|
||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
|
||||
echo '<p>'.$aResult['lat'].','.$aResult['lon'].'</p>';
|
||||
echo ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
|
||||
echo '</div>';
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<div id="intro" class="sidebar">
|
||||
Search for coordinates or click anywhere on the map.
|
||||
</div>
|
||||
<div id="intro" class="sidebar">
|
||||
Search for coordinates or click anywhere on the map.
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<div id="map-wrapper">
|
||||
<div id="map-position"></div>
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
<div id="map-wrapper">
|
||||
<div id="map-position">
|
||||
<div id="map-position-inner"></div>
|
||||
<div id="map-position-close"><a href="#">hide</a></div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /content -->
|
||||
</div> <!-- /content -->
|
||||
|
||||
|
||||
|
||||
@@ -108,22 +114,22 @@
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
|
||||
$aNominatimMapInit = [
|
||||
'zoom' => isset($_GET['zoom']) ? htmlspecialchars($_GET['zoom']) : CONST_Default_Zoom,
|
||||
'lat' => isset($_GET['lat'] ) ? htmlspecialchars($_GET['lat'] ) : CONST_Default_Lat,
|
||||
'lon' => isset($_GET['lon'] ) ? htmlspecialchars($_GET['lon'] ) : CONST_Default_Lon,
|
||||
'tile_url' => $sTileURL,
|
||||
'tile_attribution' => $sTileAttribution
|
||||
];
|
||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
||||
$aNominatimMapInit = array(
|
||||
'zoom' => $iZoom !== false ? $iZoom : CONST_Default_Zoom,
|
||||
'lat' => $fLat !== false ? $fLat : CONST_Default_Lat,
|
||||
'lon' => $fLon !== false ? $fLon : CONST_Default_Lon,
|
||||
'tile_url' => $sTileURL,
|
||||
'tile_attribution' => $sTileAttribution
|
||||
);
|
||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
||||
|
||||
echo 'var nominatim_results = ' . json_encode([$aPlace], JSON_PRETTY_PRINT) . ';';
|
||||
?>
|
||||
</script>
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
||||
echo 'var nominatim_results = ' . json_encode([$aPlace], JSON_PRETTY_PRINT) . ';';
|
||||
?>
|
||||
</script>
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user