forked from hans/Nominatim
Compare commits
681 Commits
v3.4.2
...
docs-3.6.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b20a1531c1 | ||
|
|
9610664fc5 | ||
|
|
eed2e3f2a8 | ||
|
|
1b62162e08 | ||
|
|
5cf573c340 | ||
|
|
76cd8bf258 | ||
|
|
31ba7e7cf0 | ||
|
|
a338ba695b | ||
|
|
9f6e2de4ed | ||
|
|
1b3acc4f8f | ||
|
|
56cf3b362e | ||
|
|
b59d01fe85 | ||
|
|
65d8770b28 | ||
|
|
38582c3e52 | ||
|
|
5f6fe6cab7 | ||
|
|
34f8e6ddf7 | ||
|
|
603367dced | ||
|
|
eaee87e73d | ||
|
|
ea844db847 | ||
|
|
3ca8cc0344 | ||
|
|
f67c06f128 | ||
|
|
7889509856 | ||
|
|
67c995aef6 | ||
|
|
e20defeebd | ||
|
|
cca646a19e | ||
|
|
ddc2b4b806 | ||
|
|
0334915067 | ||
|
|
987d60ccda | ||
|
|
7d520bf448 | ||
|
|
dc53288e6b | ||
|
|
db02673b60 | ||
|
|
cf9b248f29 | ||
|
|
df12954312 | ||
|
|
a9357b4dce | ||
|
|
63544db8f9 | ||
|
|
7295cad715 | ||
|
|
ff85da0a31 | ||
|
|
75b2d7ca99 | ||
|
|
3c9eeb11fa | ||
|
|
63bacaee2e | ||
|
|
5016eace34 | ||
|
|
2e5c8b5cd3 | ||
|
|
bf0f81adcb | ||
|
|
2db751700e | ||
|
|
62bee4ed37 | ||
|
|
1f07d63dc5 | ||
|
|
cc1af99dbd | ||
|
|
c5d98effc0 | ||
|
|
b68b2ff6b8 | ||
|
|
57f0d55c2e | ||
|
|
3cf763475f | ||
|
|
0f87da017f | ||
|
|
22800d7d59 | ||
|
|
f21853ea9d | ||
|
|
1e76d668bd | ||
|
|
b4b50eef15 | ||
|
|
a9ad390b9e | ||
|
|
2e9e961fff | ||
|
|
13180989d9 | ||
|
|
a4f1e40b72 | ||
|
|
04d485c550 | ||
|
|
a92bd1e2db | ||
|
|
f89e71a861 | ||
|
|
dcc075b34b | ||
|
|
49083c2597 | ||
|
|
29785ba166 | ||
|
|
ffb2c93ba3 | ||
|
|
30a6b6bdac | ||
|
|
cc345f531a | ||
|
|
9ede048769 | ||
|
|
d23bf6e659 | ||
|
|
6b60f0ab03 | ||
|
|
aa9923bf07 | ||
|
|
9160cce6d8 | ||
|
|
885dc0a8e1 | ||
|
|
7324431b12 | ||
|
|
021f2bef4c | ||
|
|
6260fef2e8 | ||
|
|
c7472662a6 | ||
|
|
fecfe62fc6 | ||
|
|
21b0430e46 | ||
|
|
66595c2d2b | ||
|
|
4ac29fb525 | ||
|
|
04d50a271d | ||
|
|
cbbda1ddf0 | ||
|
|
928c6245c9 | ||
|
|
ac9be161f6 | ||
|
|
33378dcf6e | ||
|
|
e31a1f7ef1 | ||
|
|
fa574ae9fd | ||
|
|
b2ebf4b4b7 | ||
|
|
0f5615b618 | ||
|
|
f050f898bc | ||
|
|
ce1c3bab6d | ||
|
|
5cdabc5173 | ||
|
|
42775f959b | ||
|
|
7f55dcef3a | ||
|
|
ba8accf4bb | ||
|
|
e62db51b06 | ||
|
|
b81894d3d5 | ||
|
|
d86cf6801f | ||
|
|
c0d21d0bd3 | ||
|
|
b838f66dd7 | ||
|
|
db9cc270b3 | ||
|
|
4147e04319 | ||
|
|
a888f6ff93 | ||
|
|
bd04c49bc1 | ||
|
|
5872b81232 | ||
|
|
abd20d3ca6 | ||
|
|
95f83b90d2 | ||
|
|
7a16909219 | ||
|
|
ee4684e6a9 | ||
|
|
a2b1a1eb33 | ||
|
|
7bc0fc9611 | ||
|
|
e0e18e2b6f | ||
|
|
788ba6d985 | ||
|
|
b012e15245 | ||
|
|
ba31456278 | ||
|
|
b661c66c00 | ||
|
|
f0372736aa | ||
|
|
e286d3f23d | ||
|
|
6c8a7b0a1a | ||
|
|
3107dc208a | ||
|
|
bf4d75458c | ||
|
|
3604d0d913 | ||
|
|
dd06638dec | ||
|
|
73a0ec22a3 | ||
|
|
b0ef84caae | ||
|
|
64899ef54b | ||
|
|
1064a9264e | ||
|
|
acfa7bec9c | ||
|
|
62b94e838b | ||
|
|
5236e7a03e | ||
|
|
7e9412a044 | ||
|
|
e47c19beb9 | ||
|
|
2fe3c654fc | ||
|
|
5ec48c66cb | ||
|
|
4e7ec92d6f | ||
|
|
e115de47fc | ||
|
|
bb5ffd3904 | ||
|
|
887ae7fcab | ||
|
|
abaaf942cb | ||
|
|
ff47f6f65d | ||
|
|
ca680fc9fc | ||
|
|
b04463bb2d | ||
|
|
c66f701232 | ||
|
|
3b23144ae6 | ||
|
|
f7a9462337 | ||
|
|
17f24d0061 | ||
|
|
eb8bce22b8 | ||
|
|
6a31691121 | ||
|
|
25c4bf6ed4 | ||
|
|
ec5743bcc0 | ||
|
|
7cd330ee55 | ||
|
|
7d2b6879c8 | ||
|
|
a40684162a | ||
|
|
cd997ff058 | ||
|
|
5391bb6cf7 | ||
|
|
6b8ce1ee74 | ||
|
|
a37d46ec25 | ||
|
|
b5c8237118 | ||
|
|
eeffe2caf4 | ||
|
|
c5c7a6a453 | ||
|
|
b969392f40 | ||
|
|
40bc1752c2 | ||
|
|
851c3779b5 | ||
|
|
2a717da850 | ||
|
|
f8694da3c9 | ||
|
|
eeb2b1f998 | ||
|
|
1db8f7e353 | ||
|
|
6625e93be6 | ||
|
|
56053988bf | ||
|
|
d9325dc11a | ||
|
|
d3ca9dd3f7 | ||
|
|
e552f6bce5 | ||
|
|
cf23e10382 | ||
|
|
c84e7e72f1 | ||
|
|
f2ff351da4 | ||
|
|
c5c242d193 | ||
|
|
72193a1c23 | ||
|
|
248d6b413a | ||
|
|
d04e87fb80 | ||
|
|
cc61b74cde | ||
|
|
915d362b11 | ||
|
|
f8a5f2964f | ||
|
|
a8dfbcef44 | ||
|
|
caea14d035 | ||
|
|
c5fc12e04b | ||
|
|
731c620e31 | ||
|
|
0dad470eb9 | ||
|
|
6947ab3a65 | ||
|
|
b219374d36 | ||
|
|
576ee5aaab | ||
|
|
4c9cfe2532 | ||
|
|
7fb62ea904 | ||
|
|
fe250d3ee8 | ||
|
|
6f55c67d16 | ||
|
|
3aa6e6a365 | ||
|
|
b8ced5d96b | ||
|
|
9e21c6a862 | ||
|
|
fe8566928e | ||
|
|
3600709116 | ||
|
|
df115c73b2 | ||
|
|
dbe025fe40 | ||
|
|
2b11a47a2f | ||
|
|
fb63a7418e | ||
|
|
2029931b95 | ||
|
|
91219bb3dd | ||
|
|
ab4fe4d58a | ||
|
|
8ff1f16b7f | ||
|
|
cf4f62c82c | ||
|
|
1d2f4264a2 | ||
|
|
9d506a4afa | ||
|
|
7ac22e9227 | ||
|
|
47f2fe724f | ||
|
|
130571fb29 | ||
|
|
da7218350b | ||
|
|
b6078de6f8 | ||
|
|
07430b0194 | ||
|
|
fae02fab00 | ||
|
|
a68cdc40be | ||
|
|
76b307f42a | ||
|
|
6e4b7eb966 | ||
|
|
770754ae2c | ||
|
|
a932855f6f | ||
|
|
72ee1abc90 | ||
|
|
9e1909643c | ||
|
|
77a1329285 | ||
|
|
be6ecc388c | ||
|
|
13dba94307 | ||
|
|
d51440bb5d | ||
|
|
d730e179bf | ||
|
|
559fe513fa | ||
|
|
5b20fa7e38 | ||
|
|
d16e75de91 | ||
|
|
6fd9994590 | ||
|
|
b9729f3b66 | ||
|
|
d6ff7475f1 | ||
|
|
984979d9bf | ||
|
|
73c449b97b | ||
|
|
1529666232 | ||
|
|
3816b86a9e | ||
|
|
a4b30fc649 | ||
|
|
fc50eb8688 | ||
|
|
071db1fae7 | ||
|
|
a163ea63c5 | ||
|
|
e21a707166 | ||
|
|
1b484fa90d | ||
|
|
06aa0f0b76 | ||
|
|
fb8bb30144 | ||
|
|
7429a33818 | ||
|
|
5b9f61cff8 | ||
|
|
4ff0e20e1f | ||
|
|
a692bfa8f9 | ||
|
|
49dd927406 | ||
|
|
4b21cc1737 | ||
|
|
7ae16f7302 | ||
|
|
73566a9f15 | ||
|
|
fbdf205ab4 | ||
|
|
83b2b4970d | ||
|
|
f29dc7d7ac | ||
|
|
4d5db74c18 | ||
|
|
8c7d285e03 | ||
|
|
1347abb1e7 | ||
|
|
2cb85e48b4 | ||
|
|
5be084e0f5 | ||
|
|
2323923bec | ||
|
|
0f54d42863 | ||
|
|
8201c7f46c | ||
|
|
ed22d640f4 | ||
|
|
01b009ff24 | ||
|
|
665b90bf5a | ||
|
|
4e1f245331 | ||
|
|
f8e1d39208 | ||
|
|
955dae5d4b | ||
|
|
b78cd3f4c9 | ||
|
|
6a3eb7edf2 | ||
|
|
0a710c0762 | ||
|
|
9a204f6284 | ||
|
|
7837970303 | ||
|
|
8cd9550295 | ||
|
|
840c692d5b | ||
|
|
05e0d3e2d4 | ||
|
|
7429ff9dce | ||
|
|
29602fe0cf | ||
|
|
1b95ec5591 | ||
|
|
3efe0dc8dc | ||
|
|
241e4af1b0 | ||
|
|
cac8a8df18 | ||
|
|
1181ceb735 | ||
|
|
f376f45277 | ||
|
|
d364afdf3b | ||
|
|
7ecfcf7eaa | ||
|
|
709c9bbe88 | ||
|
|
db175f606e | ||
|
|
3a664dc676 | ||
|
|
5f8d5f10a6 | ||
|
|
f02d4d9677 | ||
|
|
1889643eca | ||
|
|
4fc5c2024b | ||
|
|
354487d7f4 | ||
|
|
6478058946 | ||
|
|
8b8dcea3de | ||
|
|
64ace51e02 | ||
|
|
4cb5c67a44 | ||
|
|
2edefd9e80 | ||
|
|
fa8b16e7e7 | ||
|
|
2a953700e2 | ||
|
|
c1dc835b5c | ||
|
|
214f92c428 | ||
|
|
95fc680af9 | ||
|
|
22d0c6b5e1 | ||
|
|
ff1be13d0e | ||
|
|
a3201be7e7 | ||
|
|
37f0b51dff | ||
|
|
95d2dd74ad | ||
|
|
a175a25e6c | ||
|
|
6c406124dd | ||
|
|
0f17529486 | ||
|
|
dd10c867db | ||
|
|
8335dd3aa5 | ||
|
|
cd73ac7038 | ||
|
|
7cc33a839c | ||
|
|
828da6a425 | ||
|
|
2e5bdb8794 | ||
|
|
f56bac350b | ||
|
|
d373f16c81 | ||
|
|
ebffa15c7c | ||
|
|
6e4ee160ee | ||
|
|
a5697c5279 | ||
|
|
5abec720d8 | ||
|
|
84403b47cb | ||
|
|
4342a539af | ||
|
|
f4e744ade5 | ||
|
|
155d4c5591 | ||
|
|
f5cbe0e6ba | ||
|
|
a0e7d80daf | ||
|
|
d7e2f61e13 | ||
|
|
96ed4b02d7 | ||
|
|
cffc7c0121 | ||
|
|
d89000cc3d | ||
|
|
3661c75b39 | ||
|
|
3b20b11a9f | ||
|
|
cca366196d | ||
|
|
e09d444068 | ||
|
|
4956f5e710 | ||
|
|
5bebdfa434 | ||
|
|
aea915aa8d | ||
|
|
e0d29f398e | ||
|
|
c43b39bd88 | ||
|
|
8218da27b3 | ||
|
|
aa4bd00631 | ||
|
|
af6b9fdb39 | ||
|
|
c1b6493373 | ||
|
|
c386cca73f | ||
|
|
cadbdaff18 | ||
|
|
57510f517a | ||
|
|
3a2ddbe2e0 | ||
|
|
859347523f | ||
|
|
528fe6553f | ||
|
|
1faa0f4d41 | ||
|
|
82a11cae2d | ||
|
|
431948d768 | ||
|
|
f69c3d2b66 | ||
|
|
08b05964fa | ||
|
|
bd7f597682 | ||
|
|
6d4fbc9d32 | ||
|
|
124410a17b | ||
|
|
a543d57cbd | ||
|
|
8c3a0efe8b | ||
|
|
9e2841ad44 | ||
|
|
233e5f7c0e | ||
|
|
d5d9445cfd | ||
|
|
7be7417b5b | ||
|
|
0a14142156 | ||
|
|
a5e3785843 | ||
|
|
fc19ebb218 | ||
|
|
b45411f988 | ||
|
|
42f6371e47 | ||
|
|
be2aa6ab3a | ||
|
|
6e39ed9573 | ||
|
|
daf45a2993 | ||
|
|
d351b10fde | ||
|
|
0b21050904 | ||
|
|
644a7f524c | ||
|
|
53949ace36 | ||
|
|
14dba39157 | ||
|
|
43fd2a7423 | ||
|
|
4b0ac5356e | ||
|
|
c2f0d8e5ba | ||
|
|
0fb93b1e8a | ||
|
|
f94828c3f4 | ||
|
|
0e1e7c7df2 | ||
|
|
06110ba358 | ||
|
|
bae69f0102 | ||
|
|
77e7f4696b | ||
|
|
47fb2c9126 | ||
|
|
2ab9e4acd3 | ||
|
|
65ee7a8002 | ||
|
|
a5d0657d9b | ||
|
|
b8f01f91ca | ||
|
|
6cc6cf950c | ||
|
|
0b0349f746 | ||
|
|
2740974a13 | ||
|
|
97a9a262bb | ||
|
|
207efe700f | ||
|
|
e33315eaa6 | ||
|
|
5469d02d03 | ||
|
|
42c80893cb | ||
|
|
5c56ea3198 | ||
|
|
42f86329a9 | ||
|
|
08e273c0c7 | ||
|
|
5f8f98fa03 | ||
|
|
08c53ae27d | ||
|
|
f4f369895c | ||
|
|
38c21de0ee | ||
|
|
43cf36e0c7 | ||
|
|
9a9ff95989 | ||
|
|
fc40939775 | ||
|
|
553d8a828c | ||
|
|
80f7392fb1 | ||
|
|
61535c9972 | ||
|
|
b443c92a7a | ||
|
|
22da6c541d | ||
|
|
cd96354bc7 | ||
|
|
c6d859a08a | ||
|
|
ef47515420 | ||
|
|
a471a3d1b0 | ||
|
|
79a68fc2db | ||
|
|
93ddd46231 | ||
|
|
f5f0c197be | ||
|
|
4a30ec28b9 | ||
|
|
37ef9bb3d3 | ||
|
|
3e1d4a87fa | ||
|
|
320d46cc96 | ||
|
|
a06ceeef4c | ||
|
|
def573d7b4 | ||
|
|
7f7d29fdd1 | ||
|
|
c611d49941 | ||
|
|
11cd648699 | ||
|
|
98be5bf637 | ||
|
|
29df9771bb | ||
|
|
e68c1132da | ||
|
|
1047b1c191 | ||
|
|
9431e80eb4 | ||
|
|
178501de61 | ||
|
|
81c7f618fb | ||
|
|
eb2d816f2a | ||
|
|
244cb0e98c | ||
|
|
300ac4b77b | ||
|
|
0d189ac5df | ||
|
|
fed2c307a7 | ||
|
|
7aa2df5389 | ||
|
|
975ef0b305 | ||
|
|
e59146a733 | ||
|
|
8150c3602b | ||
|
|
ca8d776724 | ||
|
|
fdc40d5169 | ||
|
|
d0a97056c4 | ||
|
|
e98619f801 | ||
|
|
86eebc4305 | ||
|
|
4930f776fe | ||
|
|
19948c378a | ||
|
|
b3215b802d | ||
|
|
ed16d5b6aa | ||
|
|
7a94872413 | ||
|
|
98750922eb | ||
|
|
60c4c9ef2c | ||
|
|
101f04bbf2 | ||
|
|
4c593fa859 | ||
|
|
6603ad4006 | ||
|
|
d56c69dd01 | ||
|
|
e26a300c2f | ||
|
|
405482ede4 | ||
|
|
3db2b05069 | ||
|
|
ce5870223a | ||
|
|
9c1bb87493 | ||
|
|
1f7394dd54 | ||
|
|
bb569aa484 | ||
|
|
b0a350db37 | ||
|
|
78526a33b4 | ||
|
|
ab997b7fb1 | ||
|
|
6d431aebb7 | ||
|
|
a00ea23847 | ||
|
|
53ca751a02 | ||
|
|
8c444378bc | ||
|
|
55fdf0abda | ||
|
|
acd8ca2ebd | ||
|
|
06fdfad89e | ||
|
|
00ca493f33 | ||
|
|
b00d16fd7d | ||
|
|
03c373a4b3 | ||
|
|
bdaa39573f | ||
|
|
8a4c7f6e2b | ||
|
|
84ea0753d8 | ||
|
|
c1ef56c870 | ||
|
|
0e3252f045 | ||
|
|
65df218f91 | ||
|
|
5220a92be4 | ||
|
|
d643ca8dee | ||
|
|
de45152028 | ||
|
|
7fd9d0eeef | ||
|
|
d35a0b392e | ||
|
|
cbddfcde5b | ||
|
|
02ffa752ea | ||
|
|
6189e0c79b | ||
|
|
6ed6a0d447 | ||
|
|
484892ae97 | ||
|
|
027d9e938c | ||
|
|
e171f90d81 | ||
|
|
92c5d3b720 | ||
|
|
2a6e8ad68e | ||
|
|
55c8a0ac08 | ||
|
|
6073d948e6 | ||
|
|
b9171dd10b | ||
|
|
97b892fac2 | ||
|
|
b3fdf19b85 | ||
|
|
8c89e16ad2 | ||
|
|
960409c701 | ||
|
|
d1eeaa59a6 | ||
|
|
882f496e0a | ||
|
|
8b8aa1b4e6 | ||
|
|
932ac23f18 | ||
|
|
6c6560ca53 | ||
|
|
0698757e6e | ||
|
|
3a3f9b3496 | ||
|
|
97d87895bf | ||
|
|
c36fd72f99 | ||
|
|
57ae3d03a1 | ||
|
|
3737712044 | ||
|
|
8531339b4e | ||
|
|
540b12537a | ||
|
|
9e2fb45783 | ||
|
|
e7c128b973 | ||
|
|
d4a3470c9e | ||
|
|
4165b8c011 | ||
|
|
357ba2f64d | ||
|
|
3ce8818045 | ||
|
|
76082ac7cb | ||
|
|
4a451671d3 | ||
|
|
a3728b7188 | ||
|
|
a8711ab013 | ||
|
|
3e4754febd | ||
|
|
da1d661fa0 | ||
|
|
1801db523b | ||
|
|
2979c39628 | ||
|
|
bb9bb40287 | ||
|
|
8f6fdfeb0b | ||
|
|
b4e6d72fde | ||
|
|
a338ebfce0 | ||
|
|
4144364a15 | ||
|
|
11c0dd235b | ||
|
|
4a9502bf88 | ||
|
|
6c0d6d3178 | ||
|
|
0a26ca7104 | ||
|
|
2a15b2522f | ||
|
|
c11d1d78e9 | ||
|
|
7e51aa4cef | ||
|
|
9abb96fa6b | ||
|
|
879aafc916 | ||
|
|
5ec25122f6 | ||
|
|
9371b1aeb9 | ||
|
|
6f6d116451 | ||
|
|
3ff6eccfd7 | ||
|
|
5d1fa597ea | ||
|
|
3b6c2c9155 | ||
|
|
f863040b38 | ||
|
|
1033f8bce7 | ||
|
|
cf4dbbd681 | ||
|
|
6dccc693d0 | ||
|
|
c3dc66ce9c | ||
|
|
4856f56d61 | ||
|
|
2edc15dfb8 | ||
|
|
69e31baf68 | ||
|
|
586ff0c364 | ||
|
|
f3ba358d50 | ||
|
|
54bf4c3339 | ||
|
|
acda4344de | ||
|
|
6b0afd5d9b | ||
|
|
850910ed9e | ||
|
|
4ffa11a26c | ||
|
|
f5e60f8c40 | ||
|
|
ddaf1b79d4 | ||
|
|
d732dc3bb2 | ||
|
|
f0af5c5643 | ||
|
|
7a194789bc | ||
|
|
827d7a9a62 | ||
|
|
dae2761137 | ||
|
|
4304c1a7bb | ||
|
|
c537ea18a4 | ||
|
|
28fa7be75a | ||
|
|
f1a5862f3d | ||
|
|
4088e4e371 | ||
|
|
0ef6425847 | ||
|
|
7489deb1b7 | ||
|
|
2059e18e8b | ||
|
|
d11ee4c6d9 | ||
|
|
c74cbde329 | ||
|
|
20d541af06 | ||
|
|
2c163b3959 | ||
|
|
c6a7ef5574 | ||
|
|
7005c6af12 | ||
|
|
256986f01f | ||
|
|
33d322df9d | ||
|
|
631013be02 | ||
|
|
89a990e000 | ||
|
|
ccddd9d1de | ||
|
|
9587fc9909 | ||
|
|
22b7aed769 | ||
|
|
7db0da40ad | ||
|
|
2be70b2c36 | ||
|
|
0e03668cf2 | ||
|
|
28b89daa22 | ||
|
|
2bfa2f4292 | ||
|
|
3b22b9911b | ||
|
|
bc68ff1e43 | ||
|
|
f59af7483b | ||
|
|
394f85a96b | ||
|
|
626e3238f2 | ||
|
|
2051a84a09 | ||
|
|
f8bd4f5133 | ||
|
|
a4e514033d | ||
|
|
95f20ed7ab | ||
|
|
bfe92ea191 | ||
|
|
d3bacf475a | ||
|
|
3474464894 | ||
|
|
5b25bff2d8 | ||
|
|
c36896c524 | ||
|
|
d9fe25ac2e | ||
|
|
0bd006eef8 | ||
|
|
081d1f9779 | ||
|
|
546c975e28 | ||
|
|
05fb037edb | ||
|
|
5cdc196df1 | ||
|
|
0896c07972 | ||
|
|
be9f54d0a9 | ||
|
|
88fab44006 | ||
|
|
000fe3ddff | ||
|
|
f180f99a95 | ||
|
|
8d9aa9bf33 | ||
|
|
9fed91a47f | ||
|
|
12f830fbbb | ||
|
|
6d764a05b0 | ||
|
|
cd3ddec746 | ||
|
|
e4555a208d | ||
|
|
d53af96aa4 | ||
|
|
d1a9dc0f24 | ||
|
|
a1bcb28cea | ||
|
|
3fbba8b9db | ||
|
|
5fb850982a | ||
|
|
6f2e767c77 | ||
|
|
1ead5b0f3f | ||
|
|
eb6681d486 | ||
|
|
7503987630 | ||
|
|
1d337e8a76 | ||
|
|
f42e40712e | ||
|
|
b5fb8608ba | ||
|
|
dea1d67d03 | ||
|
|
9e6fc8f073 | ||
|
|
db6da75683 | ||
|
|
9cfd891fb9 | ||
|
|
f985680d2c | ||
|
|
8ae317e002 | ||
|
|
36e99c43ce | ||
|
|
eeb26aaa6f | ||
|
|
260dbe302a | ||
|
|
c297584726 | ||
|
|
5930383404 | ||
|
|
7395eb9791 | ||
|
|
e0de838b13 | ||
|
|
5292239714 | ||
|
|
b54fca4f7e | ||
|
|
26f47d2eb7 | ||
|
|
72c0898409 | ||
|
|
92d1f5122b | ||
|
|
314de3c3c0 | ||
|
|
544db43026 | ||
|
|
4aca3700b2 | ||
|
|
af833ff042 | ||
|
|
96c1a0a101 | ||
|
|
043f9d8298 |
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
contact_links:
|
||||
- name: Nominatim Discussions
|
||||
url: https://github.com/osm-search/Nominatim/discussions
|
||||
about: Ask questions, get support, share ideas and discuss with community members.
|
||||
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Before opening a new feature request, please search through the open issue to check that your request hasn't been reported already.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
36
.github/ISSUE_TEMPLATE/report-issues-with-search-results.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/report-issues-with-search-results.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Report issues with search results
|
||||
about: You have searched something with Nominatim and did not get the expected result.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## What did you search for?
|
||||
|
||||
Please try to provide a link to your search. You can go to https://nominatim.openstreetmap.org and repeat your search there. If you originally found the issue somewhere else, please tell us what software/website you were using.
|
||||
|
||||
## What result did you get?
|
||||
|
||||
## What result did you expect?
|
||||
|
||||
**Is the result in the right place and just named wrongly?**
|
||||
|
||||
Please tell us the display name you expected.
|
||||
|
||||
**Is the result missing completely?**
|
||||
|
||||
Make sure that the data you are looking for is in OpenStreetMap. Provide a link to the OpenStreetMap object or if you cannot get it, a link to the map on https://openstreetmap.org where you expect the result to be.
|
||||
|
||||
To get the link to the OSM object, you can try the following:
|
||||
|
||||
* Go to [https://openstreetmap.org](https://openstreetmap.org).
|
||||
* Move to the area of the map where you expect the result and then zoom in as much as possible.
|
||||
* Click on the question mark on the right side of the map. You get a question cursor. Use it to click 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 back the URL that the browser shows.
|
||||
|
||||
## Further details
|
||||
|
||||
Anything else we should know about the search. Particularities with addresses in the area etc.
|
||||
31
.github/ISSUE_TEMPLATE/report-problems-with-the-software.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/report-problems-with-the-software.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Report problems with the software
|
||||
about: You have your own installation of Nominatim and found a bug.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
___Note: if you are installing Nominatim through a docker image, you should report issues with the installation process with the docker repository first.___
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Please describe what you did to get to the issue.
|
||||
|
||||
**Software Environment (please complete the following information):**
|
||||
- Nominatim version:
|
||||
- Postgresql version:
|
||||
- Postgis version:
|
||||
- OS:
|
||||
|
||||
**Hardware Configuration (please complete the following information):**
|
||||
- RAM:
|
||||
- number of CPUs:
|
||||
- type and size of disks:
|
||||
- bare metal/AWS/other cloud service:
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
28
.github/actions/build-nominatim/action.yml
vendored
Normal file
28
.github/actions/build-nominatim/action.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: 'Build Nominatim'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
|
||||
steps:
|
||||
- name: Install prerequisits
|
||||
run: sudo apt-get install -y -qq libboost-system-dev libboost-filesystem-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev python3-psycopg2 python3-pyosmium
|
||||
shell: bash
|
||||
|
||||
- name: Configure
|
||||
run: mkdir build && cd build && cmake ..
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
make -j2 all
|
||||
./utils/setup.php --setup-website
|
||||
shell: bash
|
||||
working-directory: build
|
||||
|
||||
- name: Download dependencies
|
||||
run: |
|
||||
if [ ! -f data/country_osm_grid.sql.gz ]; then
|
||||
wget --no-verbose -O data/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
45
.github/actions/setup-postgresql/action.yml
vendored
Normal file
45
.github/actions/setup-postgresql/action.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: 'Setup Postgresql and Postgis'
|
||||
|
||||
inputs:
|
||||
postgresql-version:
|
||||
description: 'Version of PostgreSQL to install'
|
||||
required: true
|
||||
postgis-version:
|
||||
description: 'Version of Postgis to install'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
|
||||
steps:
|
||||
- name: Remove existing PostgreSQL
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get purge -yq postgresql*
|
||||
shell: bash
|
||||
|
||||
- name: Install PostgreSQL
|
||||
run: |
|
||||
sudo apt-get install -y -qq --no-install-suggests --no-install-recommends postgresql-client-${PGVER} postgresql-${PGVER}-postgis-${POSTGISVER} postgresql-${PGVER}-postgis-${POSTGISVER}-scripts postgresql-contrib-${PGVER} postgresql-${PGVER} postgresql-server-dev-${PGVER}
|
||||
shell: bash
|
||||
env:
|
||||
PGVER: ${{ inputs.postgresql-version }}
|
||||
POSTGISVER: ${{ inputs.postgis-version }}
|
||||
|
||||
- name: Adapt postgresql configuration
|
||||
run: |
|
||||
echo 'fsync = off' | sudo tee /etc/postgresql/${PGVER}/main/conf.d/local.conf
|
||||
echo 'synchronous_commit = off' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf
|
||||
echo 'full_page_writes = off' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf
|
||||
echo 'shared_buffers = 1GB' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf
|
||||
echo 'port = 5432' | sudo tee -a /etc/postgresql/${PGVER}/main/conf.d/local.conf
|
||||
shell: bash
|
||||
env:
|
||||
PGVER: ${{ inputs.postgresql-version }}
|
||||
|
||||
- name: Setup database
|
||||
run: |
|
||||
sudo systemctl restart postgresql
|
||||
sudo -u postgres createuser -S www-data
|
||||
sudo -u postgres createuser -s runner
|
||||
shell: bash
|
||||
120
.github/workflows/ci-tests.yml
vendored
Normal file
120
.github/workflows/ci-tests.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
name: CI Tests
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
postgresql: [9.5, 13]
|
||||
include:
|
||||
- postgresql: 9.5
|
||||
postgis: 2.5
|
||||
- postgresql: 13
|
||||
postgis: 3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Get Date
|
||||
id: get-date
|
||||
run: |
|
||||
echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
data/country_osm_grid.sql.gz
|
||||
monaco-latest.osm.pbf
|
||||
key: nominatim-data-${{ steps.get-date.outputs.date }}
|
||||
|
||||
- uses: ./.github/actions/setup-postgresql
|
||||
with:
|
||||
postgresql-version: ${{ matrix.postgresql }}
|
||||
postgis-version: ${{ matrix.postgis }}
|
||||
- uses: ./.github/actions/build-nominatim
|
||||
|
||||
- name: Install test prerequsites
|
||||
run: |
|
||||
sudo apt-get install -y -qq php-codesniffer python3-tidylib
|
||||
sudo pip3 install behave nose
|
||||
|
||||
- name: PHP linting
|
||||
run: phpcs --report-width=120 .
|
||||
|
||||
- name: PHP unit tests
|
||||
run: phpunit ./
|
||||
working-directory: test/php
|
||||
|
||||
- name: BDD tests
|
||||
run: behave -DREMOVE_TEMPLATE=1 --format=progress3 db osm2pgsql
|
||||
working-directory: test/bdd
|
||||
|
||||
import:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Get Date
|
||||
id: get-date
|
||||
run: |
|
||||
echo "::set-output name=date::$(/bin/date -u "+%Y%W")"
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
data/country_osm_grid.sql.gz
|
||||
monaco-latest.osm.pbf
|
||||
key: nominatim-data-${{ steps.get-date.outputs.date }}
|
||||
|
||||
- uses: ./.github/actions/setup-postgresql
|
||||
with:
|
||||
postgresql-version: 13
|
||||
postgis-version: 3
|
||||
- uses: ./.github/actions/build-nominatim
|
||||
|
||||
- name: Create configuration
|
||||
run: |
|
||||
echo '<?php' > settings/local.php
|
||||
echo " @define('CONST_Pyosmium_Binary', '/usr/lib/python3-pyosmium/pyosmium-get-changes');" >> settings/local.php
|
||||
working-directory: build
|
||||
|
||||
- name: Download import data
|
||||
run: |
|
||||
if [ ! -f monaco-latest.osm.pbf ]; then
|
||||
wget --no-verbose https://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Import
|
||||
run: php ./utils/setup.php --osm-file ../monaco-latest.osm.pbf --osm2pgsql-cache 500 --all
|
||||
working-directory: build
|
||||
|
||||
- name: Import special phrases
|
||||
run: php ./utils/specialphrases.php --wiki-import | psql -d nominatim
|
||||
working-directory: build
|
||||
|
||||
- name: Check import
|
||||
run: php ./utils/check_import_finished.php
|
||||
working-directory: build
|
||||
|
||||
- name: Run update
|
||||
run: |
|
||||
php ./utils/update.php --init-updates
|
||||
php ./utils/update.php --import-osmosis
|
||||
working-directory: build
|
||||
|
||||
- name: Run reverse-only import
|
||||
run : |
|
||||
dropdb nominatim
|
||||
php ./utils/setup.php --osm-file ../monaco-latest.osm.pbf --reverse-only --all
|
||||
working-directory: build
|
||||
34
.travis.yml
34
.travis.yml
@@ -1,34 +0,0 @@
|
||||
---
|
||||
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
|
||||
183
CMakeLists.txt
183
CMakeLists.txt
@@ -19,7 +19,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
project(nominatim)
|
||||
|
||||
set(NOMINATIM_VERSION_MAJOR 3)
|
||||
set(NOMINATIM_VERSION_MINOR 4)
|
||||
set(NOMINATIM_VERSION_MINOR 6)
|
||||
set(NOMINATIM_VERSION_PATCH 0)
|
||||
|
||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
||||
@@ -28,30 +28,40 @@ add_definitions(-DNOMINATIM_VERSION="${NOMINATIM_VERSION}")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Find external dependencies
|
||||
#
|
||||
# Configuration
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
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")
|
||||
set(BUILD_IMPORTER on CACHE BOOL "Build everything for importing/updating the database")
|
||||
set(BUILD_API on CACHE BOOL "Build everything for the API server")
|
||||
set(BUILD_MODULE on CACHE BOOL "Build PostgreSQL module")
|
||||
set(BUILD_TESTS on CACHE BOOL "Build test suite")
|
||||
set(BUILD_DOCS on CACHE BOOL "Build documentation")
|
||||
set(BUILD_OSM2PGSQL on CACHE BOOL "Build osm2pgsql (expert only)")
|
||||
|
||||
if (NOT ONLY_DOCS)
|
||||
#-----------------------------------------------------------------------------
|
||||
# osm2pgsql (imports/updates only)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (BUILD_IMPORTER AND BUILD_OSM2PGSQL)
|
||||
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()
|
||||
set(BUILD_TESTS_SAVED "${BUILD_TESTS}")
|
||||
set(BUILD_TESTS off)
|
||||
set(WITH_LUA off CACHE BOOL "")
|
||||
add_subdirectory(osm2pgsql)
|
||||
set(BUILD_TESTS ${BUILD_TESTS_SAVED})
|
||||
endif()
|
||||
|
||||
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})
|
||||
#-----------------------------------------------------------------------------
|
||||
# python and pyosmium (imports/updates only)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (BUILD_IMPORTER)
|
||||
find_package(PythonInterp 3)
|
||||
|
||||
find_program(PYOSMIUM pyosmium-get-changes)
|
||||
if (NOT EXISTS "${PYOSMIUM}")
|
||||
@@ -61,25 +71,15 @@ if (NOT ONLY_DOCS)
|
||||
set(PYOSMIUM_PATH "${PYOSMIUM}")
|
||||
message(STATUS "Using pyosmium-get-changes at ${PYOSMIUM_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# PHP
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
find_program(PG_CONFIG pg_config)
|
||||
execute_process(COMMAND ${PG_CONFIG} --pgxs
|
||||
OUTPUT_VARIABLE PGXS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
# Setting PHP binary variable as to command line (prevailing) or auto detect
|
||||
|
||||
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 (BUILD_API OR BUILD_IMPORTER)
|
||||
if (NOT PHP_BIN)
|
||||
find_program (PHP_BIN php)
|
||||
endif()
|
||||
@@ -91,85 +91,112 @@ if (NOT ONLY_DOCS)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Setup settings and paths
|
||||
#
|
||||
# import scripts and utilities (importer only)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
)
|
||||
if (BUILD_IMPORTER)
|
||||
set(CUSTOMSCRIPTS
|
||||
utils/check_import_finished.php
|
||||
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
|
||||
)
|
||||
|
||||
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()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# webserver scripts (API only)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (BUILD_API)
|
||||
set(WEBSITESCRIPTS
|
||||
website/deletable.php
|
||||
website/details.php
|
||||
website/lookup.php
|
||||
website/polygons.php
|
||||
website/reverse.php
|
||||
website/search.php
|
||||
website/status.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()
|
||||
|
||||
foreach (script_source ${WEBSITESCRIPTS})
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/website.tmpl
|
||||
${PROJECT_BINARY_DIR}/${script_source})
|
||||
endforeach()
|
||||
set(WEBPATHS css images js)
|
||||
|
||||
foreach (wp ${WEBPATHS})
|
||||
execute_process(
|
||||
COMMAND ln -sf ${PROJECT_SOURCE_DIR}/website/${wp} ${PROJECT_BINARY_DIR}/website/
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(serve
|
||||
php -S 127.0.0.1:8088
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/website
|
||||
)
|
||||
|
||||
add_custom_target(serve-global
|
||||
php -S 0.0.0.0:8088
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/website
|
||||
)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# default settings
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
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)
|
||||
if (BUILD_TESTS)
|
||||
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)
|
||||
COMMAND behave ${test}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/bdd)
|
||||
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)
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/php)
|
||||
|
||||
add_test(NAME phpcs
|
||||
COMMAND phpcs --report-width=120 --colors lib website utils
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Postgres module
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (NOT ONLY_DOCS)
|
||||
if (BUILD_MODULE)
|
||||
add_subdirectory(module)
|
||||
add_subdirectory(nominatim)
|
||||
endif()
|
||||
add_subdirectory(docs)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Documentation
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
if (BUILD_DOCS)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
@@ -7,38 +7,6 @@ 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
|
||||
|
||||
87
ChangeLog
87
ChangeLog
@@ -1,3 +1,90 @@
|
||||
3.6.0
|
||||
|
||||
* add full support for searching by and displaying of addr:* tags
|
||||
* improve address output for large-area objects
|
||||
* better use of country names from OSM data for search and display
|
||||
* better debug output for reverse call
|
||||
* add support for addr:place links without an place equivalent in OSM
|
||||
* improve finding postcodes with normalisation artefacts
|
||||
* batch object to index for rank 30, avoiding a wrap-around of transaction
|
||||
IDs in PostgreSQL
|
||||
* introduce dynamic address rank computation for administrative boundaries
|
||||
depending on linked objects and their place in the admin level hierarchy
|
||||
* add country-specific address ranking for Indonesia, Russia, Belgium and
|
||||
the Netherlands (thanks @hendrikmoree)
|
||||
* make sure wikidata/wikipedia tags are imported for all styles
|
||||
* make POIs searchable by name and housenumber (thanks @joy-yyd)
|
||||
* reverse geocoding now ignores places without an address rank (rivers etc.)
|
||||
* installation of a webserver is no longer mandatory, for development
|
||||
use the php internal webserver via 'make serve
|
||||
* reduce the influence of place nodes in addresses
|
||||
* drop support for the unspecific is_in tag
|
||||
* various minor tweaks to supplied styles
|
||||
* move HTML web frontend into its own project
|
||||
* move scripts for processing external data sources into separate directories
|
||||
* introduce separate configuration for website (thanks @krahulreddy)
|
||||
* update documentation, in particular, clean up development docs
|
||||
* update osm2pgsql to 1.4.0
|
||||
|
||||
3.5.2
|
||||
|
||||
* ensure that wikipedia tags are imported for all styles
|
||||
* reinstate verbosity for indexing during updates
|
||||
* make house number reappear in display name on named POIs
|
||||
* introduce batch processing in indexer to avoid transaction ID overrun
|
||||
* increase splitting for large geometries to improve indexing speed
|
||||
* remove deprecated get_magic_quotes_gpc() function
|
||||
* make sure that all postcodes have an entry in word and are thus searchable
|
||||
* remove use of ST_Covers in conjunction woth ST_Intersects,
|
||||
causes bad query planning and slow updates in Postgis3
|
||||
* update osm2pgsql
|
||||
|
||||
3.5.1
|
||||
|
||||
* disable jit and parallel processing in PostgreSQL for osm2pgsql
|
||||
* update libosmium to 2.15.6 (fixes an issue with processing hanging
|
||||
on large multipolygons)
|
||||
|
||||
3.5.0
|
||||
|
||||
* structured select on HTML search page
|
||||
* new PHP Nominatim\Shell class to wrap shell escaping
|
||||
* remove polygon parameter from all API calls
|
||||
* improve handling of postcode areas
|
||||
* reorganise place linking algorithm, now using wikidata tag as well
|
||||
* remove linkees from search_name and larger_area tables
|
||||
* introduce country-specific address ranks
|
||||
* reorganise rank address computation
|
||||
* cleanup of partition function
|
||||
* improve parenting for large POIs
|
||||
* add support for Postgresql 12 and Postgis 3
|
||||
* add earlier cleanup when --drop is given, to reduce memory usage
|
||||
* remove use of place_id in URLs
|
||||
* replace C nominatim indexer with a simpler Python implementation
|
||||
* split up the huge sql/functions.sql file
|
||||
* move osm2pgsql tests to osm2pgsql
|
||||
* add new extratags style which imports all tags from OSM
|
||||
* add new script for checking the import after completion
|
||||
* update osm2pgsql, reducing memory usage
|
||||
* use new wikipedia importance and add processing of wikidata tags
|
||||
* add search form for details page
|
||||
* use ExtraDataPath for country_grid table
|
||||
* remove short_name from list of names to be displayed
|
||||
* split up CMakeFile, so that all parts can be built separately
|
||||
* update installation instructions for CentOS and Ubuntu
|
||||
* add script for importing/updating multiple country extracts
|
||||
* various documentation improvements
|
||||
|
||||
3.4.2
|
||||
|
||||
* fix security bug in /details endpoint where user input was not
|
||||
properly sanitized
|
||||
|
||||
3.4.1
|
||||
|
||||
* update osm2pgsql to fix hans during updates and lost address numbers
|
||||
during updates
|
||||
|
||||
3.4.0
|
||||
|
||||
* increase required version for PostgreSQL(9.3), PostGIS(2.2) and PHP(7.0)
|
||||
|
||||
12
README.md
12
README.md
@@ -1,4 +1,4 @@
|
||||
[](https://travis-ci.org/openstreetmap/Nominatim)
|
||||
[](https://github.com/osm-search/Nominatim/actions?query=workflow%3A%22CI+Tests%22)
|
||||
|
||||
Nominatim
|
||||
=========
|
||||
@@ -19,8 +19,16 @@ https://nominatim.org/release-docs/develop/ .
|
||||
Installation
|
||||
============
|
||||
|
||||
**Nominatim is a complex piece of software and runs in a complex environment.
|
||||
Installing and running Nominatim is something for experienced system
|
||||
administrators only who can do some trouble-shooting themselves. We are sorry,
|
||||
but we can not provide installation support. We are all doing this in our free
|
||||
time and there is just so much of that time to go around. Do not open issues in
|
||||
our bug tracker if you need help. You can ask questions on the mailing list
|
||||
(see below) or on [help.openstreetmap.org](https://help.openstreetmap.org/).**
|
||||
|
||||
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).
|
||||
There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/admin/Installation), as well as an extensive [Troubleshooting/FAQ section](https://nominatim.org/release-docs/latest/admin/Faq/).
|
||||
|
||||
Detailed installation instructions for the development version can be
|
||||
found at [nominatim.org](https://nominatim.org/release-docs/develop/admin/Installation)
|
||||
|
||||
@@ -141,7 +141,7 @@ 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://nominatim.org/release-docs/latest/Installation) for details.
|
||||
of search results. See [Nominatim installation](https://nominatim.org/release-docs/latest/admin/Installation) for details.
|
||||
|
||||
##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD?
|
||||
|
||||
@@ -160,9 +160,9 @@ You can configure/download other Vagrant boxes from [https://app.vagrantup.com/b
|
||||
|
||||
Let's say you have a Postgres database named `nominatim_it` on server `your-server.com` and port `5432`. The Postgres username is `postgres`. You can edit `settings/local.php` and point Nominatim to it.
|
||||
|
||||
pgsql://postgres@your-server.com:5432/nominatim_it
|
||||
pgsql:host=your-server.com;port=5432;user=postgres;dbname=nominatim_it
|
||||
|
||||
No data import necessary or restarting necessary.
|
||||
No data import or restarting necessary.
|
||||
|
||||
If the Postgres installation is behind a firewall, you can try
|
||||
|
||||
|
||||
88
Vagrantfile
vendored
88
Vagrantfile
vendored
@@ -4,18 +4,65 @@
|
||||
Vagrant.configure("2") do |config|
|
||||
# Apache webserver
|
||||
config.vm.network "forwarded_port", guest: 80, host: 8089
|
||||
config.vm.network "forwarded_port", guest: 8088, host: 8088
|
||||
|
||||
# If true, then any SSH connections made will enable agent forwarding.
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
# Never sync the current directory to /vagrant.
|
||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
|
||||
checkout = "yes"
|
||||
if ENV['CHECKOUT'] != 'y' then
|
||||
config.vm.synced_folder ".", "/home/vagrant/Nominatim"
|
||||
checkout = "no"
|
||||
checkout = "no"
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |vb, override|
|
||||
vb.gui = false
|
||||
vb.memory = 2048
|
||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"]
|
||||
if ENV['CHECKOUT'] != 'y' then
|
||||
override.vm.synced_folder ".", "/home/vagrant/Nominatim"
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.provider "libvirt" do |lv, override|
|
||||
lv.memory = 2048
|
||||
lv.nested = true
|
||||
if ENV['CHECKOUT'] != 'y' then
|
||||
override.vm.synced_folder ".", "/home/vagrant/Nominatim", type: 'nfs'
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu", primary: true do |sub|
|
||||
sub.vm.box = "bento/ubuntu-18.04"
|
||||
sub.vm.box = "generic/ubuntu2004"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-20.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu-apache" do |sub|
|
||||
sub.vm.box = "generic/ubuntu2004"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-20.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout, "install-apache"]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu-nginx" do |sub|
|
||||
sub.vm.box = "generic/ubuntu2004"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-20.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout, "install-nginx"]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu18" do |sub|
|
||||
sub.vm.box = "generic/ubuntu1804"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
||||
s.privileged = false
|
||||
@@ -23,48 +70,41 @@ Vagrant.configure("2") do |config|
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu18nginx" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-18.04"
|
||||
config.vm.define "ubuntu18-apache" do |sub|
|
||||
sub.vm.box = "generic/ubuntu1804"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-18-nginx.sh"
|
||||
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
s.args = [checkout, "install-apache"]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "ubuntu16" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-16.04"
|
||||
config.vm.define "ubuntu18-nginx" do |sub|
|
||||
sub.vm.box = "generic/ubuntu1804"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Ubuntu-16.sh"
|
||||
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
s.args = [checkout, "install-nginx"]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "travis" do |sub|
|
||||
sub.vm.box = "bento/ubuntu-14.04"
|
||||
config.vm.define "centos7" do |sub|
|
||||
sub.vm.box = "centos/7"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/install-on-travis-ci.sh"
|
||||
s.path = "vagrant/Install-on-Centos-7.sh"
|
||||
s.privileged = false
|
||||
s.args = [checkout]
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.define "centos" do |sub|
|
||||
sub.vm.box = "centos/7"
|
||||
sub.vm.box = "generic/centos8"
|
||||
sub.vm.provision :shell do |s|
|
||||
s.path = "vagrant/Install-on-Centos-7.sh"
|
||||
s.path = "vagrant/Install-on-Centos-8.sh"
|
||||
s.privileged = false
|
||||
s.args = "yes"
|
||||
s.args = [checkout]
|
||||
end
|
||||
sub.vm.synced_folder ".", "/home/vagrant/Nominatim", disabled: true
|
||||
sub.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.gui = false
|
||||
vb.memory = 2048
|
||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<?php
|
||||
require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
|
||||
@define('CONST_Debug', (isset($_GET['debug']) && $_GET['debug']));
|
||||
require_once(dirname(dirname(__FILE__)).'/settings/settings-frontend.php');
|
||||
|
||||
require_once(CONST_BasePath.'/@script_source@');
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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`
|
||||
|
||||

|
||||
@@ -1,33 +0,0 @@
|
||||
-- Script to build a calculated country grid from existing tables
|
||||
DROP TABLE IF EXISTS tmp_country_osm_grid;
|
||||
CREATE TABLE tmp_country_osm_grid as select country_name.country_code,st_union(placex.geometry) as geometry from country_name,
|
||||
placex
|
||||
where (lower(placex.country_code) = country_name.country_code)
|
||||
and placex.rank_search < 16 and st_area(placex.geometry) > 0
|
||||
group by country_name.country_code;
|
||||
ALTER TABLE tmp_country_osm_grid add column area double precision;
|
||||
UPDATE tmp_country_osm_grid set area = st_area(geometry::geography);
|
||||
|
||||
-- compare old and new
|
||||
select country_code, round, round(log(area)) from (select distinct country_code,round(log(area)) from country_osm_grid order by country_code) as x
|
||||
left outer join tmp_country_osm_grid using (country_code) where area is null or round(log(area)) != round;
|
||||
|
||||
DROP TABLE IF EXISTS new_country_osm_grid;
|
||||
CREATE TABLE new_country_osm_grid as select country_code,area,quad_split_geometry(geometry,0.5,20) as geometry from tmp_country_osm_grid;
|
||||
CREATE INDEX new_idx_country_osm_grid_geometry ON new_country_osm_grid USING GIST (geometry);
|
||||
|
||||
-- Sometimes there are problems calculating area due to invalid data - optionally recalc
|
||||
UPDATE new_country_osm_grid set area = sum from (select country_code,sum(case when st_area(geometry::geography) = 'NaN' THEN 0 ELSE st_area(geometry::geography) END)
|
||||
from new_country_osm_grid group by country_code) as x where x.country_code = new_country_osm_grid.country_code;
|
||||
|
||||
-- compare old and new
|
||||
select country_code, x.round, y.round from (select distinct country_code,round(log(area)) from country_osm_grid order by country_code) as x
|
||||
left outer join (select distinct country_code,round(log(area)) from new_country_osm_grid order by country_code) as y
|
||||
using (country_code) where x.round != y.round;
|
||||
|
||||
-- Flip the new table in
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS country_osm_grid;
|
||||
ALTER TABLE new_country_osm_grid rename to country_osm_grid;
|
||||
ALTER INDEX new_idx_country_osm_grid_geometry RENAME TO idx_country_osm_grid_geometry;
|
||||
COMMIT;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 320 KiB |
@@ -1,56 +0,0 @@
|
||||
# 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
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/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;
|
||||
@@ -1,26 +0,0 @@
|
||||
# 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
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,620 +0,0 @@
|
||||
#!/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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,58 +0,0 @@
|
||||
## 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
|
||||
```
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,951 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# mysql2pgsql
|
||||
# MySQL to PostgreSQL dump file converter
|
||||
#
|
||||
# For usage: perl mysql2pgsql.perl --help
|
||||
#
|
||||
# ddl statments are changed but none or only minimal real data
|
||||
# formatting are done.
|
||||
# data consistency is up to the DBA.
|
||||
#
|
||||
# (c) 2004-2007 Jose M Duarte and Joseph Speigle ... gborg
|
||||
#
|
||||
# (c) 2000-2004 Maxim Rudensky <fonin@omnistaronline.com>
|
||||
# (c) 2000 Valentine Danilchuk <valdan@ziet.zhitomir.ua>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by the Max Rudensky
|
||||
# and its contributors.
|
||||
# 4. Neither the name of the author nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
|
||||
use Getopt::Long;
|
||||
|
||||
use POSIX;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
||||
# main sections
|
||||
# -------------
|
||||
# 1 variable declarations
|
||||
# 2 subroutines
|
||||
# 3 get commandline options and specify help statement
|
||||
# 4 loop through file and process
|
||||
# 5. print_plpgsql function prototype
|
||||
|
||||
#################################################################
|
||||
# 1. variable declarations
|
||||
#################################################################
|
||||
# command line options
|
||||
my( $ENC_IN, $ENC_OUT, $PRESERVE_CASE, $HELP, $DEBUG, $SCHEMA, $LOWERCASE, $CHAR2VARCHAR, $NODROP, $SEP_FILE, $opt_debug, $opt_help, $opt_schema, $opt_preserve_case, $opt_char2varchar, $opt_nodrop, $opt_sepfile, $opt_enc_in, $opt_enc_out );
|
||||
# variables for constructing pre-create-table entities
|
||||
my $pre_create_sql=''; # comments, 'enum' constraints preceding create table statement
|
||||
my $auto_increment_seq= ''; # so we can easily substitute it if we need a default value
|
||||
my $create_sql=''; # all the datatypes in the create table section
|
||||
my $post_create_sql=''; # create indexes, foreign keys, table comments
|
||||
my $function_create_sql = ''; # for the set (function,trigger) and CURRENT_TIMESTAMP ( function,trigger )
|
||||
# constraints
|
||||
my ($type, $column_valuesStr, @column_values, $value );
|
||||
my %constraints=(); # holds values constraints used to emulate mysql datatypes (e.g. year, set)
|
||||
# datatype conversion variables
|
||||
my ( $index,$seq);
|
||||
my ( $column_name, $col, $quoted_column);
|
||||
my ( @year_holder, $year, $constraint_table_name);
|
||||
my $table=""; # table_name for create sql statements
|
||||
my $table_no_quotes=""; # table_name for create sql statements
|
||||
my $sl = '^\s+\w+\s+'; # matches the column name
|
||||
my $tables_first_timestamp_column= 1; # decision to print warnings about default_timestamp not being in postgres
|
||||
my $mysql_numeric_datatypes = "TINYINT|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|NUMERIC";
|
||||
my $mysql_datetime_datatypes = "|DATE|TIME|TIMESTAMP|DATETIME|YEAR";
|
||||
my $mysql_text_datatypes = "CHAR|VARCHAR|BINARY|VARBINARY|TINYBLOB|BLOB|MEDIUMBLOB|LONGBLOB|TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT|ENUM|SET";
|
||||
my $mysql_datatypesStr = $mysql_numeric_datatypes . "|". $mysql_datetime_datatypes . "|". $mysql_text_datatypes ;
|
||||
# handling INSERT INTO statements
|
||||
my $rowRe = qr{
|
||||
\( # opening parens
|
||||
( # (start capture)
|
||||
(?: # (start group)
|
||||
' # string start
|
||||
[^'\\]* # up to string-end or backslash (escape)
|
||||
(?: # (start group)
|
||||
\\. # gobble escaped character
|
||||
[^'\\]* # up to string-end of backslash
|
||||
)* # (end group, repeat zero or more)
|
||||
' # string end
|
||||
| # (OR)
|
||||
.*? # everything else (not strings)
|
||||
)* # (end group, repeat zero or more)
|
||||
) # (end capture)
|
||||
\) # closing parent
|
||||
}x;
|
||||
|
||||
my ($insert_table, $valueString);
|
||||
#
|
||||
########################################################
|
||||
# 2. subroutines
|
||||
#
|
||||
# get_identifier
|
||||
# print_post_create_sql()
|
||||
# quote_and_lc()
|
||||
# make_plpgsql($table,$column_name) -- at end of file
|
||||
########################################################
|
||||
|
||||
# returns an identifier with the given suffix doing controlled
|
||||
# truncation if necessary
|
||||
sub get_identifier($$$) {
|
||||
my ($table, $col, $suffix) = @_;
|
||||
my $name = '';
|
||||
$table=~s/\"//g; # make sure that $table doesn't have quotes so we don't end up with redundant quoting
|
||||
# in the case of multiple columns
|
||||
my @cols = split(/,/,$col);
|
||||
$col =~ s/,//g;
|
||||
# in case all columns together too long we have to truncate them
|
||||
if (length($col) > 55) {
|
||||
my $totaltocut = length($col)-55;
|
||||
my $tocut = ceil($totaltocut / @cols);
|
||||
@cols = map {substr($_,0,abs(length($_)-$tocut))} @cols;
|
||||
$col="";
|
||||
foreach (@cols){
|
||||
$col.=$_;
|
||||
}
|
||||
}
|
||||
|
||||
my $max_table_length = 63 - length("_${col}_$suffix");
|
||||
|
||||
if (length($table) > $max_table_length) {
|
||||
$table = substr($table, length($table) - $max_table_length, $max_table_length);
|
||||
}
|
||||
return quote_and_lc("${table}_${col}_${suffix}");
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# called when we encounter next CREATE TABLE statement
|
||||
# also called at EOF to print out for last table
|
||||
# prints comments, indexes, foreign key constraints (the latter 2 possibly to a separate file)
|
||||
sub print_post_create_sql() {
|
||||
my ( @create_idx_comments_constraints_commandsArr, $stmts, $table_field_combination);
|
||||
my %stmts;
|
||||
# loop to check for duplicates in $post_create_sql
|
||||
# Needed because of duplicate key declarations ( PRIMARY KEY and KEY), auto_increment columns
|
||||
|
||||
@create_idx_comments_constraints_commandsArr = split(';\n?', $post_create_sql);
|
||||
if ($SEP_FILE) {
|
||||
open(SEP_FILE, ">>:encoding($ENC_OUT)", $SEP_FILE) or die "Unable to open $SEP_FILE for output: $!\n";
|
||||
}
|
||||
|
||||
foreach (@create_idx_comments_constraints_commandsArr) {
|
||||
if (m/CREATE INDEX "*(\S+)"*\s/i) { # CREATE INDEX korean_english_wordsize_idx ON korean_english USING btree (wordsize);
|
||||
$table_field_combination = $1;
|
||||
# if this particular table_field_combination was already used do not print the statement:
|
||||
if ($SEP_FILE) {
|
||||
print SEP_FILE "$_;\n" if !defined($stmts{$table_field_combination});
|
||||
} else {
|
||||
print OUT "$_;\n" if !defined($stmts{$table_field_combination});
|
||||
}
|
||||
$stmts{$table_field_combination} = 1;
|
||||
}
|
||||
elsif (m/COMMENT/i) { # COMMENT ON object IS 'text'; but comment may be part of table name so use 'elsif'
|
||||
print OUT "$_;\n"
|
||||
} else { # foreign key constraint or comments (those preceded by -- )
|
||||
if ($SEP_FILE) {
|
||||
print SEP_FILE "$_;\n";
|
||||
} else {
|
||||
print OUT "$_;\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($SEP_FILE) {
|
||||
close SEP_FILE;
|
||||
}
|
||||
$post_create_sql='';
|
||||
# empty %constraints for next " create table" statement
|
||||
}
|
||||
|
||||
# quotes a string or a multicolumn string (comma separated)
|
||||
# and optionally lowercase (if LOWERCASE is set)
|
||||
# lowercase .... if user wants default postgres behavior
|
||||
# quotes .... to preserve keywords and to preserve case when case-sensitive tables are to be used
|
||||
sub quote_and_lc($)
|
||||
{
|
||||
my $col = shift;
|
||||
if ($LOWERCASE) {
|
||||
$col = lc($col);
|
||||
}
|
||||
if ($col =~ m/,/) {
|
||||
my @cols = split(/,\s?/, $col);
|
||||
@cols = map {"\"$_\""} @cols;
|
||||
return join(', ', @cols);
|
||||
} else {
|
||||
return "\"$col\"";
|
||||
}
|
||||
}
|
||||
|
||||
########################################################
|
||||
# 3. get commandline options and maybe print help
|
||||
########################################################
|
||||
|
||||
GetOptions("help", "debug"=> \$opt_debug, "schema=s" => \$SCHEMA, "preserve_case" => \$opt_preserve_case, "char2varchar" => \$opt_char2varchar, "nodrop" => \$opt_nodrop, "sepfile=s" => \$opt_sepfile, "enc_in=s" => \$opt_enc_in, "enc_out=s" => \$opt_enc_out );
|
||||
|
||||
$HELP = $opt_help || 0;
|
||||
$DEBUG = $opt_debug || 0;
|
||||
$PRESERVE_CASE = $opt_preserve_case || 0;
|
||||
if ($PRESERVE_CASE == 1) { $LOWERCASE = 0; }
|
||||
else { $LOWERCASE = 1; }
|
||||
$CHAR2VARCHAR = $opt_char2varchar || 0;
|
||||
$NODROP = $opt_nodrop || 0;
|
||||
$SEP_FILE = $opt_sepfile || 0;
|
||||
$ENC_IN = $opt_enc_in || 'utf8';
|
||||
$ENC_OUT = $opt_enc_out || 'utf8';
|
||||
|
||||
if (($HELP) || ! defined($ARGV[0]) || ! defined($ARGV[1])) {
|
||||
print "\n\nUsage: perl $0 {--help --debug --preserve_case --char2varchar --nodrop --schema --sepfile --enc_in --enc_out } mysql.sql pg.sql\n";
|
||||
print "\t* OPTIONS WITHOUT ARGS\n";
|
||||
print "\t--help: prints this message \n";
|
||||
print "\t--debug: output the commented-out mysql line above the postgres line in pg.sql \n";
|
||||
print "\t--preserve_case: prevents automatic case-lowering of column and table names\n";
|
||||
print "\t\tIf you want to preserve case, you must set this flag. For example,\n";
|
||||
print "\t\tIf your client application quotes table and column-names and they have cases in them, set this flag\n";
|
||||
print "\t--char2varchar: converts all char fields to varchar\n";
|
||||
print "\t--nodrop: strips out DROP TABLE statements\n";
|
||||
print "\t\totherise harmless warnings are printed by psql when the dropped table does not exist\n";
|
||||
print "\n\t* OPTIONS WITH ARGS\n";
|
||||
print "\t--schema: outputs a line into the postgres sql file setting search_path \n";
|
||||
print "\t--sepfile: output foreign key constraints and indexes to a separate file so that it can be\n";
|
||||
print "\t\timported after large data set is inserted from another dump file\n";
|
||||
print "\t--enc_in: encoding of mysql in file (default utf8) \n";
|
||||
print "\t--enc_out: encoding of postgres out file (default utf8) \n";
|
||||
print "\n\t* REQUIRED ARGUMENTS\n";
|
||||
if (defined ($ARGV[0])) {
|
||||
print "\tmysql.sql ($ARGV[0])\n";
|
||||
} else {
|
||||
print "\tmysql.sql (undefined)\n";
|
||||
}
|
||||
if (defined ($ARGV[1])) {
|
||||
print "\tpg.sql ($ARGV[1])\n";
|
||||
} else {
|
||||
print "\tpg.sql (undefined)\n";
|
||||
}
|
||||
print "\n";
|
||||
exit 1;
|
||||
}
|
||||
########################################################
|
||||
# 4. process through mysql_dump.sql file
|
||||
# in a big loop
|
||||
########################################################
|
||||
|
||||
# open in and out files
|
||||
open(IN,"<:encoding($ENC_IN)", $ARGV[0]) || die "can't open mysql dump file $ARGV[0]";
|
||||
open(OUT,">:encoding($ENC_OUT)", $ARGV[1]) || die "can't open pg dump file $ARGV[1]";
|
||||
|
||||
# output header
|
||||
print OUT "--\n";
|
||||
print OUT "-- Generated from mysql2pgsql.perl\n";
|
||||
print OUT "-- http://gborg.postgresql.org/project/mysql2psql/\n";
|
||||
print OUT "-- (c) 2001 - 2007 Jose M. Duarte, Joseph Speigle\n";
|
||||
print OUT "--\n";
|
||||
print OUT "\n";
|
||||
print OUT "-- warnings are printed for drop tables if they do not exist\n";
|
||||
print OUT "-- please see http://archives.postgresql.org/pgsql-novice/2004-10/msg00158.php\n\n";
|
||||
print OUT "-- ##############################################################\n";
|
||||
|
||||
if ($SCHEMA ) {
|
||||
print OUT "set search_path='" . $SCHEMA . "'\\g\n" ;
|
||||
}
|
||||
|
||||
# loop through mysql file on a per-line basis
|
||||
while(<IN>) {
|
||||
|
||||
############## flow #########################
|
||||
# (the lines are directed to different string variables at different times)
|
||||
#
|
||||
# handle drop table , unlock, connect statements
|
||||
# if ( start of create table) {
|
||||
# print out post_create table (indexes, foreign key constraints, comments from previous table)
|
||||
# add drop table statement if !$NODROP to pre_create_sql
|
||||
# next;
|
||||
# }
|
||||
# else if ( inside create table) {
|
||||
# add comments in this portion to create_sql
|
||||
# if ( end of create table) {
|
||||
# delete mysql-unique CREATE TABLE commands
|
||||
# print pre_create_sql
|
||||
# print the constraint tables for set and year datatypes
|
||||
# print create_sql
|
||||
# print function_create_sql (this is for the enum columns only)
|
||||
# next;
|
||||
# }
|
||||
# do substitutions
|
||||
# -- NUMERIC DATATYPES
|
||||
# -- CHARACTER DATATYPES
|
||||
# -- DATE AND TIME DATATYPES
|
||||
# -- KEY AND UNIQUE CREATIONS
|
||||
# and append them to create_sql
|
||||
# } else {
|
||||
# print inserts on-the-spot (this script only changes default timestamp of 0000-00-00)
|
||||
# }
|
||||
# LOOP until EOF
|
||||
#
|
||||
########################################################
|
||||
|
||||
|
||||
if (!/^\s*insert into/i) { # not inside create table so don't worry about data corruption
|
||||
s/`//g; # '`pgsql uses no backticks to denote table name (CREATE TABLE `sd`) or around field
|
||||
# and table names like mysql
|
||||
# doh! we hope all dashes and special chars are caught by the regular expressions :)
|
||||
}
|
||||
if (/^\s*USE\s*([^;]*);/) {
|
||||
print OUT "\\c ". $1;
|
||||
next;
|
||||
}
|
||||
if (/^(UN)?LOCK TABLES/i || /drop\s+table/i ) {
|
||||
|
||||
# skip
|
||||
# DROP TABLE is added when we see the CREATE TABLE
|
||||
next;
|
||||
}
|
||||
if (/(create\s+table\s+)([-_\w]+)\s/i) { # example: CREATE TABLE `english_english`
|
||||
print_post_create_sql(); # for last table
|
||||
$tables_first_timestamp_column= 1; # decision to print warnings about default_timestamp not being in postgres
|
||||
$create_sql = '';
|
||||
$table_no_quotes = $2 ;
|
||||
$table=quote_and_lc($2);
|
||||
if ( !$NODROP ) { # always print drop table if user doesn't explicitly say not to
|
||||
# to drop a table that is referenced by a view or a foreign-key constraint of another table,
|
||||
# CASCADE must be specified. (CASCADE will remove a dependent view entirely, but in the
|
||||
# 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;\n"; # custom dumps may be missing the 'dump' commands
|
||||
}
|
||||
|
||||
s/(create\s+table\s+)([-_\w]+)\s/$1 $table /i;
|
||||
if ($DEBUG) {
|
||||
$create_sql .= '-- ' . $_;
|
||||
}
|
||||
$create_sql .= $_;
|
||||
next;
|
||||
}
|
||||
if ($create_sql ne "") { # we are inside create table statement so lets process datatypes
|
||||
# print out comments or empty lines in context
|
||||
if ($DEBUG) {
|
||||
$create_sql .= '-- ' . $_;
|
||||
}
|
||||
if (/^#/ || /^$/ || /^\s*--/) {
|
||||
s/^#/--/; # Two hyphens (--) is the SQL-92 standard indicator for comments
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
}
|
||||
|
||||
if (/\).*;/i) { # end of create table squence
|
||||
|
||||
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;
|
||||
s/CONNECTION=[^;\s]+//i;
|
||||
s/CHECKSUM=[^;\s]+//i;
|
||||
s/Type=[^;\s]+//i; # ISAM , # older versions
|
||||
s/COLLATE=[^;\s]+//i; # table's collate
|
||||
s/COLLATE\s+[^;\s]+//i; # table's collate
|
||||
# possible AUTO_INCREMENT starting index, it is used in mysql 5.0.26, not sure since which version
|
||||
if (/AUTO_INCREMENT=(\d+)/i) {
|
||||
# should take < ---- ) ENGINE=MyISAM AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;
|
||||
# and should ouput ---> CREATE SEQUENCE "rhm_host_info_id_seq" START WITH 16;
|
||||
my $start_value = $1;
|
||||
print $auto_increment_seq . "--\n";
|
||||
# print $pre_create_sql . "--\n";
|
||||
$pre_create_sql =~ s/(CREATE SEQUENCE $auto_increment_seq )/$1 START WITH $start_value /;
|
||||
}
|
||||
s/AUTO_INCREMENT=\d+//i;
|
||||
s/PACK_KEYS=\d//i; # mysql 5.0.22
|
||||
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;
|
||||
if (/COMMENT='([^']*)'/) { # ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='must be country zones';
|
||||
$post_create_sql.="COMMENT ON TABLE $table IS '$1'\;"; # COMMENT ON table_name IS 'text';
|
||||
s/COMMENT='[^']*'//i;
|
||||
}
|
||||
$create_sql =~ s/,$//g; # strip last , inside create table
|
||||
# make sure we end in a comma, as KEY statments are turned
|
||||
# into post_create_sql indices
|
||||
# they often are the last line so leaving a 'hanging comma'
|
||||
my @array = split("\n", $create_sql);
|
||||
for (my $a = $#array; $a >= 0; $a--) { #loop backwards
|
||||
if ($a == $#array && $array[$a] =~ m/,\s*$/) { # for last line
|
||||
$array[$a] =~ s/,\s*$//;
|
||||
next;
|
||||
}
|
||||
if ($array[$a] !~ m/create table/i) { # i.e. if there was more than one column in table
|
||||
if ($a != $#array && $array[$a] !~ m/,\s*$/ ) { # for second to last
|
||||
$array[$a] =~ s/$/,/;
|
||||
last;
|
||||
}
|
||||
elsif ($a != $#array && $array[$a] =~ m/,\s*$/ ) { # for second to last
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
$create_sql = join("\n", @array) . "\n";
|
||||
$create_sql .= $_;
|
||||
|
||||
# put comments out first
|
||||
print OUT $pre_create_sql;
|
||||
|
||||
# create separate table to reference and to hold mysql's possible set data-type
|
||||
# values. do that table's creation before create table
|
||||
# definition
|
||||
foreach $column_name (keys %constraints) {
|
||||
$type=$constraints{$column_name}{'type'};
|
||||
$column_valuesStr = $constraints{$column_name}{'values'};
|
||||
$constraint_table_name = get_identifier(${table},${column_name} ,"constraint_table");
|
||||
if ($type eq 'set') {
|
||||
print OUT qq~DROP TABLE $constraint_table_name CASCADE\\g\n~ ;
|
||||
print OUT qq~create table $constraint_table_name ( set_values varchar UNIQUE)\\g\n~ ;
|
||||
$function_create_sql .= make_plpgsql($table,$column_name);
|
||||
} elsif ($type eq 'year') {
|
||||
print OUT qq~DROP TABLE $constraint_table_name CASCADE\\g\n~ ;
|
||||
print OUT qq~create table $constraint_table_name ( year_values varchar UNIQUE)\\g\n~ ;
|
||||
}
|
||||
@column_values = split /,/, $column_valuesStr;
|
||||
foreach $value (@column_values) {
|
||||
print OUT qq~insert into $constraint_table_name values ( $value )\\g\n~; # ad ' for ints and varchars
|
||||
}
|
||||
}
|
||||
|
||||
$create_sql =~ s/double double/double precision/g;
|
||||
|
||||
# print create table and reset create table vars
|
||||
# when moving from each "create table" to "insert" part of dump
|
||||
print OUT $create_sql;
|
||||
print OUT $function_create_sql;
|
||||
$pre_create_sql="";
|
||||
$auto_increment_seq="";
|
||||
$create_sql="";
|
||||
$function_create_sql='';
|
||||
%constraints=();
|
||||
# the post_create_sql for this table is output at the beginning of the next table def
|
||||
# in case we want to make indexes after doing inserting
|
||||
next;
|
||||
}
|
||||
if (/^\s*(\w+)\s+.*COMMENT\s*'([^']*)'/) { #`zone_country_id` int(11) COMMENT 'column comment here',
|
||||
$quoted_column=quote_and_lc($1);
|
||||
$post_create_sql.="COMMENT ON COLUMN $table"."."." $quoted_column IS '$2'\;"; # COMMENT ON table_name.column_name IS 'text';
|
||||
s/COMMENT\s*'[^']*'//i;
|
||||
}
|
||||
|
||||
|
||||
# NUMERIC DATATYPES
|
||||
#
|
||||
# auto_increment -> sequences
|
||||
# UNSIGNED conversions
|
||||
# TINYINT
|
||||
# SMALLINT
|
||||
# MEDIUMINT
|
||||
# INT, INTEGER
|
||||
# BIGINT
|
||||
#
|
||||
# DOUBLE [PRECISION], REAL
|
||||
# DECIMAL(M,D), NUMERIC(M,D)
|
||||
# FLOAT(p)
|
||||
# FLOAT
|
||||
|
||||
s/(\w*int)\(\d+\)/$1/g; # hack of the (n) stuff for e.g. mediumint(2) int(3)
|
||||
|
||||
if (/^(\s*)(\w+)\s*.*numeric.*auto_increment/i) { # int,auto_increment -> serial
|
||||
$seq = get_identifier($table, $2, 'seq');
|
||||
$quoted_column=quote_and_lc($2);
|
||||
# Smash datatype to int8 and autogenerate the sequence.
|
||||
s/^(\s*)(\w+)\s*.*NUMERIC(.*)auto_increment([^,]*)/$1 $quoted_column serial8 $4/ig;
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
}
|
||||
if (/^\s*(\w+)\s+.*int.*auto_increment/i) { # example: data_id mediumint(8) unsigned NOT NULL auto_increment,
|
||||
$seq = get_identifier($table, $1, 'seq');
|
||||
$quoted_column=quote_and_lc($1);
|
||||
s/(\s*)(\w+)\s+.*int.*auto_increment([^,]*)/$1 $quoted_column serial8 $3/ig;
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# convert UNSIGNED to CHECK constraints
|
||||
if (m/^(\s*)(\w+)\s+((float|double precision|double|real|decimal|numeric))(.*)unsigned/i) {
|
||||
$quoted_column = quote_and_lc($2);
|
||||
s/^(\s*)(\w+)\s+((float|double precision|double|real|decimal|numeric))(.*)unsigned/$1 $quoted_column $3 $4 CHECK ($quoted_column >= 0)/i;
|
||||
}
|
||||
# example: `wordsize` tinyint(3) unsigned default NULL,
|
||||
if (m/^(\s+)(\w+)\s+(\w+)\s+unsigned/i) {
|
||||
$quoted_column=quote_and_lc($2);
|
||||
s/^(\s+)(\w+)\s+(\w+)\s+unsigned/$1 $quoted_column $3 CHECK ($quoted_column >= 0)/i;
|
||||
}
|
||||
if (m/^(\s*)(\w+)\s+(bigint.*)unsigned/) {
|
||||
$quoted_column=quote_and_lc($2);
|
||||
# see http://archives.postgresql.org/pgsql-general/2005-07/msg01178.php
|
||||
# and see http://www.postgresql.org/docs/8.2/interactive/datatype-numeric.html
|
||||
# see http://dev.mysql.com/doc/refman/5.1/en/numeric-types.html max size == 20 digits
|
||||
s/^(\s*)(\w+)\s+bigint(.*)unsigned/$1 $quoted_column NUMERIC (20,0) CHECK ($quoted_column >= 0)/i;
|
||||
|
||||
}
|
||||
|
||||
# int type conversion
|
||||
# TINYINT (signed) -128 to 127 (unsigned) 0 255
|
||||
# SMALLINT A small integer. The signed range is -32768 to 32767. The unsigned range is 0 to 65535.
|
||||
# MEDIUMINT A medium-sized integer. The signed range is -8388608 to 8388607. The unsigned range is 0 to 16777215.
|
||||
# INT A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295.
|
||||
# BIGINT The signed range is -9223372036854775808 to 9223372036854775807. The unsigned range is 0 to 18446744073709551615
|
||||
# for postgres see http://www.postgresql.org/docs/8.2/static/datatype-numeric.html#DATATYPE-INT
|
||||
s/^(\s+"*\w+"*\s+)tinyint/$1 smallint/i;
|
||||
s/^(\s+"*\w+"*\s+)mediumint/$1 integer/i;
|
||||
|
||||
# the floating point types
|
||||
# double -> double precision
|
||||
# double(n,m) -> double precision
|
||||
# float - no need for conversion
|
||||
# float(n) - no need for conversion
|
||||
# float(n,m) -> double precision
|
||||
|
||||
s/(^\s*\w+\s+)double(\(\d+,\d+\))?/$1float/i;
|
||||
s/float(\(\d+,\d+\))/float/i;
|
||||
|
||||
#
|
||||
# CHARACTER TYPES
|
||||
#
|
||||
# set
|
||||
# enum
|
||||
# binary(M), VARBINARy(M), tinyblob, tinytext,
|
||||
# bit
|
||||
# char(M), varchar(M)
|
||||
# blob -> text
|
||||
# mediumblob
|
||||
# longblob, longtext
|
||||
# text -> text
|
||||
# mediumtext
|
||||
# longtext
|
||||
# mysql docs: A BLOB is a binary large object that can hold a variable amount of data.
|
||||
|
||||
# set
|
||||
# For example, a column specified as SET('one', 'two') NOT NULL can have any of these values:
|
||||
# ''
|
||||
# 'one'
|
||||
# 'two'
|
||||
# 'one,two'
|
||||
if (/(\w*)\s+set\(((?:['"]\w+['"]\s*,*)+(?:['"]\w+['"])*)\)(.*)$/i) { # example: `au_auth` set('r','w','d') NOT NULL default '',
|
||||
$column_name = $1;
|
||||
$constraints{$column_name}{'values'} = $2; # 'abc','def', ...
|
||||
$constraints{$column_name}{'type'} = "set"; # 'abc','def', ...
|
||||
$_ = qq~ $column_name varchar , ~;
|
||||
$column_name = quote_and_lc($1);
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
|
||||
}
|
||||
if (/(\S*)\s+enum\(((?:['"][^'"]+['"]\s*,)+['"][^'"]+['"])\)(.*)$/i) { # enum handling
|
||||
# example: `test` enum('?','+','-') NOT NULL default '?'
|
||||
# $2 is the values of the enum 'abc','def', ...
|
||||
$quoted_column=quote_and_lc($1);
|
||||
# "test" NOT NULL default '?' CONSTRAINT test_test_constraint CHECK ("test" IN ('?','+','-'))
|
||||
$_ = qq~ $quoted_column varchar CHECK ($quoted_column IN ( $2 ))$3\n~; # just assume varchar?
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
}
|
||||
# Take care of "binary" option for char and varchar
|
||||
# (pre-4.1.2, it indicated a byte array; from 4.1.2, indicates
|
||||
# a binary collation)
|
||||
s/(?:var)?char(?:\(\d+\))? (?:byte|binary)/text/i;
|
||||
if (m/(?:var)?binary\s*\(\d+\)/i) { # c varBINARY(3) in Mysql
|
||||
warn "WARNING in table '$table' '$_': binary type is converted to bytea (unsized) for Postgres\n";
|
||||
}
|
||||
s/(?:var)?binary(?:\(\d+\))?/text/i; # c varBINARY(3) in Mysql
|
||||
s/bit(?:\(\d+\))?/bytea/i; # bit datatype -> bytea
|
||||
|
||||
# large datatypes
|
||||
s/\w*blob/bytea/gi;
|
||||
s/tinytext/text/gi;
|
||||
s/mediumtext/text/gi;
|
||||
s/longtext/text/gi;
|
||||
|
||||
# char -> varchar -- if specified as a command line option
|
||||
# PostgreSQL would otherwise pad with spaces as opposed
|
||||
# to MySQL! Your user interface may depend on this!
|
||||
if ($CHAR2VARCHAR) {
|
||||
s/(^\s+\S+\s+)char/${1}varchar/gi;
|
||||
}
|
||||
|
||||
# nuke column's collate and character set
|
||||
s/(\S+)\s+character\s+set\s+\w+/$1/gi;
|
||||
s/(\S+)\s+collate\s+\w+/$1/gi;
|
||||
|
||||
#
|
||||
# DATE AND TIME TYPES
|
||||
#
|
||||
# date time
|
||||
# year
|
||||
# datetime
|
||||
# timestamp
|
||||
|
||||
# date time
|
||||
# these are the same types in postgres, just do the replacement of 0000-00-00 date
|
||||
|
||||
if (m/default '(\d+)-(\d+)-(\d+)([^']*)'/i) { # we grab the year, month and day
|
||||
# NOTE: times of 00:00:00 are possible and are okay
|
||||
my $time = '';
|
||||
my $year=$1;
|
||||
my $month= $2;
|
||||
my $day = $3;
|
||||
if ($4) {
|
||||
$time = $4;
|
||||
}
|
||||
if ($year eq "0000") { $year = '1970'; }
|
||||
if ($month eq "00") { $month = '01'; }
|
||||
if ($day eq "00") { $day = '01'; }
|
||||
s/default '[^']+'/default '$year-$month-$day$time'/i; # finally we replace with $datetime
|
||||
}
|
||||
|
||||
# convert mysql's year datatype to a constraint
|
||||
if (/(\w*)\s+year\(4\)(.*)$/i) { # can be integer OR string 1901-2155
|
||||
$constraint_table_name = get_identifier($table,$1 ,"constraint_table");
|
||||
$column_name=quote_and_lc($1);
|
||||
@year_holder = ();
|
||||
$year='';
|
||||
for (1901 .. 2155) {
|
||||
$year = "'$_'";
|
||||
unless ($year =~ /2155/) { $year .= ','; }
|
||||
push( @year_holder, $year);
|
||||
}
|
||||
$constraints{$column_name}{'values'} = join('','',@year_holder); # '1901','1902', ...
|
||||
$constraints{$column_name}{'type'} = "year";
|
||||
$_ = qq~ $column_name varchar CONSTRAINT ${table}_${column_name}_constraint REFERENCES $constraint_table_name ("year_values") $2\n~;
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
} elsif (/(\w*)\s+year\(2\)(.*)$/i) { # same for a 2-integer string
|
||||
$constraint_table_name = get_identifier($table,$1 ,"constraint_table");
|
||||
$column_name=quote_and_lc($1);
|
||||
@year_holder = ();
|
||||
$year='';
|
||||
for (1970 .. 2069) {
|
||||
$year = "'$_'";
|
||||
if ($year =~ /2069/) { next; }
|
||||
push( @year_holder, $year);
|
||||
}
|
||||
push( @year_holder, '0000');
|
||||
$constraints{$column_name}{'values'} = join(',',@year_holder); # '1971','1972', ...
|
||||
$constraints{$column_name}{'type'} = "year"; # 'abc','def', ...
|
||||
$_ = qq~ $1 varchar CONSTRAINT ${table}_${column_name}_constraint REFERENCES $constraint_table_name ("year_values") $2\n~;
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
}
|
||||
|
||||
# datetime
|
||||
# Default on a dump from MySQL 5.0.22 is in the same form as datetime so let it flow down
|
||||
# to the timestamp section and deal with it there
|
||||
s/(${sl})datetime /$1timestamp without time zone /i;
|
||||
|
||||
# change not null datetime field to null valid ones
|
||||
# (to support remapping of "zero time" to null
|
||||
# s/($sl)datetime not null/$1timestamp without time zone/i;
|
||||
|
||||
|
||||
# timestamps
|
||||
#
|
||||
# nuke datetime representation (not supported in PostgreSQL)
|
||||
# change default time of 0000-00-00 to 1970-01-01
|
||||
|
||||
# we may possibly need to create a trigger to provide
|
||||
# equal functionality with ON UPDATE CURRENT TIMESTAMP
|
||||
|
||||
|
||||
if (m/${sl}timestamp/i) {
|
||||
if ( m/ON UPDATE CURRENT_TIMESTAMP/i ) { # the ... default CURRENT_TIMESTAMP only applies for blank inserts, not updates
|
||||
s/ON UPDATE CURRENT_TIMESTAMP//i ;
|
||||
m/^\s*(\w+)\s+timestamp/i ;
|
||||
# automatic trigger creation
|
||||
$table_no_quotes =~ s/"//g;
|
||||
$function_create_sql .= " CREATE OR REPLACE FUNCTION update_". $table_no_quotes . "() RETURNS trigger AS '
|
||||
BEGIN
|
||||
NEW.$1 := CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
' LANGUAGE 'plpgsql';
|
||||
|
||||
-- before INSERT is handled by 'default CURRENT_TIMESTAMP'
|
||||
CREATE TRIGGER add_current_date_to_".$table_no_quotes." BEFORE UPDATE ON ". $table . " FOR EACH ROW EXECUTE PROCEDURE
|
||||
update_".$table_no_quotes."();\n";
|
||||
|
||||
}
|
||||
if ($tables_first_timestamp_column && m/DEFAULT NULL/i) {
|
||||
# DEFAULT NULL is the same as DEFAULT CURRENT_TIMESTAMP for the first TIMESTAMP column. (MYSQL manual)
|
||||
s/($sl)(timestamp\s+)default null/$1 $2 DEFAULT CURRENT_TIMESTAMP/i;
|
||||
}
|
||||
$tables_first_timestamp_column= 0;
|
||||
if (m/${sl}timestamp\s*\(\d+\)/i) { # fix for timestamps with width spec not handled (ID: 1628)
|
||||
warn "WARNING for in table '$table' '$_': your default timestamp width is being ignored for table $table \n";
|
||||
s/($sl)timestamp(?:\(\d+\))/$1datetime/i;
|
||||
}
|
||||
} # end timestamp section
|
||||
|
||||
# KEY AND UNIQUE CREATIONS
|
||||
#
|
||||
# unique
|
||||
if ( /^\s+unique\s+\(([^(]+)\)/i ) { # example UNIQUE `name` (`name`), same as UNIQUE KEY
|
||||
# POSTGRESQL: treat same as mysql unique
|
||||
$quoted_column = quote_and_lc($1);
|
||||
s/\s+unique\s+\(([^(]+)\)/ unique ($quoted_column) /i;
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
} elsif ( /^\s+unique\s+key\s*(\w+)\s*\(([^(]+)\)/i ) { # example UNIQUE KEY `name` (`name`)
|
||||
# MYSQL: unique key: allows null=YES, allows duplicates=NO (*)
|
||||
# ... new ... UNIQUE KEY `unique_fullname` (`fullname`) in my mysql v. Ver 14.12 Distrib 5.1.7-beta
|
||||
# POSTGRESQL: treat same as mysql unique
|
||||
# just quote columns
|
||||
$quoted_column = quote_and_lc($2);
|
||||
s/\s+unique\s+key\s*(\w+)\s*\(([^(]+)\)/ unique ($quoted_column) /i;
|
||||
$create_sql.=$_;
|
||||
# the index corresponding to the 'key' is automatically created
|
||||
next;
|
||||
}
|
||||
# keys
|
||||
if ( /^\s+fulltext key\s+/i) { # example: FULLTEXT KEY `commenttext` (`commenttext`)
|
||||
# that is key as a word in the first check for a match
|
||||
# the tsvector datatype is made for these types of things
|
||||
# example mysql file:
|
||||
# what is tsvector datatype?
|
||||
# http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/docs/tsearch-V2-intro.html
|
||||
warn "dba must do fulltext key transformation for $table\n";
|
||||
next;
|
||||
}
|
||||
if ( /^(\s+)constraint (\S+) foreign key \((\S+)\) references (\S+) \((\S+)\)(.*)/i ) {
|
||||
$quoted_column =quote_and_lc($3);
|
||||
$col=quote_and_lc($5);
|
||||
$post_create_sql .= "ALTER TABLE $table ADD FOREIGN KEY ($quoted_column) REFERENCES " . quote_and_lc($4) . " ($col);\n";
|
||||
next;
|
||||
}
|
||||
if ( /^\s*primary key\s*\(([^)]+)\)([,\s]+)/i ) { # example PRIMARY KEY (`name`)
|
||||
# MYSQL: primary key: allows null=NO , allows duplicates=NO
|
||||
# POSTGRESQL: When an index is declared unique, multiple table rows with equal indexed values will not be
|
||||
# allowed. Null values are not considered equal.
|
||||
# POSTGRESQL quote's source: 8.1.3 docs section 11.5 "unique indexes"
|
||||
# so, in postgres, we need to add a NOT NULL to the UNIQUE constraint
|
||||
# and, primary key (mysql) == primary key (postgres) so that we *really* don't need change anything
|
||||
$quoted_column = quote_and_lc($1);
|
||||
s/(\s*)primary key\s+\(([^)]+)\)([,\s]+)/$1 primary key ($quoted_column)$3/i;
|
||||
# indexes are automatically created for unique columns
|
||||
$create_sql.=$_;
|
||||
next;
|
||||
} elsif (m/^\s+key\s[-_\s\w]+\((.+)\)/i ) { # example: KEY `idx_mod_english_def_word` (`word`),
|
||||
# regular key: allows null=YES, allows duplicates=YES
|
||||
# MYSQL: KEY is normally a synonym for INDEX. http://dev.mysql.com/doc/refman/5.1/en/create-table.html
|
||||
#
|
||||
# * MySQL: ALTER TABLE {$table} ADD KEY $column ($column)
|
||||
# * PostgreSQL: CREATE INDEX {$table}_$column_idx ON {$table}($column) // Please note the _idx "extension"
|
||||
# PRIMARY KEY (`postid`),
|
||||
# KEY `ownerid` (`ownerid`)
|
||||
# create an index for everything which has a key listed for it.
|
||||
my $col = $1;
|
||||
# TODO we don't have a translation for the substring syntax in text columns in MySQL (e.g. "KEY my_idx (mytextcol(20))")
|
||||
# for now just getting rid of the brackets and numbers (the substring specifier):
|
||||
$col=~s/\(\d+\)//g;
|
||||
$quoted_column = quote_and_lc($col);
|
||||
if ($col =~ m/,/) {
|
||||
$col = s/,/_/;
|
||||
}
|
||||
$index = get_identifier($table, $col, 'idx');
|
||||
$post_create_sql.="CREATE INDEX $index ON $table USING btree ($quoted_column)\;";
|
||||
# just create index do not add to create table statement
|
||||
next;
|
||||
}
|
||||
|
||||
# handle 'key' declared at end of column
|
||||
if (/\w+.*primary key/i) { # mysql: key is normally just a synonym for index
|
||||
# just leave as is ( postgres has primary key type)
|
||||
|
||||
|
||||
} elsif (/(\w+\s+(?:$mysql_datatypesStr)\s+.*)key/i) { # mysql: key is normally just a synonym for index
|
||||
# I can't find a reference for 'key' in a postgres command without using the word 'primary key'
|
||||
s/$1key/$1/i ;
|
||||
$index = get_identifier($table, $1, 'idx');
|
||||
$quoted_column =quote_and_lc($1);
|
||||
$post_create_sql.="CREATE INDEX $index ON $table USING btree ($quoted_column) \;";
|
||||
$create_sql.=$_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
# do we really need this anymore?
|
||||
# remap colums with names of existing system attribute
|
||||
if (/"oid"/i) {
|
||||
s/"oid"/"_oid"/g;
|
||||
print STDERR "WARNING: table $table uses column \"oid\" which is renamed to \"_oid\"\nYou should fix application manually! Press return to continue.";
|
||||
my $wait=<STDIN>;
|
||||
}
|
||||
|
||||
s/oid/_oid/i if (/key/i && /oid/i); # fix oid in key
|
||||
|
||||
# FINAL QUOTING OF ALL COLUMNS
|
||||
# quote column names which were not already quoted
|
||||
# perhaps they were not quoted because they were not explicitly handled
|
||||
if (!/^\s*"(\w+)"(\s+)/i) {
|
||||
/^(\s*)(\w+)(\s+)(.*)$/i ;
|
||||
$quoted_column= quote_and_lc($2);
|
||||
s/^(\s*)(\w+)(\s+)(.*)$/$1 $quoted_column $3 $4 /;
|
||||
}
|
||||
$create_sql.=$_;
|
||||
# END of if ($create_sql ne "") i.e. were inside create table statement so processed datatypes
|
||||
}
|
||||
# add "not in create table" comments or empty lines to pre_create_sql
|
||||
elsif (/^#/ || /^$/ || /^\s*--/) {
|
||||
s/^#/--/; # Two hyphens (--) is the SQL-92 standard indicator for comments
|
||||
$pre_create_sql .= $_ ; # printed above create table statement
|
||||
next;
|
||||
}
|
||||
elsif (/^\s*insert into/i) { # not inside create table and doing insert
|
||||
# fix mysql's zero/null value for timestamps
|
||||
s/'0000-00-00/'1970-01-01/gi;
|
||||
# commented out to fix bug "Field contents interpreted as a timestamp", what was the point of this line anyway?
|
||||
#s/([12]\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-6]\d)([0-6]\d)/'$1-$2-$3 $4:$5:$6'/;
|
||||
|
||||
#---- fix data in inserted data: (from MS world)
|
||||
s!\x96!-!g; # --
|
||||
s!\x93!"!g; # ``
|
||||
s!\x94!"!g; # ''
|
||||
s!\x85!... !g; # \ldots
|
||||
s!\x92!`!g;
|
||||
|
||||
print OUT $pre_create_sql; # print comments preceding the insert section
|
||||
$pre_create_sql="";
|
||||
$auto_increment_seq = "";
|
||||
|
||||
s/'((?:[^'\\]++|\\.)*+)'(?=[),])/E'$1'/g;
|
||||
# for the E'' see http://www.postgresql.org/docs/8.2/interactive/release-8-1.html
|
||||
s!\\\\!\\\\\\\\!g; # replace \\ with ]\\\\
|
||||
|
||||
# split 'extended' INSERT INTO statements to something PostgreSQL can understand
|
||||
( $insert_table, $valueString) = $_ =~ m/^INSERT\s+INTO\s+['`"]*(.*?)['`"]*\s+VALUES\s*(.*)/i;
|
||||
$insert_table = quote_and_lc($insert_table);
|
||||
|
||||
s/^INSERT INTO.*?\);//i; # hose the statement which is to be replaced whether a run-on or not
|
||||
# guarantee table names are quoted
|
||||
print OUT qq(INSERT INTO $insert_table VALUES $valueString \n);
|
||||
|
||||
} else {
|
||||
print OUT $_ ; # example: /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
}
|
||||
# keep looping and get next line of IN file
|
||||
|
||||
} # END while(<IN>)
|
||||
|
||||
print_post_create_sql(); # in case there is extra from the last table
|
||||
|
||||
#################################################################
|
||||
# 5. print_plgsql function prototype
|
||||
# emulate the set datatype with the following plpgsql function
|
||||
# looks ugly so putting at end of file
|
||||
#################################################################
|
||||
#
|
||||
sub make_plpgsql {
|
||||
my ($table,$column_name) = ($_[0],$_[1]);
|
||||
$table=~s/\"//g; # make sure that $table doesn't have quotes so we don't end up with redundant quoting
|
||||
my $constraint_table = get_identifier($table,$column_name ,"constraint_table");
|
||||
return "
|
||||
-- this function is called by the insert/update trigger
|
||||
-- it checks if the INSERT/UPDATE for the 'set' column
|
||||
-- contains members which comprise a valid mysql set
|
||||
-- this TRIGGER function therefore acts like a constraint
|
||||
-- provided limited functionality for mysql's set datatype
|
||||
-- just verifies and matches for string representations of the set at this point
|
||||
-- though the set datatype uses bit comparisons, the only supported arguments to our
|
||||
-- set datatype are VARCHAR arguments
|
||||
-- to add a member to the set add it to the ".$table."_".$column_name." table
|
||||
CREATE OR REPLACE FUNCTION check_".$table."_".$column_name."_set( ) RETURNS TRIGGER AS \$\$\n
|
||||
DECLARE
|
||||
----
|
||||
arg_str VARCHAR ;
|
||||
argx VARCHAR := '';
|
||||
nobreak INT := 1;
|
||||
rec_count INT := 0;
|
||||
psn INT := 0;
|
||||
str_in VARCHAR := NEW.$column_name;
|
||||
----
|
||||
BEGIN
|
||||
----
|
||||
IF str_in IS NULL THEN RETURN NEW ; END IF;
|
||||
arg_str := REGEXP_REPLACE(str_in, '\\',\\'', ','); -- str_in is CONSTANT
|
||||
arg_str := REGEXP_REPLACE(arg_str, '^\\'', '');
|
||||
arg_str := REGEXP_REPLACE(arg_str, '\\'\$', '');
|
||||
-- RAISE NOTICE 'arg_str %',arg_str;
|
||||
psn := POSITION(',' in arg_str);
|
||||
IF psn > 0 THEN
|
||||
psn := psn - 1; -- minus-1 from comma position
|
||||
-- RAISE NOTICE 'psn %',psn;
|
||||
argx := SUBSTRING(arg_str FROM 1 FOR psn); -- get one set member
|
||||
psn := psn + 2; -- go to first starting letter
|
||||
arg_str := SUBSTRING(arg_str FROM psn); -- hack it off
|
||||
ELSE
|
||||
psn := 0; -- minus-1 from comma position
|
||||
argx := arg_str;
|
||||
END IF;
|
||||
-- RAISE NOTICE 'argx %',argx;
|
||||
-- RAISE NOTICE 'new arg_str: %',arg_str;
|
||||
WHILE nobreak LOOP
|
||||
EXECUTE 'SELECT count(*) FROM $constraint_table WHERE set_values = ' || quote_literal(argx) INTO rec_count;
|
||||
IF rec_count = 0 THEN RAISE EXCEPTION 'one of the set values was not found';
|
||||
END IF;
|
||||
IF psn > 0 THEN
|
||||
psn := psn - 1; -- minus-1 from comma position
|
||||
-- RAISE NOTICE 'psn %',psn;
|
||||
argx := SUBSTRING(arg_str FROM 1 FOR psn); -- get one set member
|
||||
psn := psn + 2; -- go to first starting letter
|
||||
arg_str := SUBSTRING(arg_str FROM psn); -- hack it off
|
||||
psn := POSITION(',' in arg_str);
|
||||
ELSE nobreak = 0;
|
||||
END IF;
|
||||
-- RAISE NOTICE 'next argx % and next arg_str %', argx, arg_str;
|
||||
END LOOP;
|
||||
RETURN NEW;
|
||||
----
|
||||
END;
|
||||
\$\$ LANGUAGE 'plpgsql' VOLATILE;
|
||||
|
||||
drop trigger set_test ON $table;
|
||||
-- make a trigger for each set field
|
||||
-- make trigger and hard-code in column names
|
||||
-- see http://archives.postgresql.org/pgsql-interfaces/2005-02/msg00020.php
|
||||
CREATE TRIGGER set_test
|
||||
BEFORE INSERT OR UPDATE ON $table FOR EACH ROW
|
||||
EXECUTE PROCEDURE check_".$table."_".$column_name."_set();\n";
|
||||
} # end sub make_plpgsql();
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
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
|
||||
|
@@ -1,195 +0,0 @@
|
||||
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
|
||||
@@ -1,200 +0,0 @@
|
||||
|
||||
## 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
@@ -5,23 +5,27 @@
|
||||
|
||||
configure_file(mkdocs.yml ../mkdocs.yml)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data-sources)
|
||||
|
||||
set (DOC_SOURCES
|
||||
admin
|
||||
develop
|
||||
api
|
||||
index.md
|
||||
extra.css
|
||||
styles.css
|
||||
)
|
||||
|
||||
foreach (src ${DOC_SOURCES})
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/${src} ${CMAKE_CURRENT_BINARY_DIR}/${src}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
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-Centos-8.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Centos-8.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 ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-20.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-20.md
|
||||
COMMAND mkdocs build -d ${CMAKE_CURRENT_BINARY_DIR}/../site-html -f ${CMAKE_CURRENT_BINARY_DIR}/../mkdocs.yml
|
||||
)
|
||||
|
||||
|
||||
172
docs/admin/Advanced-Installations.md
Normal file
172
docs/admin/Advanced-Installations.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Advanced installations
|
||||
|
||||
This page contains instructions for setting up multiple countries in
|
||||
your Nominatim database. It is assumed that you have already successfully
|
||||
installed the Nominatim software itself, if not return to the
|
||||
[installation page](Installation.md).
|
||||
|
||||
## Importing multiple regions
|
||||
|
||||
To import multiple regions in your database, you need to configure and run `utils/import_multiple_regions.sh` file. This script will set up the update directory which has the following structure:
|
||||
|
||||
```bash
|
||||
update
|
||||
├── europe
|
||||
│ ├── andorra
|
||||
│ │ └── sequence.state
|
||||
│ └── monaco
|
||||
│ └── sequence.state
|
||||
└── tmp
|
||||
├── combined.osm.pbf
|
||||
└── europe
|
||||
├── andorra-latest.osm.pbf
|
||||
└── monaco-latest.osm.pbf
|
||||
|
||||
|
||||
```
|
||||
|
||||
The `sequence.state` files will contain the sequence ID, which will be used by pyosmium to get updates. The tmp folder is used for import dump.
|
||||
|
||||
### Configuring multiple regions
|
||||
|
||||
The file `import_multiple_regions.sh` needs to be edited as per your requirement:
|
||||
|
||||
1. List of countries. eg:
|
||||
|
||||
COUNTRIES="europe/monaco europe/andorra"
|
||||
|
||||
2. Path to Build directory. eg:
|
||||
|
||||
NOMINATIMBUILD="/srv/nominatim/build"
|
||||
|
||||
3. Path to Update directory. eg:
|
||||
|
||||
UPDATEDIR="/srv/nominatim/update"
|
||||
|
||||
4. Replication URL. eg:
|
||||
|
||||
BASEURL="https://download.geofabrik.de"
|
||||
DOWNCOUNTRYPOSTFIX="-latest.osm.pbf"
|
||||
|
||||
!!! tip
|
||||
If your database already exists and you want to add more countries, replace the setting up part
|
||||
`${SETUPFILE} --osm-file ${UPDATEDIR}/tmp/combined.osm.pbf --all 2>&1`
|
||||
with `${UPDATEFILE} --import-file ${UPDATEDIR}/tmp/combined.osm.pbf --index --index-instances N 2>&1`
|
||||
where N is the numbers of CPUs in your system.
|
||||
|
||||
### Setting up multiple regions
|
||||
|
||||
Run the following command from your Nominatim directory after configuring the file.
|
||||
|
||||
bash ./utils/import_multiple_regions.sh
|
||||
|
||||
!!! danger "Important"
|
||||
This file uses osmium-tool. It must be installed before executing the import script.
|
||||
Installation instructions can be found [here](https://osmcode.org/osmium-tool/manual.html#installation).
|
||||
|
||||
### Updating multiple regions
|
||||
|
||||
To import multiple regions in your database, you need to configure and run ```utils/update_database.sh```.
|
||||
This uses the update directory set up while setting up the DB.
|
||||
|
||||
### Configuring multiple regions
|
||||
|
||||
The file `update_database.sh` needs to be edited as per your requirement:
|
||||
|
||||
1. List of countries. eg:
|
||||
|
||||
COUNTRIES="europe/monaco europe/andorra"
|
||||
|
||||
2. Path to Build directory. eg:
|
||||
|
||||
NOMINATIMBUILD="/srv/nominatim/build"
|
||||
|
||||
3. Path to Update directory. eg:
|
||||
|
||||
UPDATEDIR="/srv/nominatim/update"
|
||||
|
||||
4. Replication URL. eg:
|
||||
|
||||
BASEURL="https://download.geofabrik.de"
|
||||
DOWNCOUNTRYPOSTFIX="-updates"
|
||||
|
||||
5. Followup can be set according to your installation. eg: For Photon,
|
||||
|
||||
FOLLOWUP="curl http://localhost:2322/nominatim-update"
|
||||
|
||||
will handle the indexing.
|
||||
|
||||
### Updating the database
|
||||
|
||||
Run the following command from your Nominatim directory after configuring the file.
|
||||
|
||||
bash ./utils/update_database.sh
|
||||
|
||||
This will get diffs from the replication server, import diffs and index the database. The default replication server in the script([Geofabrik](https://download.geofabrik.de)) provides daily updates.
|
||||
|
||||
## Importing Nominatim to an external PostgreSQL database
|
||||
|
||||
You can install Nominatim using a database that runs on a different server when
|
||||
you have physical access to the file system on the other server. Nominatim
|
||||
uses a custom normalization library that needs to be made accessible to the
|
||||
PostgreSQL server. This section explains how to set up the normalization
|
||||
library.
|
||||
|
||||
### Option 1: Compiling the library on the database server
|
||||
|
||||
The most sure way to get a working library is to compile it on the database
|
||||
server. From the prerequisites you need at least cmake, gcc and the
|
||||
PostgreSQL server package.
|
||||
|
||||
Clone or unpack the Nominatim source code, enter the source directory and
|
||||
create and enter a build directory.
|
||||
|
||||
```sh
|
||||
cd Nominatim
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
|
||||
Now configure cmake to only build the PostgreSQL module and build it:
|
||||
|
||||
```
|
||||
cmake -DBUILD_IMPORTER=off -DBUILD_API=off -DBUILD_TESTS=off -DBUILD_DOCS=off -DBUILD_OSM2PGSQL=off ..
|
||||
make
|
||||
```
|
||||
|
||||
When done, you find the normalization library in `build/module/nominatim.so`.
|
||||
Copy it to a place where it is readable and executable by the PostgreSQL server
|
||||
process.
|
||||
|
||||
### Option 2: Compiling the library on the import machine
|
||||
|
||||
You can also compile the normalization library on the machine from where you
|
||||
run the import.
|
||||
|
||||
!!! important
|
||||
You can only do this when the database server and the import machine have
|
||||
the same architecture and run the same version of Linux. Otherwise there is
|
||||
no guarantee that the compiled library is compatible with the PostgreSQL
|
||||
server running on the database server.
|
||||
|
||||
Make sure that the PostgreSQL server package is installed on the machine
|
||||
**with the same version as on the database server**. You do not need to install
|
||||
the PostgreSQL server itself.
|
||||
|
||||
Download and compile Nominatim as per standard instructions. Once done, you find
|
||||
the nomrmalization library in `build/module/nominatim.so`. Copy the file to
|
||||
the database server at a location where it is readable and executable by the
|
||||
PostgreSQL server process.
|
||||
|
||||
### Running the import
|
||||
|
||||
On the client side you now need to configure the import to point to the
|
||||
correct location of the library **on the database server**. Add the following
|
||||
line to your your `settings/local.php` file:
|
||||
|
||||
```php
|
||||
@define('CONST_Database_Module_Path', '<directory on the database server where nominatim.so resides>');
|
||||
```
|
||||
|
||||
Now change the `CONST_Database_DSN` to point to your remote server and continue
|
||||
to follow the [standard instructions for importing](/admin/Import).
|
||||
141
docs/admin/Deployment.md
Normal file
141
docs/admin/Deployment.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Deploying Nominatim
|
||||
|
||||
The Nominatim API is implemented as a PHP application. The `website/` directory
|
||||
in the build directory contains the configured website. You can serve this
|
||||
in a production environment with any web server that is capable to run
|
||||
PHP scripts.
|
||||
|
||||
This section gives a quick overview on how to configure Apache and Nginx to
|
||||
serve Nominatim. It is not meant as a full system administration guide on how
|
||||
to run a web service. Please refer to the documentation of
|
||||
[Apache](http://httpd.apache.org/docs/current/) and
|
||||
[Nginx](https://nginx.org/en/docs/)
|
||||
for background information on configuring the services.
|
||||
|
||||
!!! Note
|
||||
Throughout this page, we assume that your Nominatim build directory is
|
||||
located in `/srv/nominatim/build` and the source code in
|
||||
`/srv/nominatim/Nominatim`. If you have put it somewhere else, you
|
||||
need to adjust the commands and configuration accordingly.
|
||||
|
||||
We further assume that your web server runs as user `www-data`. Older
|
||||
versions of CentOS may still use the user name `apache`. You also need
|
||||
to adapt the instructions in this case.
|
||||
|
||||
## Making the website directory accessible
|
||||
|
||||
You need to make sure that the `website` directory is accessible for the
|
||||
web server user. You can check that the permissions are correct by accessing
|
||||
on of the php files as the web server user:
|
||||
|
||||
``` sh
|
||||
sudo -u www-data head -n 1 /srv/nominatim/build/website/search.php
|
||||
```
|
||||
|
||||
If this shows a permission error, then you need to adapt the permissions of
|
||||
each directory in the path so that it is executable for `www-data`.
|
||||
|
||||
If you have SELinux enabled, further adjustments may be necessary to give the
|
||||
web server access. At a minimum the following SELinux labelling should be done
|
||||
for Nominatim:
|
||||
|
||||
``` sh
|
||||
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/nominatim/Nominatim/(website|lib|settings)(/.*)?"
|
||||
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/nominatim/build/(website|settings)(/.*)?"
|
||||
sudo semanage fcontext -a -t lib_t "/srv/nominatim/build/module/nominatim.so"
|
||||
sudo restorecon -R -v /srv/nominatim/Nominatim
|
||||
sudo restorecon -R -v /srv/nominatim/build
|
||||
```
|
||||
|
||||
## Nominatim with Apache
|
||||
|
||||
### Installing the required packages
|
||||
|
||||
With Apache you can use the PHP module to run Nominatim.
|
||||
|
||||
Under Ubuntu/Debian install them with:
|
||||
|
||||
``` sh
|
||||
sudo apt install apache2 libapache2-mod-php
|
||||
```
|
||||
|
||||
### Configuring Apache
|
||||
|
||||
Make sure your Apache configuration contains the required permissions for the
|
||||
directory and create an alias:
|
||||
|
||||
``` apache
|
||||
<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
|
||||
```
|
||||
|
||||
After making changes in the apache config you need to restart apache.
|
||||
The website should now be available on `http://localhost/nominatim`.
|
||||
|
||||
## Nominatim with Nginx
|
||||
|
||||
### Installing the required packages
|
||||
|
||||
Nginx has no built-in PHP interpreter. You need to use php-fpm as a deamon for
|
||||
serving PHP cgi.
|
||||
|
||||
On Ubuntu/Debian install nginx and php-fpm with:
|
||||
|
||||
``` sh
|
||||
sudo apt install nginx php-fpm
|
||||
```
|
||||
|
||||
### Configure php-fpm and Nginx
|
||||
|
||||
By default php-fpm listens on a network socket. If you want it to listen to a
|
||||
Unix socket instead, change the pool configuration
|
||||
(`/etc/php/<php version>/fpm/pool.d/www.conf`) as follows:
|
||||
|
||||
``` ini
|
||||
; Replace the tcp listener and add the unix socket
|
||||
listen = /var/run/php-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.
|
||||
|
||||
``` nginx
|
||||
root /srv/nominatim/build/website;
|
||||
index search.php;
|
||||
location / {
|
||||
try_files $uri $uri/ @php;
|
||||
}
|
||||
|
||||
location @php {
|
||||
fastcgi_param SCRIPT_FILENAME "$document_root$uri.php";
|
||||
fastcgi_param PATH_TRANSLATED "$document_root$uri.php";
|
||||
fastcgi_param QUERY_STRING $args;
|
||||
fastcgi_pass unix:/var/run/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
location ~ [^/]\.php(/|$) {
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
if (!-f $document_root$fastcgi_script_name) {
|
||||
return 404;
|
||||
}
|
||||
fastcgi_pass unix:/var/run/php-fpm.sock;
|
||||
fastcgi_index search.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
```
|
||||
|
||||
Restart the nginx and php-fpm services and the website should now be available
|
||||
at `http://localhost/`.
|
||||
|
||||
@@ -22,21 +22,38 @@ then you can resume with the following command:
|
||||
If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`.
|
||||
|
||||
|
||||
### PostgreSQL crashed "invalid page in block"
|
||||
|
||||
Usually serious problem, can be a hardware issue, not all data written to disc
|
||||
for example. Check PostgreSQL log file and search PostgreSQL issues/mailing
|
||||
list for hints.
|
||||
|
||||
If it happened during index creation you can try rerunning the step with
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-search-indices --ignore-errors
|
||||
```
|
||||
|
||||
Otherwise it's best to start the full setup from the beginning.
|
||||
|
||||
|
||||
### 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:
|
||||
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.
|
||||
Either add reported directories to the list or disable this setting temporarily
|
||||
by adding ";" 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
|
||||
### PHP timezeone warnings
|
||||
|
||||
The Apache log may contain lots of PHP warnings like this:
|
||||
`PHP Warning: date_default_timezone_set() function.`
|
||||
@@ -66,7 +83,7 @@ server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
|
||||
and recompile (`cmake .. && make`).
|
||||
|
||||
|
||||
## I see the error "ERROR: permission denied for language c"
|
||||
### 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
|
||||
@@ -89,6 +106,11 @@ 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.
|
||||
|
||||
### I see the error: "clang: Command not found" on CentOS
|
||||
|
||||
On CentOS 7 users reported `/opt/rh/llvm-toolset-7/root/usr/bin/clang: Command not found`.
|
||||
Double-check clang is installed. Instead of `make` try running `make CLANG=true`.
|
||||
|
||||
### 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
|
||||
@@ -107,10 +129,11 @@ to get the full error message.
|
||||
|
||||
`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).
|
||||
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
|
||||
|
||||
@@ -118,14 +141,12 @@ However, you can solve this the quick and dirty way by commenting out that line
|
||||
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`.
|
||||
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.
|
||||
|
||||
@@ -150,7 +171,8 @@ Example error message
|
||||
|
||||
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.
|
||||
The permission need to be read & executable by everybody, but not writeable
|
||||
by everybody, e.g.
|
||||
|
||||
```
|
||||
-rwxr-xr-x 1 nominatim nominatim 297984 build/module/nominatim.so
|
||||
@@ -161,22 +183,22 @@ 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).
|
||||
|
||||
When you recently updated your operating system, updated PostgreSQL to
|
||||
a new version or moved files (e.g. the build directory) you should
|
||||
recreate `nominatim.so`. Try
|
||||
|
||||
```
|
||||
cd build
|
||||
rm -r module/
|
||||
cmake $main_Nominatim_path && make
|
||||
```
|
||||
|
||||
### 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.
|
||||
See the installation instructions 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.
|
||||
@@ -186,22 +208,6 @@ 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).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Importing and Updating the Database
|
||||
# Importing 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
|
||||
@@ -29,11 +29,11 @@ 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.
|
||||
the directory exists. There should be at least 75GB of free space.
|
||||
|
||||
## Downloading additional data
|
||||
|
||||
### Wikipedia rankings
|
||||
### Wikipedia/Wikidata 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
|
||||
@@ -41,15 +41,15 @@ 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
|
||||
wget https://www.nominatim.org/data/wikimedia-importance.sql.gz
|
||||
|
||||
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.
|
||||
The file is about 400MB and adds around 4GB to the Nominatim database.
|
||||
|
||||
*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.
|
||||
!!! tip
|
||||
If you forgot to download the wikipedia rankings, you can also add
|
||||
importances after the import. Download the files, then run
|
||||
`./utils/setup.php --import-wikipedia-articles`
|
||||
and `./utils/update.php --recompute-importance`.
|
||||
|
||||
### Great Britain, USA postcodes
|
||||
|
||||
@@ -60,21 +60,21 @@ involve a GB or US postcode. This data can be optionally downloaded:
|
||||
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
|
||||
## 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
|
||||
at least 64GB of RAM and around 900GB 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
|
||||
If you only need geocoding for a smaller region, then precomputed OSM 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
|
||||
[in the next section](../Update). 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
|
||||
@@ -120,47 +120,116 @@ import styles available which only read selected data:
|
||||
Import all data necessary to compute addresses down to house number level.
|
||||
* **settings/import-full.style**
|
||||
Default style that also includes points of interest.
|
||||
* **settings/import-extratags.style**
|
||||
Like the full style but also adds most of the OSM tags into the extratags
|
||||
column.
|
||||
|
||||
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.
|
||||
2020 planet and after using the `--drop` option. It also shows the time
|
||||
needed for the import on a machine with 64GB RAM, 4 CPUS and NVME disks.
|
||||
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
|
||||
admin | 4h | 215 GB | 20 GB
|
||||
street | 22h | 440 GB | 185 GB
|
||||
address | 36h | 545 GB | 260 GB
|
||||
full | 54h | 640 GB | 330 GB
|
||||
extratags | 54h | 650 GB | 340 GB
|
||||
|
||||
You can also customize the styles further. For an description of the
|
||||
style format see [the development section](../develop/Import.md).
|
||||
You can also customize the styles further.
|
||||
A [description of the style format](../develop/Import.md#configuring-the-import)
|
||||
can be found in the development section.
|
||||
|
||||
## Initial import of the data
|
||||
|
||||
**Important:** first try the import with a small extract, for example from
|
||||
[Geofabrik](https://download.geofabrik.de).
|
||||
!!! danger "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:
|
||||
Download the data to import. Then issue the following command
|
||||
from the **build directory** to start the import:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --osm-file <data file> --all [--osm2pgsql-cache 28000] 2>&1 | tee setup.log
|
||||
./utils/setup.php --osm-file <data file> --all 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.
|
||||
### Notes on full planet imports
|
||||
|
||||
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:
|
||||
Even on a perfectly configured machine
|
||||
the import of a full planet takes around 2 days. Once you see messages
|
||||
with `Rank .. ETA` appear, the indexing process has started. This part takes
|
||||
the most time. There are 30 ranks to process. Rank 26 and 30 are the most complex.
|
||||
They take each about a third of the total import time. If you have not reached
|
||||
rank 26 after two days of import, it is worth revisiting your system
|
||||
configuration as it may not be optimal for the import.
|
||||
|
||||
### Notes on memory usage
|
||||
|
||||
In the first step of the import Nominatim uses [osm2pgsql](https://osm2pgsql.org)
|
||||
to load the OSM data into the PostgreSQL database. This step is very demanding
|
||||
in terms of RAM usage. osm2pgsql and PostgreSQL are running in parallel at
|
||||
this point. PostgreSQL blocks at least the part of RAM that has been configured
|
||||
with the `shared_buffers` parameter during
|
||||
[PostgreSQL tuning](Installation#postgresql-tuning)
|
||||
and needs some memory on top of that. osm2pgsql needs at least 2GB of RAM for
|
||||
its internal data structures, potentially more when it has to process very large
|
||||
relations. In addition it needs to maintain a cache for node locations. The size
|
||||
of this cache can be configured with the parameter `--osm2pgsql-cache`.
|
||||
|
||||
When importing with a flatnode file, it is best to disable the node cache
|
||||
completely and leave the memory for the flatnode file. Nominatim will do this
|
||||
by default, so you do not need to configure anything in this case.
|
||||
|
||||
For imports without a flatnode file, set `--osm2pgsql-cache` approximately to
|
||||
the size of the OSM pbf file you are importing. The size needs to be given in
|
||||
MB. Make sure you leave enough RAM for PostgreSQL and osm2pgsql as mentioned
|
||||
above. If the system starts swapping or you are getting out-of-memory errors,
|
||||
reduce the cache size or even consider using a flatnode file.
|
||||
|
||||
### Verify the import
|
||||
|
||||
Run this script to verify all required tables and indices got created successfully.
|
||||
|
||||
```sh
|
||||
./utils/check_import_finished.php
|
||||
```
|
||||
|
||||
### Setting up the website
|
||||
|
||||
Run the following command to set up the configuration file for the API frontend
|
||||
`settings/settings-frontend.php`. These settings are used in website/*.php files.
|
||||
|
||||
```sh
|
||||
./utils/setup.php --setup-website
|
||||
```
|
||||
!!! Note
|
||||
This step is not necessary if you use `--all` option while setting up the DB.
|
||||
|
||||
Now you can try out your installation by running:
|
||||
|
||||
```sh
|
||||
make serve
|
||||
```
|
||||
|
||||
This runs a small test server normally used for development. You can use it
|
||||
to verify that your installation is working. Go to
|
||||
`http://localhost:8088/status.php` and you should see the message `OK`.
|
||||
You can also run a search query, e.g. `http://localhost:8088/search.php?q=Berlin`.
|
||||
|
||||
To run Nominatim via webservers like Apache or nginx, please read the
|
||||
[Deployment chapter](Deployment.md).
|
||||
|
||||
## Tuning the database
|
||||
|
||||
Accurate word frequency information for search terms helps PostgreSQL's query
|
||||
planner to make the right decisions. Recomputing them can improve the performance
|
||||
of forward geocoding in particular under high load. To recompute word counts run:
|
||||
|
||||
```sh
|
||||
./utils/update.php --recompute-word-counts
|
||||
@@ -178,12 +247,13 @@ 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.
|
||||
Note that this command downloads the phrases from the wiki link above. You
|
||||
need internet access for the step.
|
||||
|
||||
|
||||
## 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)
|
||||
Nominatim is able to use the official [TIGER](https://www.census.gov/geographies/mapping-files/time-series/geo/tiger-line-file.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.
|
||||
@@ -195,8 +265,6 @@ entire US adds about 10GB to your database.
|
||||
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
|
||||
@@ -212,65 +280,6 @@ entire US adds about 10GB to your database.
|
||||
```
|
||||
|
||||
|
||||
## 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).
|
||||
See the [developer's guide](../develop/data-sources.md#us-census-tiger) for more
|
||||
information on how the data got preprocessed.
|
||||
|
||||
@@ -4,8 +4,9 @@ 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 20.04](../appendix/Install-on-Ubuntu-20.md)
|
||||
* [Ubuntu 18.04](../appendix/Install-on-Ubuntu-18.md)
|
||||
* [Ubuntu 16.04](../appendix/Install-on-Ubuntu-16.md)
|
||||
* [CentOS 8](../appendix/Install-on-Centos-8.md)
|
||||
* [CentOS 7.2](../appendix/Install-on-Centos-7.md)
|
||||
|
||||
These OS-specific instructions can also be found in executable form
|
||||
@@ -25,65 +26,74 @@ and can't offer support.
|
||||
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)
|
||||
* [expat](https://libexpat.github.io/)
|
||||
* [proj](https://proj.org/)
|
||||
* [bzip2](http://www.bzip.org/)
|
||||
* [zlib](https://www.zlib.net/)
|
||||
* [Boost libraries](https://www.boost.org/), including system and filesystem
|
||||
* PostgreSQL client libraries
|
||||
* a recent C++ compiler (gcc 5+ or Clang 3.8+)
|
||||
|
||||
For running Nominatim:
|
||||
|
||||
* [PostgreSQL](https://www.postgresql.org) (9.3 or later)
|
||||
* [PostGIS](https://postgis.org) (2.2 or later)
|
||||
* [PostgreSQL](https://www.postgresql.org) (9.3+)
|
||||
* [PostGIS](https://postgis.net) (2.2+)
|
||||
* [Python 3](https://www.python.org/)
|
||||
* [Psycopg2](https://www.psycopg.org)
|
||||
* [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)
|
||||
|
||||
For dependencies for running tests and building documentation, see
|
||||
the [Development section](../develop/Development-Environment.md).
|
||||
|
||||
### 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.
|
||||
planet import 64GB of RAM or more are strongly recommended. Do not report
|
||||
out of memory problems if you have less than 64GB RAM.
|
||||
|
||||
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.
|
||||
For a full planet install you will need at least 900GB of hard disk space.
|
||||
Take into account that the OSM database is growing fast.
|
||||
Fast disks are essential. Using NVME disks is recommended.
|
||||
|
||||
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.
|
||||
Even on a well configured machine the import of a full planet takes
|
||||
around 2 days. On traditional spinning disks, 7-8 days are more realistic.
|
||||
|
||||
|
||||
## Setup of the server
|
||||
|
||||
### PostgreSQL tuning
|
||||
## Tuning the PostgreSQL database
|
||||
|
||||
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)
|
||||
shared_buffers = 2GB
|
||||
maintenance_work_mem = (10GB)
|
||||
autovacuum_work_mem = 2GB
|
||||
work_mem = (50MB)
|
||||
effective_cache_size = (24GB)
|
||||
synchronous_commit = off
|
||||
checkpoint_segments = 100 # only for postgresql <= 9.4
|
||||
max_wal_size = 1GB # 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.
|
||||
64GB RAM machine. Adjust to your setup. A higher number for `max_wal_size`
|
||||
means that PostgreSQL needs to run checkpoints less often but it does require
|
||||
the additional space on your disk.
|
||||
|
||||
Autovacuum must not be switched off because it ensures that the
|
||||
tables are frequently analysed. If your machine has very little memory,
|
||||
you might consider setting:
|
||||
|
||||
autovacuum_max_workers = 1
|
||||
|
||||
and even reduce `autovacuum_work_mem` further. This will reduce the amount
|
||||
of memory that autovacuum takes away from the import process.
|
||||
|
||||
For the initial import, you should also set:
|
||||
|
||||
@@ -91,68 +101,47 @@ For the initial import, you should also set:
|
||||
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/`.
|
||||
corruption.
|
||||
|
||||
|
||||
Now continue with [importing the database](Import-and-Update.md).
|
||||
## Downloading and building Nominatim
|
||||
|
||||
### Downloading the latest release
|
||||
|
||||
You can download the [latest release from nominatim.org](https://nominatim.org/downloads/).
|
||||
The release contains all necessary files. Just unpack it.
|
||||
|
||||
### Downloading the latest development version
|
||||
|
||||
If you want to install latest development version from github, make sure to
|
||||
also check out the osm2pgsql subproject:
|
||||
|
||||
```
|
||||
git clone --recursive git://github.com/openstreetmap/Nominatim.git
|
||||
```
|
||||
|
||||
The development version does not include the country grid. Download it separately:
|
||||
|
||||
```
|
||||
wget -O Nominatim/data/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz
|
||||
```
|
||||
|
||||
### Building Nominatim
|
||||
|
||||
The code must be built in a separate directory. Create the directory and
|
||||
change into it.
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
|
||||
Nominatim uses cmake and make for building. Assuming that you have created the
|
||||
build at the same level as the Nominatim source directory run:
|
||||
|
||||
```
|
||||
cmake ../Nominatim
|
||||
make
|
||||
```
|
||||
|
||||
Now continue with [importing the database](Import.md).
|
||||
|
||||
@@ -6,6 +6,105 @@ to newer versions of Nominatim.
|
||||
SQL statements should be executed from the PostgreSQL commandline. Execute
|
||||
`psql nominatim` to enter command line mode.
|
||||
|
||||
## 3.5.0 -> 3.6.0
|
||||
|
||||
### Change of layout of search_name_* tables
|
||||
|
||||
The table need a different index for nearest place lookup. Recreate the
|
||||
indexes using the following shell script:
|
||||
|
||||
```bash
|
||||
for table in `psql -d nominatim -c "SELECT tablename FROM pg_tables WHERE tablename LIKE 'search_name_%'" -tA | grep -v search_name_blank`;
|
||||
do
|
||||
psql -d nominatim -c "DROP INDEX idx_${table}_centroid_place; CREATE INDEX idx_${table}_centroid_place ON ${table} USING gist (centroid) WHERE ((address_rank >= 2) AND (address_rank <= 25)); DROP INDEX idx_${table}_centroid_street; CREATE INDEX idx_${table}_centroid_street ON ${table} USING gist (centroid) WHERE ((address_rank >= 26) AND (address_rank <= 27))";
|
||||
done
|
||||
```
|
||||
|
||||
### Removal of html output
|
||||
|
||||
The debugging UI is no longer directly provided with Nominatim. Instead we
|
||||
now provide a simple Javascript application. Please refer to
|
||||
[Setting up the Nominatim UI](../Setup-Nominatim-UI) for details on how to
|
||||
set up the UI.
|
||||
|
||||
The icons served together with the API responses have been moved to the
|
||||
nominatim-ui project as well. If you want to keep the `icon` field in the
|
||||
response, you need to set `CONST_MapIcon_URL` to the URL of the `/mapicon`
|
||||
directory of nominatim-ui.
|
||||
|
||||
### Change order during indexing
|
||||
|
||||
When reindexing places during updates, there is now a different order used
|
||||
which needs a different database index. Create it with the following SQL command:
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_placex_pendingsector_rank_address
|
||||
ON placex
|
||||
USING BTREE (rank_address, geometry_sector)
|
||||
WHERE indexed_status > 0;
|
||||
```
|
||||
|
||||
You can then drop the old index with:
|
||||
|
||||
```sql
|
||||
DROP INDEX idx_placex_pendingsector;
|
||||
```
|
||||
|
||||
### Unused index
|
||||
|
||||
This index has been unused ever since the query using it was changed two years ago. Saves about 12GB on a planet installation.
|
||||
|
||||
```sql
|
||||
DROP INDEX idx_placex_geometry_reverse_lookupPoint;
|
||||
```
|
||||
|
||||
### Switching to dotenv
|
||||
|
||||
As part of the work changing the configuration format, the configuration for
|
||||
the website is now using a separate configuration file. To create the
|
||||
configuration file, run the following command after updating:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --setup-website
|
||||
```
|
||||
|
||||
## 3.4.0 -> 3.5.0
|
||||
|
||||
### New Wikipedia/Wikidata importance tables
|
||||
|
||||
The `wikipedia_*` tables have a new format that also includes references to
|
||||
Wikidata. You need to update the computation functions and the tables as
|
||||
follows:
|
||||
|
||||
* download the new Wikipedia tables as described in the import section
|
||||
* reimport the tables: `./utils/setup.php --import-wikipedia-articles`
|
||||
* update the functions: `./utils/setup.php --create-functions --enable-diff-updates`
|
||||
* create a new lookup index:
|
||||
```sql
|
||||
CREATE INDEX idx_placex_wikidata
|
||||
ON placex
|
||||
USING BTREE ((extratags -> 'wikidata'))
|
||||
WHERE extratags ? 'wikidata'
|
||||
AND class = 'place'
|
||||
AND osm_type = 'N'
|
||||
AND rank_search < 26;
|
||||
```
|
||||
* compute importance: `./utils/update.php --recompute-importance`
|
||||
|
||||
The last step takes about 10 hours on the full planet.
|
||||
|
||||
Remove one function (it will be recreated in the next step):
|
||||
|
||||
```sql
|
||||
DROP FUNCTION create_country(hstore,character varying);
|
||||
```
|
||||
|
||||
Finally, update all SQL functions:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
## 3.3.0 -> 3.4.0
|
||||
|
||||
### Reorganisation of location_area_country table
|
||||
@@ -23,6 +122,12 @@ CREATE INDEX idx_location_area_country_geometry ON location_area_country USING G
|
||||
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id);
|
||||
```
|
||||
|
||||
Finally, update all SQL functions:
|
||||
|
||||
```sh
|
||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||
```
|
||||
|
||||
## 3.2.0 -> 3.3.0
|
||||
|
||||
### New database connection string (DSN) format
|
||||
@@ -39,7 +144,7 @@ The new format is
|
||||
|
||||
### Natural Earth country boundaries no longer needed as fallback
|
||||
|
||||
```
|
||||
```sql
|
||||
DROP TABLE country_naturalearthdata;
|
||||
```
|
||||
|
||||
@@ -65,27 +170,37 @@ following command:
|
||||
The reverse algorithm has changed and requires new indexes. Run the following
|
||||
SQL statements to create the indexes:
|
||||
|
||||
```
|
||||
```sql
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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:
|
||||
|
||||
```
|
||||
```sql
|
||||
GRANT SELECT ON table country_osm_grid to "www-user";
|
||||
```
|
||||
|
||||
@@ -93,7 +208,7 @@ Replace the `www-user` with the user name of your website server if necessary.
|
||||
|
||||
You can now drop the unused indexes:
|
||||
|
||||
```
|
||||
```sql
|
||||
DROP INDEX idx_placex_reverse_geometry;
|
||||
```
|
||||
|
||||
@@ -122,8 +237,8 @@ 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 (
|
||||
DROP TYPE IF EXISTS nearfeaturecentr CASCADE;
|
||||
CREATE TYPE nearfeaturecentr AS (
|
||||
place_id BIGINT,
|
||||
keywords int[],
|
||||
rank_address smallint,
|
||||
|
||||
185
docs/admin/Setup-Nominatim-UI.md
Normal file
185
docs/admin/Setup-Nominatim-UI.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Setting up the Nominatim UI
|
||||
|
||||
Nominatim is a search API, it does not provide a website interface on its
|
||||
own. [nominatim-ui](https://github.com/osm-search/nominatim-ui) offers a
|
||||
small website for testing your setup and inspecting the database content.
|
||||
|
||||
This section provides a quick start how to use nominatim-ui with your
|
||||
installation. For more details, please also have a look at the
|
||||
[README of nominatim-ui](https://github.com/osm-search/nominatim-ui/blob/master/README.md).
|
||||
|
||||
## Installing nominatim-ui
|
||||
|
||||
nominatim-ui does not need any special installation, just download, configure
|
||||
and run it.
|
||||
|
||||
Clone the source from github:
|
||||
|
||||
git clone https://github.com/osm-search/nominatim-ui
|
||||
|
||||
Copy the example configuration into the right place:
|
||||
|
||||
cd nominatim-ui
|
||||
cp dist/config.example.js dist/config.js
|
||||
|
||||
Now adapt the configuration to your needs. You need at least
|
||||
to change the `Nominatim_API_Endpoint` to point to your Nominatim installation.
|
||||
|
||||
Then you can just test it locally by spinning up a webserver in the `dist`
|
||||
directory. For example, with Python:
|
||||
|
||||
cd nominatim-ui/dist
|
||||
python3 -m http.server 8765
|
||||
|
||||
The website is now available at `http://localhost:8765`.
|
||||
|
||||
## Forwarding searches to nominatim-ui
|
||||
|
||||
Nominatim used to provide the search interface directly by itself when
|
||||
`format=html` was requested. For all endpoints except for `/reverse` and
|
||||
`/lookup` this even used to be the default.
|
||||
|
||||
The following section describes how to set up Apache or nginx, so that your
|
||||
users are forwarded to nominatim-ui when they go to URL that formerly presented
|
||||
the UI.
|
||||
|
||||
### Setting up forwarding in Nginx
|
||||
|
||||
First of all make nominatim-ui available under `/ui` on your webserver:
|
||||
|
||||
``` nginx
|
||||
server {
|
||||
|
||||
# Here is the Nominatim setup as described in the Installation section
|
||||
|
||||
location /ui/ {
|
||||
alias <full path to the nominatim-ui directory>/dist/;
|
||||
index index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we need to find out if a URL should be forwarded to the UI. Add the
|
||||
following `map` commands *outside* the server section:
|
||||
|
||||
``` nginx
|
||||
# Inspect the format parameter in the query arguments. We are interested
|
||||
# if it is set to html or something else or if it is missing completely.
|
||||
map $args $format {
|
||||
default default;
|
||||
~(^|&)format=html(&|$) html;
|
||||
~(^|&)format= other;
|
||||
}
|
||||
|
||||
# Determine from the URI and the format parameter above if forwarding is needed.
|
||||
map $uri/$format $forward_to_ui {
|
||||
default 1; # The default is to forward.
|
||||
~^/ui 0; # If the URI point to the UI already, we are done.
|
||||
~/other$ 0; # An explicit non-html format parameter. No forwarding.
|
||||
~/reverse.*/default 0; # Reverse and lookup assume xml format when
|
||||
~/lookup.*/default 0; # no format parameter is given. No forwarding.
|
||||
}
|
||||
```
|
||||
|
||||
The `$forward_to_ui` parameter can now be used to conditionally forward the
|
||||
calls:
|
||||
|
||||
```
|
||||
# When no endpoint is given, default to search.
|
||||
# Need to add a rewrite so that the rewrite rules below catch it correctly.
|
||||
rewrite ^/$ /search;
|
||||
|
||||
location @php {
|
||||
# fastcgi stuff..
|
||||
if ($forward_to_ui) {
|
||||
rewrite ^(/[^/]*) https://yourserver.com/ui$1.html redirect;
|
||||
}
|
||||
}
|
||||
|
||||
location ~ [^/]\.php(/|$) {
|
||||
# fastcgi stuff..
|
||||
if ($forward_to_ui) {
|
||||
rewrite (.*).php https://yourserver.com/ui$1.html redirect;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Be aware that the rewrite commands are slightly different for URIs with and
|
||||
without the .php suffix.
|
||||
|
||||
Reload nginx and the UI should be available.
|
||||
|
||||
### Setting up forwarding in Apache
|
||||
|
||||
First of all make nominatim-ui available in the `ui/` subdirectory where
|
||||
Nominatim is installed. For example, given you have set up an alias under
|
||||
`nominatim` like this:
|
||||
|
||||
``` apache
|
||||
Alias /nominatim /home/vagrant/build/website
|
||||
```
|
||||
|
||||
you need to insert the following rules for nominatim-ui before that alias:
|
||||
|
||||
```
|
||||
<Directory "/home/vagrant/nominatim-ui/dist">
|
||||
DirectoryIndex search.html
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
Alias /nominatim/ui /home/vagrant/nominatim-ui/dist
|
||||
```
|
||||
|
||||
Replace `/home/vagrant/nominatim-ui` with the directory where you have cloned
|
||||
nominatim-ui.
|
||||
|
||||
!!! important
|
||||
The alias for nominatim-ui must come before the alias for the Nominatim
|
||||
website directory.
|
||||
|
||||
To set up forwarding, the Apache rewrite module is needed. Enable it with:
|
||||
|
||||
``` sh
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
Then add rewrite rules to the `Directory` directive of the Nominatim website
|
||||
directory like this:
|
||||
|
||||
``` apache
|
||||
<Directory "/home/vagrant/build/website">
|
||||
Options FollowSymLinks MultiViews
|
||||
AddType text/html .php
|
||||
Require all granted
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# This must correspond to the URL where nominatim can be found.
|
||||
RewriteBase "/nominatim/"
|
||||
|
||||
# If no endpoint is given, then use search.
|
||||
RewriteRule ^(/|$) "search.php"
|
||||
|
||||
# If format-html is explicity requested, forward to the UI.
|
||||
RewriteCond %{QUERY_STRING} "format=html"
|
||||
RewriteRule ^([^/]+).php ui/$1.html [R,END]
|
||||
# Same but .php suffix is missing.
|
||||
RewriteCond %{QUERY_STRING} "format=html"
|
||||
RewriteRule ^([^/]+) ui/$1.html [R,END]
|
||||
|
||||
# If no format parameter is there then forward anything
|
||||
# but /reverse and /lookup to the UI.
|
||||
RewriteCond %{QUERY_STRING} "!format="
|
||||
RewriteCond %{REQUEST_URI} "!/lookup"
|
||||
RewriteCond %{REQUEST_URI} "!/reverse"
|
||||
RewriteRule ^([^/]+).php ui/$1.html [R,END]
|
||||
# Same but .php suffix is missing.
|
||||
RewriteCond %{QUERY_STRING} "!format="
|
||||
RewriteCond %{REQUEST_URI} "!/lookup"
|
||||
RewriteCond %{REQUEST_URI} "!/reverse"
|
||||
RewriteRule ^([^/]+) ui/$1.html [R,END]
|
||||
</Directory>
|
||||
```
|
||||
|
||||
Restart Apache and the UI should be available.
|
||||
67
docs/admin/Update.md
Normal file
67
docs/admin/Update.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Updating the Database
|
||||
|
||||
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`.
|
||||
|
||||
!!! important
|
||||
If you have configured a flatnode file for the import, then you
|
||||
need to keep this flatnode file around for updates.
|
||||
|
||||
#### 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-changes` 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, [Advanced installations section](Advanced-Installations.md) contains instructions
|
||||
to set up and update multiple country extracts.
|
||||
@@ -1,19 +1,22 @@
|
||||
# Place details
|
||||
|
||||
Lookup details about a single place by id. The default output is HTML for debugging search logic and results.
|
||||
Show all details about a single place saved in the database.
|
||||
|
||||
**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/).
|
||||
!!! warning
|
||||
The details page exists for debugging only. You may not use it in scripts
|
||||
or to automatically query details about a result.
|
||||
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>
|
||||
``` xml
|
||||
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)
|
||||
`osmtype` and `osmid` are required parameters. 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
|
||||
@@ -23,36 +26,34 @@ 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>
|
||||
``` xml
|
||||
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.
|
||||
Place IDs 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 cannot be used as
|
||||
a 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)
|
||||
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)
|
||||
Include a breakdown of the address into elements. (Default: 0)
|
||||
|
||||
* `keywords=[0|1]`
|
||||
|
||||
@@ -60,11 +61,16 @@ 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)
|
||||
Include a details of places that are linked with this one. Places get linked
|
||||
together when they are different forms of the same physical object. Nominatim
|
||||
links two kinds of objects together: place nodes get linked with the
|
||||
corresponding administrative boundaries. Waterway relations get linked together with their
|
||||
members.
|
||||
(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)
|
||||
Include details of places lower in the address hierarchy. (Default: 0)
|
||||
|
||||
* `group_hierarchy=[0|1]`
|
||||
|
||||
@@ -72,7 +78,7 @@ 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)
|
||||
Include geometry of result. (Default: 0)
|
||||
|
||||
### Language of results
|
||||
|
||||
@@ -86,10 +92,6 @@ 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)
|
||||
|
||||
@@ -56,6 +56,21 @@ specified in the "Accept-Language" HTTP header.
|
||||
Either use a standard RFC2616 accept-language string or a simple
|
||||
comma-separated list of language codes.
|
||||
|
||||
### 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`
|
||||
|
||||
Return a simplified version of the output geometry. 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
|
||||
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
|
||||
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.
|
||||
There is one section for each format. The format correspond to what was
|
||||
selected via the `format` parameter.
|
||||
|
||||
## Formats
|
||||
|
||||
### JSON
|
||||
## JSON
|
||||
|
||||
The JSON format returns an array of places (for search and lookup) or
|
||||
a single place (for reverse) of the following format:
|
||||
@@ -41,48 +39,50 @@ a single place (for reverse) of the following format:
|
||||
"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
|
||||
* `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id))
|
||||
* `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference))
|
||||
* `boundingbox` - area of corner coordinates ([see notes](#boundingbox))
|
||||
* `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`)
|
||||
* `address` - dictionary of address details (only with `addressdetails=1`,
|
||||
[see notes](#addressdetails))
|
||||
* `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
|
||||
## 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
|
||||
## GeoJSON
|
||||
|
||||
This format follows the [RFC7946](https://geojson.org). Every feature includes
|
||||
a bounding box (`bbox`).
|
||||
|
||||
The feature list has the following fields:
|
||||
The properties object 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
|
||||
* `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id))
|
||||
* `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference))
|
||||
* `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`)
|
||||
* `address` - dictionary of address details (only with `addressdetails=1`,
|
||||
[see notes](#addressdetails))
|
||||
* `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.
|
||||
@@ -90,39 +90,36 @@ The feature list has the following fields:
|
||||
Use `polygon_geojson` to output the full geometry of the object instead
|
||||
of the centroid.
|
||||
|
||||
### GeocodeJSON
|
||||
## 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)
|
||||
* `osm_type`, `osm_id` - reference to the OSM object (unofficial extension, [see notes](#osm-reference))
|
||||
* `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` -
|
||||
* `housenumber`, `street`, `locality`, `district`, `postcode`, `city`,
|
||||
`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
|
||||
## XML
|
||||
|
||||
The XML response returns one or more place objects in slightly different
|
||||
formats depending on the API call.
|
||||
|
||||
#### Reverse
|
||||
### 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"
|
||||
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY" place_rank="15" address_rank="15"
|
||||
lat="48.9467562" lon="11.4038717"
|
||||
boundingbox="47.2701114,50.5647142,8.9763497,13.8396373">
|
||||
Bavaria, Germany
|
||||
@@ -148,11 +145,11 @@ 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
|
||||
* `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id))
|
||||
* `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference))
|
||||
* `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
|
||||
* `boundingbox` - comma-separated list of corner coordinates ([see notes](#boundingbox))
|
||||
|
||||
The full address of the result can be found in the content of the
|
||||
`result` element as a comma-separated list.
|
||||
@@ -160,14 +157,14 @@ The full address of the result can be found in the content of the
|
||||
Additional information requested with `addressdetails=1`, `extratags=1` and
|
||||
`namedetails=1` can be found in extra elements.
|
||||
|
||||
#### Search and Lookup
|
||||
### 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"
|
||||
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15" address_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"
|
||||
@@ -203,12 +200,13 @@ generic information about the query:
|
||||
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
|
||||
* `place_id` - reference to the Nominatim internal database ID ([see notes](#place_id-is-not-a-persistent-id))
|
||||
* `osm_type`, `osm_id` - reference to the OSM object ([see notes](#osm-reference))
|
||||
* `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
|
||||
* `boundingbox` - comma-separated list of corner coordinates ([see notes](#boundingbox))
|
||||
* `place_rank` - class [search rank](../develop/Ranking#search-rank)
|
||||
* `address_rank` - place [address rank](../develop/Ranking#address-rank)
|
||||
* `display_name` - full comma-separated address
|
||||
* `class`, `type` - key and value of the main OSM tag
|
||||
* `importance` - computed importance rank
|
||||
@@ -218,17 +216,19 @@ 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.
|
||||
be found in extra elements as sub-element of `extratags` and `namedetails`
|
||||
respectively.
|
||||
|
||||
|
||||
## 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 `place_id` is an internal identifier that is assigned data is imported
|
||||
into a Nominatim database. The same OSM object will have a different value
|
||||
on another server. It may even change its ID on the same server when it is
|
||||
removed and reimported while updating the database with fresh OSM data.
|
||||
It is 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
|
||||
@@ -237,10 +237,59 @@ 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.
|
||||
If you need an ID that is consistent over multiple installations of Nominatim,
|
||||
then you should use the combination of `osm_type`+`osm_id`+`class`.
|
||||
|
||||
### OSM reference
|
||||
|
||||
Nominatim may sometimes return special objects that do not correspond directly
|
||||
to an object in OpenStreetMap. These are:
|
||||
|
||||
* **Postcodes**. Nominatim returns an postcode point created from all mapped
|
||||
postcodes of the same name. The class and type of these object is `place=postcdode`.
|
||||
No `osm_type` and `osm_id` are included in the result.
|
||||
* **Housenumber interpolations**. Nominatim returns a single interpolated
|
||||
housenumber from the interpolation way. The class and type are `place=house`
|
||||
and `osm_type` and `osm_id` correspond to the interpolation way in OSM.
|
||||
* **TIGER housenumber.** Nominatim returns a single interpolated housenumber
|
||||
from the TIGER data. The class and type are `place=house`
|
||||
and `osm_type` and `osm_id` correspond to the street mentioned in the result.
|
||||
|
||||
Please note that the `osm_type` and `osm_id` returned may be changed in the
|
||||
future. You should not expect to only find `node`, `way` and `relation` for
|
||||
the type.
|
||||
|
||||
### boundingbox
|
||||
|
||||
Comma separated list of min latitude, max latitude, min longitude, max longitude.
|
||||
The whole planet would be `-90,90,-180,180`.
|
||||
|
||||
Can be used to pan and center the map on the result, for example with leafletjs
|
||||
mapping library
|
||||
`map.fitBounds([[bbox[0],bbox[2]],[bbox[1],bbox[3]]], {padding: [20, 20], maxzoom: 16});`
|
||||
|
||||
Bounds crossing the antimeridian have a min latitude -180 and max latitude 180,
|
||||
essentially covering the entire planet
|
||||
(see [issue 184](https://github.com/openstreetmap/Nominatim/issues/184)).
|
||||
|
||||
### addressdetails
|
||||
|
||||
Address details in the xml and json formats return a list of names together
|
||||
with a designation label. Per default the following labels may appear:
|
||||
|
||||
* continent
|
||||
* country, country_code
|
||||
* region, state, state_district, county
|
||||
* municipality, city, town, village
|
||||
* city_district, district, borough, suburb, subdivision
|
||||
* hamlet, croft, isolated_dwelling
|
||||
* neighbourhood, allotments, quarter
|
||||
* city_block, residental, farm, farmyard, industrial, commercial, retail
|
||||
* road
|
||||
* house_number, house_name
|
||||
* emergency, historic, military, natural, landuse, place, railway,
|
||||
man_made, aerialway, boundary, amenity, aeroway, club, craft, leisure,
|
||||
office, mountain_pass, shop, tourism, bridge, tunnel, waterway
|
||||
|
||||
They roughly correspond to the classification of the OpenStreetMap data
|
||||
according to either the `place` tag or the main key of the object.
|
||||
|
||||
@@ -7,7 +7,7 @@ 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
|
||||
* __[/status](Status.md)__ - 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
|
||||
|
||||
@@ -1,36 +1,48 @@
|
||||
# Reverse Geocoding
|
||||
|
||||
Reverse geocoding generates an address from a latitude and longitude or from
|
||||
an OSM object.
|
||||
Reverse geocoding generates an address from a latitude and longitude.
|
||||
|
||||
## How it works
|
||||
|
||||
The reverse geocoding API does not exactly compute the address for the
|
||||
coordinate it receives. It works by finding the closest suitable OSM object
|
||||
and returning its address information. This may occasionally lead to
|
||||
unexpected results.
|
||||
|
||||
First of all, Nominatim only includes OSM objects in
|
||||
its index that are suitable for searching. Small, unnamed paths for example
|
||||
are missing from the database and can therefore not be used for reverse
|
||||
geocoding either.
|
||||
|
||||
The other issue to be aware of is that the closest OSM object may not always
|
||||
have a similar enough address to the coordinate you were requesting. For
|
||||
example, in dense city areas it may belong to a completely different street.
|
||||
|
||||
|
||||
## Parameters
|
||||
|
||||
The main format of the reverse API is
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/reverse?<query>
|
||||
https://nominatim.openstreetmap.org/reverse?lat=<value>&lon=<value>&<params>
|
||||
```
|
||||
|
||||
There are two ways how the requested location can be specified:
|
||||
where `lat` and `lon` are latitude and longitutde of a coordinate in WGS84
|
||||
projection. The API returns exactly one result or an error when the coordinate
|
||||
is in an area with no OSM data coverage.
|
||||
|
||||
* `lat=<value>` `lon=<value>`
|
||||
Additional paramters are accepted as listed below.
|
||||
|
||||
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.
|
||||
!!! warning "Deprecation warning"
|
||||
The reverse API used to allow address lookup for a single OSM object by
|
||||
its OSM id. This use is now deprecated. Use the [Address Lookup API](../Lookup)
|
||||
instead.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
@@ -69,8 +81,9 @@ comma-separated list of language codes.
|
||||
|
||||
* `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.
|
||||
Level of detail required for the address. Default: 18. This is a number that
|
||||
corresponds roughly to the zoom level used in XYZ tile sources in frameworks
|
||||
like Leaflet.js, Openlayers etc.
|
||||
In terms of address details the zoom levels are as follows:
|
||||
|
||||
zoom | address detail
|
||||
@@ -97,7 +110,7 @@ options can be used at a time. (Default: 0)
|
||||
|
||||
* `polygon_threshold=0.0`
|
||||
|
||||
Simplify the output geometry before returning. The parameter is the
|
||||
Return a simplified version of the output geometry. 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)
|
||||
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
# 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 API allows you to look up a location from a textual description
|
||||
or address. Nominatim supports structured and 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/).
|
||||
This can be used to narrow down the kind of objects to be returned.
|
||||
|
||||
!!! warning
|
||||
Special phrases are not suitable to query all objects of a certain type in an
|
||||
area. Nominatim will always just return a collection of the best matches. To
|
||||
download OSM data by object type, use the [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.
|
||||
The search API has the following format:
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/search?<params>
|
||||
```
|
||||
|
||||
In this form, the query may be given through two different sets of parameters:
|
||||
The search term may be specified with two different sets of parameters:
|
||||
|
||||
* `q=<query>`
|
||||
|
||||
@@ -46,13 +43,13 @@ In this form, the query may be given through two different sets of parameters:
|
||||
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.
|
||||
Both query forms accept the additional parameters listed below.
|
||||
|
||||
### Output format
|
||||
|
||||
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
||||
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||
See [Place Output Formats](Output.md) for details on each format. (Default: jsonv2)
|
||||
|
||||
* `json_callback=<string>`
|
||||
|
||||
@@ -92,16 +89,20 @@ comma-separated list of language codes.
|
||||
* `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.
|
||||
[ISO 3166-1alpha2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code,
|
||||
e.g. `gb` for the United Kingdom, `de` for Germany.
|
||||
|
||||
Each place in Nominatim is assigned to one country code based
|
||||
on OSM country boundaries. In rare cases a place may not be in any country
|
||||
at all, for example, in international waters.
|
||||
|
||||
* `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).
|
||||
This can be used to retrieve additional 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>`
|
||||
@@ -112,16 +113,17 @@ 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,
|
||||
are accepted 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
|
||||
When a viewbox is given, restrict the result to items contained within 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)
|
||||
only search is allowed. Give the special keyword for the amenity in square
|
||||
brackets, e.g. `[pub]` and a selection of objects of this type is returned.
|
||||
There is no guarantee that the result is complete. (Default: 0)
|
||||
|
||||
|
||||
### Polygon output
|
||||
@@ -136,7 +138,7 @@ options can be used at a time. (Default: 0)
|
||||
|
||||
* `polygon_threshold=0.0`
|
||||
|
||||
Simplify the output geometry before returning. The parameter is the
|
||||
Return a simplified version of the output geometry. 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)
|
||||
|
||||
@@ -150,13 +152,11 @@ address to identify your requests. See Nominatim's [Usage Policy](https://operat
|
||||
* `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
|
||||
object in reality. The simplest case is a street being split into 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
|
||||
@@ -168,21 +168,27 @@ This overrides the specified machine readable format. (Default: 0)
|
||||
## Examples
|
||||
|
||||
|
||||
##### XML with polygon points
|
||||
##### XML with kml polygon
|
||||
|
||||
* [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)
|
||||
* [https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon_geojson=1&addressdetails=1](https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon_geojson=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">
|
||||
<geokml>
|
||||
<Polygon>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>-1.816513,52.548756599999997 -1.816434,52.548747300000002 -1.816429,52.5487629 -1.8163717,52.548756099999999 -1.8163464,52.548834599999999 -1.8164599,52.548848100000001 -1.8164685,52.5488213 -1.8164913,52.548824000000003 -1.816513,52.548756599999997</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</geokml>
|
||||
<house_number>135</house_number>
|
||||
<road>Pilkington Avenue</road>
|
||||
<village>Wylde Green</village>
|
||||
|
||||
60
docs/api/Status.md
Normal file
60
docs/api/Status.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Status
|
||||
|
||||
Useful for checking if the service and database is running. The JSON output also shows
|
||||
when the database was last updated.
|
||||
|
||||
## Parameters
|
||||
|
||||
* `format=[text|json]` (defaults to 'text')
|
||||
|
||||
|
||||
## Output
|
||||
|
||||
#### Text format
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/status.php
|
||||
```
|
||||
|
||||
will return HTTP status code 200 and print `OK`.
|
||||
|
||||
On error it will return HTTP status code 500 and print a message, e.g.
|
||||
`ERROR: Database connection failed`.
|
||||
|
||||
|
||||
|
||||
#### JSON format
|
||||
|
||||
```
|
||||
https://nominatim.openstreetmap.org/status.php?format=json
|
||||
```
|
||||
|
||||
will return HTTP code 200 and a structure
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "OK",
|
||||
"data_updated": "2020-05-04T14:47:00+00:00"
|
||||
}
|
||||
```
|
||||
|
||||
On error will also return HTTP status code 200 and a structure with error
|
||||
code and message, e.g.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 700,
|
||||
"message": "Database connection failed"
|
||||
}
|
||||
```
|
||||
|
||||
Possible status codes are
|
||||
|
||||
| | message | notes |
|
||||
|-----|----------------------|---------------------------------------------------|
|
||||
| 700 | "No database" | connection failed |
|
||||
| 701 | "Module failed" | database could not load nominatim.so |
|
||||
| 702 | "Module call failed" | nominatim.so loaded but calling a function failed |
|
||||
| 703 | "Query failed" | test query against a database table failed |
|
||||
| 704 | "No value" | test query worked but returned no results |
|
||||
@@ -1,4 +0,0 @@
|
||||
# Additional Data Sources
|
||||
|
||||
This guide explains how data sources other than OpenStreetMap mentioned in
|
||||
the install instructions got obtained and converted.
|
||||
169
docs/develop/Development-Environment.md
Normal file
169
docs/develop/Development-Environment.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Setting up Nominatim for Development
|
||||
|
||||
This chapter gives an overview how to set up Nominatim for developement
|
||||
and how to run tests.
|
||||
|
||||
!!! Important
|
||||
This guide assumes that you develop under the latest version of Ubuntu. You
|
||||
can of course also use your favourite distribution. You just might have to
|
||||
adapt the commands below slightly, in particular the commands for installing
|
||||
additional software.
|
||||
|
||||
## Installing Nominatim
|
||||
|
||||
The first step is to install Nominatim itself. Please follow the installation
|
||||
instructions in the [Admin section](../admin/Installation.md). You don't need
|
||||
to set up a webserver for development, the webserver that is included with PHP
|
||||
is sufficient.
|
||||
|
||||
If you want to run Nominatim in a VM via Vagrant, use the default `ubuntu` setup.
|
||||
Vagrant's libvirt provider runs out-of-the-box under Ubuntu. You also need to
|
||||
install an NFS daemon to enable directory sharing between host and guest. The
|
||||
following packages should get you started:
|
||||
|
||||
sudo apt install vagrant vagrant-libvirt libvirt-daemon nfs-kernel-server
|
||||
|
||||
## Prerequisites for testing and documentation
|
||||
|
||||
The Nominatim test suite consists of behavioural tests (using behave) and
|
||||
unit tests (using PHPUnit). It has the following additional requirements:
|
||||
|
||||
* [behave test framework](https://behave.readthedocs.io) >= 1.2.5
|
||||
* [nose](https://nose.readthedocs.io)
|
||||
* [phpunit](https://phpunit.de) >= 7.3
|
||||
* [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer)
|
||||
|
||||
The documentation is built with mkdocs:
|
||||
|
||||
* [mkdocs](https://www.mkdocs.org/) >= 1.1.2
|
||||
|
||||
### Installing prerequisites on Ubuntu/Debian
|
||||
|
||||
Some of the Python packages require the newest version which is not yet
|
||||
available with the current distributions. Therefore it is recommended to
|
||||
install pip to get the newest versions.
|
||||
|
||||
To install all necessary packages run:
|
||||
|
||||
```sh
|
||||
sudo apt install php-cgi phpunit php-codesniffer \
|
||||
python3-pip python3-setuptools python3-dev
|
||||
|
||||
pip3 install --user behave nose mkdocs
|
||||
```
|
||||
|
||||
The `mkdocs` executable will be located in `.local/bin`. You may have to add
|
||||
this directory to your path, for example by running:
|
||||
|
||||
```
|
||||
echo 'export PATH=~/.local/bin:$PATH' > ~/.profile
|
||||
```
|
||||
|
||||
If your distribution does not have PHPUnit 7.3+, you can install it (as well
|
||||
as CodeSniffer) via composer:
|
||||
|
||||
```
|
||||
sudo apt-get install composer
|
||||
composer global require "squizlabs/php_codesniffer=*"
|
||||
composer global require "phpunit/phpunit=8.*"
|
||||
```
|
||||
|
||||
The binaries are found in `.config/composer/vendor/bin`. You need to add this
|
||||
to your PATH as well:
|
||||
|
||||
```
|
||||
echo 'export PATH=~/.config/composer/vendor/bin:$PATH' > ~/.profile
|
||||
```
|
||||
|
||||
|
||||
## Executing Tests
|
||||
|
||||
All tests are located in the `\test` directory.
|
||||
|
||||
### Preparing the test database
|
||||
|
||||
Some of the behavioural test expect a test database to be present. You need at
|
||||
least 2GB RAM and 10GB disk space to create the database.
|
||||
|
||||
First create a separate directory for the test DB and fetch the test planet
|
||||
data and the Tiger data for South Dakota:
|
||||
|
||||
```
|
||||
mkdir testdb
|
||||
cd testdb
|
||||
wget https://www.nominatim.org/data/test/nominatim-api-testdata.pbf
|
||||
wget -O - https://nominatim.org/data/tiger2018-nominatim-preprocessed.tar.gz | tar xz --wildcards --no-anchored '46*'
|
||||
```
|
||||
|
||||
Configure and build Nominatim in the usual way:
|
||||
|
||||
```
|
||||
cmake $USERNAME/Nominatim
|
||||
make
|
||||
```
|
||||
|
||||
Copy the test settings:
|
||||
|
||||
```
|
||||
cp $USERNAME/Nominatim/test/testdb/local.php settings/
|
||||
```
|
||||
|
||||
Inspect the file to check that all settings are correct for your local setup.
|
||||
|
||||
Now you can import the test database:
|
||||
|
||||
```
|
||||
dropdb --if-exists test_api_nominatim
|
||||
./utils/setup.php --all --osm-file nominatim-api-testdb.pbf 2>&1 | tee import.log
|
||||
./utils/specialphrases.php --wiki-import | psql -d test_api_nominatim 2>&1 | tee -a import.log
|
||||
./utils/setup.php --import-tiger-data 2>&1 | tee -a import.log
|
||||
```
|
||||
|
||||
### Running the tests
|
||||
|
||||
To run all tests just go to the test directory and run make:
|
||||
|
||||
```sh
|
||||
cd test
|
||||
make
|
||||
```
|
||||
|
||||
To skip tests that require the test database, run `make no-test-db` instead.
|
||||
|
||||
For more information about the structure of the tests and how to change and
|
||||
extend the test suite, see the [Testing chapter](Testing.md).
|
||||
|
||||
## 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
|
||||
[https://nominatim.org/release-docs/develop/](https://nominatim.org/release-docs/develop/)
|
||||
|
||||
To build the documentation, go to the build directory and run
|
||||
|
||||
```
|
||||
make doc
|
||||
INFO - Cleaning site directory
|
||||
INFO - Building documentation to directory: /home/vagrant/build/site-html
|
||||
```
|
||||
|
||||
This runs `mkdocs build` plus extra transformation of some files and adds
|
||||
symlinks (see `CMakeLists.txt` for the exact steps).
|
||||
|
||||
Now you can start webserver for local testing
|
||||
|
||||
```
|
||||
build> 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, use a port that is forwarded
|
||||
to your host:
|
||||
|
||||
```
|
||||
build> mkdocs serve --dev-addr 0.0.0.0:8088
|
||||
[server:296] Serving on http://0.0.0.0:8088
|
||||
[handlers:62] Start watching changes
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,8 +1,8 @@
|
||||
# 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.
|
||||
OSM data is initially imported using [osm2pgsql](https://osm2pgsql.org).
|
||||
Nominatim uses its own data output style 'gazetteer', which differs from the
|
||||
output style created for map rendering.
|
||||
|
||||
## Database Layout
|
||||
|
||||
|
||||
45
docs/develop/Postcodes.md
Normal file
45
docs/develop/Postcodes.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Postcodes in Nominatim
|
||||
|
||||
The blog post
|
||||
[Nominatim and Postcodes](https://www.openstreetmap.org/user/lonvia/diary/43143)
|
||||
describes the handling implemented since Nominatim 3.1.
|
||||
|
||||
Postcode centroids (aka 'calculated postcodes') are generated by looking at all
|
||||
postcodes of a country, grouping them and calculating the geometric centroid.
|
||||
There is currently no logic to deal with extreme outliers (typos or other
|
||||
mistakes in OSM data). There is also no check if a postcodes adheres to a
|
||||
country's format, e.g. if Swiss postcodes are 4 digits.
|
||||
|
||||
|
||||
## Regular updating calculated postcodes
|
||||
|
||||
The script to rerun the calculation is
|
||||
`build/utils/update.php --calculate-postcodes`
|
||||
and runs once per night on nominatim.openstreetmap.org.
|
||||
|
||||
|
||||
## Finding places that share a specific postcode
|
||||
|
||||
In the Nominatim database run
|
||||
|
||||
```sql
|
||||
SELECT address->'postcode' as pc,
|
||||
osm_type, osm_id, class, type,
|
||||
st_x(centroid) as lon, st_y(centroid) as lat
|
||||
FROM placex
|
||||
WHERE country_code='fr'
|
||||
AND upper(trim (both ' ' from address->'postcode')) = '33210';
|
||||
```
|
||||
|
||||
Alternatively on [Overpass](https://overpass-turbo.eu/) run the following query
|
||||
|
||||
```
|
||||
[out:json][timeout:250];
|
||||
area["name"="France"]->.boundaryarea;
|
||||
(
|
||||
nwr(area.boundaryarea)["addr:postcode"="33210"];
|
||||
);
|
||||
out body;
|
||||
>;
|
||||
out skel qt;
|
||||
```
|
||||
@@ -7,24 +7,74 @@ 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
|
||||
when ranking search results. 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.
|
||||
|
||||
The following table gives an overview of the kind of features that Nominatim
|
||||
expects for each rank:
|
||||
|
||||
rank | typical place types | extent
|
||||
-------|---------------------------------|-------
|
||||
1-3 | oceans, continents | -
|
||||
4 | countries | -
|
||||
5-9 | states, regions, provinces | -
|
||||
10-12 | counties | -
|
||||
13-16 | cities, municipalities, islands | 15 km
|
||||
17-18 | towns, boroughs | 4 km
|
||||
19 | villages, suburbs | 2 km
|
||||
20 | hamlets, farms, neighbourhoods | 1 km
|
||||
21-25 | isolated dwellings, city blocks | 500 m
|
||||
|
||||
The extent column describes how far a feature is assumed to reach when it
|
||||
is mapped only as a point. Larger features like countries and states are usually
|
||||
available with their exact area in the OpenStreetMap data. That is why no extent
|
||||
is given.
|
||||
|
||||
## 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.
|
||||
eligible to be part of an address. Places that should not appear in the
|
||||
address must 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.
|
||||
The following table gives an overview how ranks are mapped to address parts:
|
||||
|
||||
rank | address part
|
||||
-------------|-------------
|
||||
1-3 | _unused_
|
||||
4 | country
|
||||
5-9 | state
|
||||
10-12 | county
|
||||
13-16 | city
|
||||
17-21 | suburb
|
||||
22-24 | neighbourhood
|
||||
25 | squares, farms, localities
|
||||
26-27 | street
|
||||
28-30 | POI/house number
|
||||
|
||||
The country rank 4 usually doesn't show up in the address parts of an object.
|
||||
The country is determined indirectly from the country code.
|
||||
|
||||
Ranks 5-24 can be assigned more or less freely. They make up the major part
|
||||
of the address.
|
||||
|
||||
Rank 25 is also an addressing rank but it is special because while it can be
|
||||
the parent to a POI with an addr:place of the same name, it cannot be a parent
|
||||
to streets. Use it for place features that are technically on the same level
|
||||
as a street (e.g. squares, city blocks) or for places that should not normally
|
||||
appear in an address unless explicitly tagged so (e.g place=locality which
|
||||
should be uninhabited and as such not addressable).
|
||||
|
||||
The street ranks 26 and 27 are handled slightly differently. Only one object
|
||||
from these ranks shows up in an address.
|
||||
|
||||
For POI level objects like shops, buildings or house numbers always use rank 30.
|
||||
Ranks 28 is reserved for house number interpolations. 29 is for internal use
|
||||
only.
|
||||
|
||||
## Rank configuration
|
||||
|
||||
@@ -84,7 +134,7 @@ 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
|
||||
make sure that each combination of country/key/value appears only once per
|
||||
file. Otherwise the import will fail with a UNIQUE INDEX constraint violation
|
||||
on import.
|
||||
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
This directory contains functional and unit tests for the Nominatim API.
|
||||
# Nominatim Test Suite
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
This chapter describes the tests in the `/test` directory, how they are
|
||||
structured and how to extend them. For a quick introduction on how to run
|
||||
the tests, see the [Development setup chapter](Development-Environment.md).
|
||||
|
||||
* Python 3 (https://www.python.org/)
|
||||
* behave test framework >= 1.2.5 (https://github.com/behave/behave)
|
||||
* nose (https://nose.readthedocs.org)
|
||||
* pytidylib (http://countergram.com/open-source/pytidylib)
|
||||
* psycopg2 (http://initd.org/psycopg/)
|
||||
|
||||
To get the prerequisites on a a fresh Ubuntu LTS 16.04 run:
|
||||
|
||||
[sudo] apt-get install python3-dev python3-pip python3-psycopg2 python3-tidylib phpunit php-cgi
|
||||
pip3 install --user behave nose
|
||||
|
||||
|
||||
Overall structure
|
||||
=================
|
||||
## Overall structure
|
||||
|
||||
There are two kind of tests in this test suite. There are functional tests
|
||||
which test the API interface using a BDD test framework and there are unit
|
||||
@@ -37,29 +25,35 @@ This test directory is sturctured as follows:
|
||||
+- testdb Base data for generating API test database
|
||||
```
|
||||
|
||||
PHP Unit Tests
|
||||
==============
|
||||
## PHP Unit Tests (`test/php`)
|
||||
|
||||
Unit tests can be found in the php/ directory and tests selected php functions.
|
||||
Unit tests can be found in the php/ directory. They test selected php functions.
|
||||
Very low coverage.
|
||||
|
||||
To execute the test suite run
|
||||
|
||||
cd test/php
|
||||
phpunit ../
|
||||
UNIT_TEST_DSN='pgsql:dbname=nominatim_unit_tests' phpunit ../
|
||||
|
||||
It will read phpunit.xml which points to the library, test path, bootstrap
|
||||
strip and set other parameters.
|
||||
|
||||
It will use (and destroy) a local database 'nominatim_unit_tests'. You can set
|
||||
a different connection string with e.g. UNIT_TEST_DSN='pgsql:dbname=foo_unit_tests'.
|
||||
|
||||
BDD Functional Tests
|
||||
====================
|
||||
## BDD Functional Tests (`test/bdd`)
|
||||
|
||||
Functional tests are written as BDD instructions. For more information on
|
||||
the philosophy of BDD testing, see http://pythonhosted.org/behave/philosophy.html
|
||||
the philosophy of BDD testing, see the
|
||||
[Behave manual](http://pythonhosted.org/behave/philosophy.html).
|
||||
|
||||
Usage
|
||||
-----
|
||||
The following explanation assume that the reader is familiar with the BDD
|
||||
notations of features, scenarios and steps.
|
||||
|
||||
All possible steps can be found in the `steps` directory and should ideally
|
||||
be documented.
|
||||
|
||||
### General Usage
|
||||
|
||||
To run the functional tests, do
|
||||
|
||||
@@ -89,42 +83,29 @@ The tests can be configured with a set of environment variables (`behave -D key=
|
||||
run, otherwise the result is undefined.
|
||||
|
||||
Logging can be defined through command line parameters of behave itself. Check
|
||||
out `behave --help` for details. Also keep an eye out for the 'work-in-progress'
|
||||
out `behave --help` for details. Also have a look at the 'work-in-progress'
|
||||
feature of behave which comes in handy when writing new tests.
|
||||
|
||||
Writing Tests
|
||||
-------------
|
||||
|
||||
The following explanation assume that the reader is familiar with the BDD
|
||||
notations of features, scenarios and steps.
|
||||
|
||||
All possible steps can be found in the `steps` directory and should ideally
|
||||
be documented.
|
||||
|
||||
### API Tests (`test/bdd/api`)
|
||||
|
||||
These tests are meant to test the different API endpoints and their parameters.
|
||||
They require a preimported test database, which consists of the import of a
|
||||
planet extract. A precompiled PBF with the necessary data can be downloaded from
|
||||
https://www.nominatim.org/data/test/nominatim-api-testdata.pbf
|
||||
They require to import several datasets into a test database.
|
||||
See the [Development Setup chapter](Development-Environment.md#preparing-the-test-database)
|
||||
for instructions on how to set up this database.
|
||||
|
||||
The polygons defining the extract can be found in the test/testdb
|
||||
directory. There is also a reduced set of wikipedia data for this extract,
|
||||
which you need to import as well. For Tiger tests the data of South Dakota
|
||||
is required. Get the Tiger files `46*`.
|
||||
The official test dataset was derived from the 180924 planet (note: such
|
||||
file no longer exists at https://planet.openstreetmap.org/planet/2018/).
|
||||
Newer planets are likely to work as well but you may see isolated test
|
||||
failures where the data has changed.
|
||||
|
||||
The official test dataset is derived from the 160725 planet. Newer
|
||||
planets are likely to work as well but you may see isolated test
|
||||
failures where the data has changed. To recreate the input data
|
||||
for the test database run:
|
||||
The official test dataset can always be downloaded from
|
||||
[nominatim.org](https://www.nominatim.org/data/test/nominatim-api-testdata.pbf)
|
||||
To recreate the input data for the test database run:
|
||||
|
||||
wget https://ftp5.gwdg.de/pub/misc/openstreetmap/planet.openstreetmap.org/pbf/planet-180924.osm.pbf
|
||||
osmconvert planet-180924.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
|
||||
|
||||
Before importing make sure to add the following to your local settings:
|
||||
|
||||
@define('CONST_Database_DSN', 'pgsql://@/test_api_nominatim');
|
||||
@define('CONST_Wikipedia_Data_Path', CONST_BasePath.'/test/testdb');
|
||||
```
|
||||
wget https://ftp5.gwdg.de/pub/misc/openstreetmap/planet.openstreetmap.org/pbf/planet-180924.osm.pbf
|
||||
osmconvert planet-180924.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
|
||||
```
|
||||
|
||||
#### Code Coverage
|
||||
|
||||
@@ -134,7 +115,7 @@ On Debian/Ubuntu run:
|
||||
|
||||
apt-get install php-codecoverage php-xdebug
|
||||
|
||||
The run the API tests as follows:
|
||||
Then run the API tests as follows:
|
||||
|
||||
behave api -DPHPCOV=<coverage output dir>
|
||||
|
||||
@@ -143,11 +124,11 @@ the [phpcov](https://github.com/sebastianbergmann/phpcov) tool:
|
||||
|
||||
phpcov merge --html=<report output dir> <coverage output dir>
|
||||
|
||||
### Indexing Tests (`test/bdd/db`)
|
||||
### DB Creation Tests (`test/bdd/db`)
|
||||
|
||||
These tests check the import and update of the Nominatim database. They do not
|
||||
test the correctness of osm2pgsql. Each test will write some data into the `place`
|
||||
table (and optionally `the planet_osm_*` tables if required) and then run
|
||||
table (and optionally the `planet_osm_*` tables if required) and then run
|
||||
Nominatim's processing functions on that.
|
||||
|
||||
These tests need to create their own test databases. By default they will be
|
||||
@@ -158,4 +139,4 @@ needs superuser rights for postgres.
|
||||
### Import Tests (`test/bdd/osm2pgsql`)
|
||||
|
||||
These tests check that data is imported correctly into the place table. They
|
||||
use the same template database as the Indexing tests, so the same remarks apply.
|
||||
use the same template database as the DB Creation tests, so the same remarks apply.
|
||||
34
docs/develop/data-sources.md
Normal file
34
docs/develop/data-sources.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Additional Data Sources
|
||||
|
||||
This guide explains how data sources other than OpenStreetMap mentioned in
|
||||
the install instructions got obtained and converted.
|
||||
|
||||
## Country grid
|
||||
|
||||
Nominatim uses pre-generated country borders data. In case one imports only
|
||||
a subset of a country. And to assign each place a partition. Nominatim
|
||||
database tables are split into partitions for performance.
|
||||
|
||||
More details in [osm-search/country-grid-data](https://github.com/osm-search/country-grid-data).
|
||||
|
||||
## US Census TIGER
|
||||
|
||||
For the United States you can choose to import additonal street-level data.
|
||||
The data isn't mixed into OSM data but queried as fallback when no OSM
|
||||
result can be found.
|
||||
|
||||
More details in [osm-search/TIGER-data](https://github.com/osm-search/TIGER-data).
|
||||
|
||||
## GB postcodes
|
||||
|
||||
For Great Britain you can choose to import Royalmail postcode centroids.
|
||||
|
||||
More details in [osm-search/gb-postcode-data](https://github.com/osm-search/gb-postcode-data).
|
||||
|
||||
|
||||
## Wikipedia & Wikidata rankings
|
||||
|
||||
Nominatim can import "importance" data of place names. This greatly
|
||||
improves ranking of results.
|
||||
|
||||
More details in [osm-search/wikipedia-wikidata](https://github.com/osm-search/wikipedia-wikidata).
|
||||
@@ -9,14 +9,14 @@ 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
|
||||
gazetteer output plugin in `osm2pgsql/src/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`.
|
||||
and can be found in the files in the `sql/functions/` directory.
|
||||
|
||||
The __search frontend__ implements the actual API. It takes search
|
||||
and reverse geocoding queries from the user, looks up the data and
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
.toctree-l3 {
|
||||
display: none!important
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 12pt
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 1pt 12pt;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
site_name: Nominatim Documentation
|
||||
site_name: Nominatim 3.6.0
|
||||
theme: readthedocs
|
||||
docs_dir: ${CMAKE_CURRENT_BINARY_DIR}
|
||||
site_url: http://nominatim.org
|
||||
site_url: https://nominatim.org
|
||||
repo_url: https://github.com/openstreetmap/Nominatim
|
||||
pages:
|
||||
- 'Introduction' : 'index.md'
|
||||
@@ -11,31 +11,34 @@ pages:
|
||||
- 'Reverse': 'api/Reverse.md'
|
||||
- 'Address Lookup': 'api/Lookup.md'
|
||||
- 'Details' : 'api/Details.md'
|
||||
- 'Status' : 'api/Status.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'
|
||||
- 'Import' : 'admin/Import.md'
|
||||
- 'Update' : 'admin/Update.md'
|
||||
- 'Deploy' : 'admin/Deployment.md'
|
||||
- 'Nominatim UI' : 'admin/Setup-Nominatim-UI.md'
|
||||
- 'Advanced Installations' : 'admin/Advanced-Installations.md'
|
||||
- 'Migration from older Versions' : 'admin/Migration.md'
|
||||
- 'Troubleshooting' : 'admin/Faq.md'
|
||||
- 'Developers Guide':
|
||||
- 'Overview' : 'develop/overview.md'
|
||||
- 'Setup for Development' : 'develop/Development-Environment.md'
|
||||
- 'Architecture 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'
|
||||
- 'Postcodes' : 'develop/Postcodes.md'
|
||||
- 'Testing' : 'develop/Testing.md'
|
||||
- 'External Data Sources': 'develop/data-sources.md'
|
||||
- 'Appendix':
|
||||
- 'Installation on CentOS 7' : 'appendix/Install-on-Centos-7.md'
|
||||
- 'Installation on Ubuntu 16' : 'appendix/Install-on-Ubuntu-16.md'
|
||||
- 'Installation on CentOS 8' : 'appendix/Install-on-Centos-8.md'
|
||||
- 'Installation on Ubuntu 18' : 'appendix/Install-on-Ubuntu-18.md'
|
||||
- 'Installation on Ubuntu 20' : 'appendix/Install-on-Ubuntu-20.md'
|
||||
markdown_extensions:
|
||||
- codehilite:
|
||||
use_pygments: False
|
||||
- codehilite
|
||||
- admonition
|
||||
- toc:
|
||||
permalink:
|
||||
extra_css: [extra.css]
|
||||
extra_css: [extra.css, styles.css]
|
||||
|
||||
69
docs/styles.css
Normal file
69
docs/styles.css
Normal file
@@ -0,0 +1,69 @@
|
||||
.codehilite .hll { background-color: #ffffcc }
|
||||
.codehilite { background: #f0f0f0; }
|
||||
.codehilite .c { color: #60a0b0; font-style: italic } /* Comment */
|
||||
.codehilite .err { /* border: 1px solid #FF0000 */ } /* Error */
|
||||
.codehilite .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.codehilite .o { color: #666666 } /* Operator */
|
||||
.codehilite .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */
|
||||
.codehilite .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
|
||||
.codehilite .cp { color: #007020 } /* Comment.Preproc */
|
||||
.codehilite .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */
|
||||
.codehilite .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
|
||||
.codehilite .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
|
||||
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.codehilite .ge { font-style: italic } /* Generic.Emph */
|
||||
.codehilite .gr { color: #FF0000 } /* Generic.Error */
|
||||
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.codehilite .go { color: #888888 } /* Generic.Output */
|
||||
.codehilite .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.codehilite .gs { font-weight: bold } /* Generic.Strong */
|
||||
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.codehilite .gt { color: #0044DD } /* Generic.Traceback */
|
||||
.codehilite .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
|
||||
.codehilite .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
|
||||
.codehilite .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
|
||||
.codehilite .kp { color: #007020 } /* Keyword.Pseudo */
|
||||
.codehilite .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
|
||||
.codehilite .kt { color: #902000 } /* Keyword.Type */
|
||||
.codehilite .m { color: #40a070 } /* Literal.Number */
|
||||
.codehilite .s { color: #4070a0 } /* Literal.String */
|
||||
.codehilite .na { color: #4070a0 } /* Name.Attribute */
|
||||
.codehilite .nb { color: #007020 } /* Name.Builtin */
|
||||
.codehilite .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
|
||||
.codehilite .no { color: #60add5 } /* Name.Constant */
|
||||
.codehilite .nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.codehilite .ni { color: #d55537; font-weight: bold } /* Name.Entity */
|
||||
.codehilite .ne { color: #007020 } /* Name.Exception */
|
||||
.codehilite .nf { color: #06287e } /* Name.Function */
|
||||
.codehilite .nl { color: #002070; font-weight: bold } /* Name.Label */
|
||||
.codehilite .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.codehilite .nt { color: #062873; font-weight: bold } /* Name.Tag */
|
||||
.codehilite .nv { color: #bb60d5 } /* Name.Variable */
|
||||
.codehilite .ow { color: #007020; font-weight: bold } /* Operator.Word */
|
||||
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.codehilite .mb { color: #40a070 } /* Literal.Number.Bin */
|
||||
.codehilite .mf { color: #40a070 } /* Literal.Number.Float */
|
||||
.codehilite .mh { color: #40a070 } /* Literal.Number.Hex */
|
||||
.codehilite .mi { color: #40a070 } /* Literal.Number.Integer */
|
||||
.codehilite .mo { color: #40a070 } /* Literal.Number.Oct */
|
||||
.codehilite .sa { color: #4070a0 } /* Literal.String.Affix */
|
||||
.codehilite .sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.codehilite .sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.codehilite .dl { color: #4070a0 } /* Literal.String.Delimiter */
|
||||
.codehilite .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.codehilite .s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.codehilite .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
.codehilite .sh { color: #4070a0 } /* Literal.String.Heredoc */
|
||||
.codehilite .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
|
||||
.codehilite .sx { color: #c65d09 } /* Literal.String.Other */
|
||||
.codehilite .sr { color: #235388 } /* Literal.String.Regex */
|
||||
.codehilite .s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.codehilite .ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.codehilite .bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.codehilite .fm { color: #06287e } /* Name.Function.Magic */
|
||||
.codehilite .vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.codehilite .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.codehilite .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.codehilite .vm { color: #bb60d5 } /* Name.Variable.Magic */
|
||||
.codehilite .il { color: #40a070 } /* Literal.Number.Integer.Long */
|
||||
@@ -9,10 +9,13 @@ require_once(CONST_BasePath.'/lib/ClassTypes.php');
|
||||
*/
|
||||
class AddressDetails
|
||||
{
|
||||
private $iPlaceID;
|
||||
private $aAddressLines;
|
||||
|
||||
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
||||
{
|
||||
$this->iPlaceID = $iPlaceID;
|
||||
|
||||
if (is_array($mLangPref)) {
|
||||
$mLangPref = $oDB->getArraySQL($oDB->getDBQuotedList($mLangPref));
|
||||
}
|
||||
@@ -58,48 +61,94 @@ class AddressDetails
|
||||
return join(', ', $aParts);
|
||||
}
|
||||
|
||||
public function getAddressNames()
|
||||
public function getAddressNames($sCountry = null)
|
||||
{
|
||||
$aAddress = array();
|
||||
$aFallback = array();
|
||||
|
||||
foreach ($this->aAddressLines as $aLine) {
|
||||
if (!self::isAddress($aLine)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bFallback = false;
|
||||
$aTypeLabel = ClassTypes\getInfo($aLine);
|
||||
$sTypeLabel = ClassTypes\getLabelTag($aLine);
|
||||
|
||||
if ($aTypeLabel === false) {
|
||||
$aTypeLabel = ClassTypes\getFallbackInfo($aLine);
|
||||
$bFallback = true;
|
||||
}
|
||||
|
||||
$sName = false;
|
||||
if (isset($aLine['localname']) && $aLine['localname']) {
|
||||
$sName = null;
|
||||
if (isset($aLine['localname']) && $aLine['localname']!=='') {
|
||||
$sName = $aLine['localname'];
|
||||
} elseif (isset($aLine['housenumber']) && $aLine['housenumber']) {
|
||||
} 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($sName)) {
|
||||
$sTypeLabel = strtolower(str_replace(' ', '_', $sTypeLabel));
|
||||
if (!isset($aAddress[$sTypeLabel])
|
||||
|| isset($aFallback[$sTypeLabel])
|
||||
|| $aLine['class'] == 'place'
|
||||
) {
|
||||
$aAddress[$sTypeLabel] = $sName;
|
||||
if ($bFallback) {
|
||||
$aFallback[$sTypeLabel] = $bFallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates the given json with geocodejson address information fields.
|
||||
*
|
||||
* @param array $aJson Json hash to add the fields to.
|
||||
*
|
||||
* Geocodejson has the following fields:
|
||||
* street, locality, postcode, city, district,
|
||||
* county, state, country
|
||||
*
|
||||
* Postcode and housenumber are added by type, district is not used.
|
||||
* All other fields are set according to address rank.
|
||||
*/
|
||||
public function addGeocodeJsonAddressParts(&$aJson)
|
||||
{
|
||||
foreach (array_reverse($this->aAddressLines) as $aLine) {
|
||||
if (!$aLine['isaddress']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($aLine['localname']) || $aLine['localname'] == '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($aLine['type'] == 'postcode' || $aLine['type'] == 'postal_code') {
|
||||
$aJson['postcode'] = $aLine['localname'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($aLine['type'] == 'house_number') {
|
||||
$aJson['housenumber'] = $aLine['localname'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->iPlaceID == $aLine['place_id']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$iRank = (int)$aLine['rank_address'];
|
||||
|
||||
if ($iRank > 25 && $iRank < 28) {
|
||||
$aJson['street'] = $aLine['localname'];
|
||||
} elseif ($iRank >= 22 && $iRank <= 25) {
|
||||
$aJson['locality'] = $aLine['localname'];
|
||||
} elseif ($iRank >= 17 && $iRank <= 21) {
|
||||
$aJson['district'] = $aLine['localname'];
|
||||
} elseif ($iRank >= 13 && $iRank <= 16) {
|
||||
$aJson['city'] = $aLine['localname'];
|
||||
} elseif ($iRank >= 10 && $iRank <= 12) {
|
||||
$aJson['county'] = $aLine['localname'];
|
||||
} elseif ($iRank >= 5 && $iRank <= 9) {
|
||||
$aJson['state'] = $aLine['localname'];
|
||||
} elseif ($iRank == 4) {
|
||||
$aJson['country'] = $aLine['localname'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAdminLevels()
|
||||
{
|
||||
$aAddress = array();
|
||||
|
||||
@@ -2,373 +2,567 @@
|
||||
|
||||
namespace Nominatim\ClassTypes;
|
||||
|
||||
function getInfo($aPlace)
|
||||
/**
|
||||
* Create a label tag for the given place that can be used as an XML name.
|
||||
*
|
||||
* @param array[] $aPlace Information about the place to label.
|
||||
*
|
||||
* A label tag groups various object types together under a common
|
||||
* label. The returned value is lower case and has no spaces
|
||||
*/
|
||||
function getLabelTag($aPlace, $sCountry = null)
|
||||
{
|
||||
$aClassType = getList();
|
||||
|
||||
if (isset($aPlace['admin_level'])) {
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||
if (isset($aClassType[$sName])) {
|
||||
return $aClassType[$sName];
|
||||
}
|
||||
$iRank = (int) ($aPlace['rank_address'] ?? 30);
|
||||
$sLabel;
|
||||
if (isset($aPlace['place_type'])) {
|
||||
$sLabel = $aPlace['place_type'];
|
||||
} elseif ($aPlace['class'] == 'boundary' && $aPlace['type'] == 'administrative') {
|
||||
$sLabel = getBoundaryLabel($iRank/2, $sCountry);
|
||||
} elseif ($aPlace['type'] == 'postal_code') {
|
||||
$sLabel = 'postcode';
|
||||
} elseif ($iRank < 26) {
|
||||
$sLabel = $aPlace['type'];
|
||||
} elseif ($iRank < 28) {
|
||||
$sLabel = 'road';
|
||||
} elseif ($aPlace['class'] == 'place'
|
||||
&& ($aPlace['type'] == 'house_number' ||
|
||||
$aPlace['type'] == 'house_name' ||
|
||||
$aPlace['type'] == 'country_code')
|
||||
) {
|
||||
$sLabel = $aPlace['type'];
|
||||
} else {
|
||||
$sLabel = $aPlace['class'];
|
||||
}
|
||||
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||
if (isset($aClassType[$sName])) {
|
||||
return $aClassType[$sName];
|
||||
}
|
||||
|
||||
return false;
|
||||
return strtolower(str_replace(' ', '_', $sLabel));
|
||||
}
|
||||
|
||||
function getFallbackInfo($aPlace)
|
||||
/**
|
||||
* Create a label for the given place.
|
||||
*
|
||||
* @param array[] $aPlace Information about the place to label.
|
||||
*/
|
||||
function getLabel($aPlace, $sCountry = null)
|
||||
{
|
||||
$aClassType = getList();
|
||||
|
||||
$sFallback = 'boundary:administrative:'.((int)($aPlace['rank_address']/2));
|
||||
if (isset($aClassType[$sFallback])) {
|
||||
return $aClassType[$sFallback];
|
||||
if (isset($aPlace['place_type'])) {
|
||||
return ucwords(str_replace('_', ' ', $aPlace['place_type']));
|
||||
}
|
||||
|
||||
return array('simplelabel' => 'address'.$aPlace['rank_address']);
|
||||
if ($aPlace['class'] == 'boundary' && $aPlace['type'] == 'administrative') {
|
||||
return getBoundaryLabel(($aPlace['rank_address'] ?? 30)/2, $sCountry ?? null);
|
||||
}
|
||||
|
||||
// Return a label only for 'important' class/type combinations
|
||||
if (getImportance($aPlace) !== null) {
|
||||
return ucwords(str_replace('_', ' ', $aPlace['type']));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getProperty($aPlace, $sProp, $mDefault = false)
|
||||
|
||||
/**
|
||||
* Return a simple label for an administrative boundary for the given country.
|
||||
*
|
||||
* @param int $iAdminLevel Content of admin_level tag.
|
||||
* @param string $sCountry Country code of the country where the object is
|
||||
* in. May be null, in which case a world-wide
|
||||
* fallback is used.
|
||||
* @param string $sFallback String to return if no explicit string is listed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getBoundaryLabel($iAdminLevel, $sCountry, $sFallback = 'Administrative')
|
||||
{
|
||||
$aClassType = getList();
|
||||
static $aBoundaryList = array (
|
||||
'default' => array (
|
||||
1 => 'Continent',
|
||||
2 => 'Country',
|
||||
3 => 'Region',
|
||||
4 => 'State',
|
||||
5 => 'State District',
|
||||
6 => 'County',
|
||||
7 => 'Municipality',
|
||||
8 => 'City',
|
||||
9 => 'City District',
|
||||
10 => 'Suburb',
|
||||
11 => 'Neighbourhood',
|
||||
12 => 'City Block'
|
||||
),
|
||||
'no' => array (
|
||||
3 => 'State',
|
||||
4 => 'County'
|
||||
),
|
||||
'se' => array (
|
||||
3 => 'State',
|
||||
4 => 'County'
|
||||
)
|
||||
);
|
||||
|
||||
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];
|
||||
}
|
||||
if (isset($aBoundaryList[$sCountry])
|
||||
&& isset($aBoundaryList[$sCountry][$iAdminLevel])
|
||||
) {
|
||||
return $aBoundaryList[$sCountry][$iAdminLevel];
|
||||
}
|
||||
|
||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
||||
return $aClassType[$sName][$sProp];
|
||||
}
|
||||
|
||||
return $mDefault;
|
||||
return $aBoundaryList['default'][$iAdminLevel] ?? $sFallback;
|
||||
}
|
||||
|
||||
function getListWithImportance()
|
||||
/**
|
||||
* Return an estimated radius of how far the object node extends.
|
||||
*
|
||||
* @param array[] $aPlace Information about the place. This must be a node
|
||||
* feature.
|
||||
*
|
||||
* @return float The radius around the feature in degrees.
|
||||
*/
|
||||
function getDefRadius($aPlace)
|
||||
{
|
||||
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),
|
||||
$aSpecialRadius = array(
|
||||
'place:continent' => 25,
|
||||
'place:country' => 7,
|
||||
'place:state' => 2.6,
|
||||
'place:province' => 2.6,
|
||||
'place:region' => 1.0,
|
||||
'place:county' => 0.7,
|
||||
'place:city' => 0.16,
|
||||
'place:municipality' => 0.16,
|
||||
'place:island' => 0.32,
|
||||
'place:postcode' => 0.16,
|
||||
'place:town' => 0.04,
|
||||
'place:village' => 0.02,
|
||||
'place:hamlet' => 0.02,
|
||||
'place:district' => 0.02,
|
||||
'place:borough' => 0.02,
|
||||
'place:suburb' => 0.02,
|
||||
'place:locality' => 0.01,
|
||||
'place:neighbourhood'=> 0.01,
|
||||
'place:quarter' => 0.01,
|
||||
'place:city_block' => 0.01,
|
||||
'landuse:farm' => 0.01,
|
||||
'place:farm' => 0.01,
|
||||
'place:airport' => 0.015,
|
||||
'aeroway:aerodrome' => 0.015,
|
||||
'railway:station' => 0.005
|
||||
);
|
||||
|
||||
$sClassPlace = $aPlace['class'].':'.$aPlace['type'];
|
||||
|
||||
return $aSpecialRadius[$sClassPlace] ?? 0.00005;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon to use with the given object.
|
||||
*/
|
||||
function getIcon($aPlace)
|
||||
{
|
||||
$aIcons = array(
|
||||
'boundary:administrative' => 'poi_boundary_administrative',
|
||||
'place:city' => 'poi_place_city',
|
||||
'place:town' => 'poi_place_town',
|
||||
'place:village' => 'poi_place_village',
|
||||
'place:hamlet' => 'poi_place_village',
|
||||
'place:suburb' => 'poi_place_village',
|
||||
'place:locality' => 'poi_place_village',
|
||||
'place:airport' => 'transport_airport2',
|
||||
'aeroway:aerodrome' => 'transport_airport2',
|
||||
'railway:station' => 'transport_train_station2',
|
||||
'amenity:place_of_worship' => 'place_of_worship_unknown3',
|
||||
'amenity:pub' => 'food_pub',
|
||||
'amenity:bar' => 'food_bar',
|
||||
'amenity:university' => 'education_university',
|
||||
'tourism:museum' => 'tourist_museum',
|
||||
'amenity:arts_centre' => 'tourist_art_gallery2',
|
||||
'tourism:zoo' => 'tourist_zoo',
|
||||
'tourism:theme_park' => 'poi_point_of_interest',
|
||||
'tourism:attraction' => 'poi_point_of_interest',
|
||||
'leisure:golf_course' => 'sport_golf',
|
||||
'historic:castle' => 'tourist_castle',
|
||||
'amenity:hospital' => 'health_hospital',
|
||||
'amenity:school' => 'education_school',
|
||||
'amenity:theatre' => 'tourist_theatre',
|
||||
'amenity:library' => 'amenity_library',
|
||||
'amenity:fire_station' => 'amenity_firestation3',
|
||||
'amenity:police' => 'amenity_police2',
|
||||
'amenity:bank' => 'money_bank2',
|
||||
'amenity:post_office' => 'amenity_post_office',
|
||||
'tourism:hotel' => 'accommodation_hotel2',
|
||||
'amenity:cinema' => 'tourist_cinema',
|
||||
'tourism:artwork' => 'tourist_art_gallery2',
|
||||
'historic:archaeological_site' => 'tourist_archaeological2',
|
||||
'amenity:doctors' => 'health_doctors',
|
||||
'leisure:sports_centre' => 'sport_leisure_centre',
|
||||
'leisure:swimming_pool' => 'sport_swimming_outdoor',
|
||||
'shop:supermarket' => 'shopping_supermarket',
|
||||
'shop:convenience' => 'shopping_convenience',
|
||||
'amenity:restaurant' => 'food_restaurant',
|
||||
'amenity:fast_food' => 'food_fastfood',
|
||||
'amenity:cafe' => 'food_cafe',
|
||||
'tourism:guest_house' => 'accommodation_bed_and_breakfast',
|
||||
'amenity:pharmacy' => 'health_pharmacy_dispensing',
|
||||
'amenity:fuel' => 'transport_fuel',
|
||||
'natural:peak' => 'poi_peak',
|
||||
'natural:wood' => 'landuse_coniferous_and_deciduous',
|
||||
'shop:bicycle' => 'shopping_bicycle',
|
||||
'shop:clothes' => 'shopping_clothes',
|
||||
'shop:hairdresser' => 'shopping_hairdresser',
|
||||
'shop:doityourself' => 'shopping_diy',
|
||||
'shop:estate_agent' => 'shopping_estateagent2',
|
||||
'shop:car' => 'shopping_car',
|
||||
'shop:garden_centre' => 'shopping_garden_centre',
|
||||
'shop:car_repair' => 'shopping_car_repair',
|
||||
'shop:bakery' => 'shopping_bakery',
|
||||
'shop:butcher' => 'shopping_butcher',
|
||||
'shop:apparel' => 'shopping_clothes',
|
||||
'shop:laundry' => 'shopping_laundrette',
|
||||
'shop:beverages' => 'shopping_alcohol',
|
||||
'shop:alcohol' => 'shopping_alcohol',
|
||||
'shop:optician' => 'health_opticians',
|
||||
'shop:chemist' => 'health_pharmacy',
|
||||
'shop:gallery' => 'tourist_art_gallery2',
|
||||
'shop:jewelry' => 'shopping_jewelry',
|
||||
'tourism:information' => 'amenity_information',
|
||||
'historic:ruins' => 'tourist_ruin',
|
||||
'amenity:college' => 'education_school',
|
||||
'historic:monument' => 'tourist_monument',
|
||||
'historic:memorial' => 'tourist_monument',
|
||||
'historic:mine' => 'poi_mine',
|
||||
'tourism:caravan_site' => 'accommodation_caravan_park',
|
||||
'amenity:bus_station' => 'transport_bus_station',
|
||||
'amenity:atm' => 'money_atm2',
|
||||
'tourism:viewpoint' => 'tourist_view_point',
|
||||
'tourism:guesthouse' => 'accommodation_bed_and_breakfast',
|
||||
'railway:tram' => 'transport_tram_stop',
|
||||
'amenity:courthouse' => 'amenity_court',
|
||||
'amenity:recycling' => 'amenity_recycling',
|
||||
'amenity:dentist' => 'health_dentist',
|
||||
'natural:beach' => 'tourist_beach',
|
||||
'railway:tram_stop' => 'transport_tram_stop',
|
||||
'amenity:prison' => 'amenity_prison',
|
||||
'highway:bus_stop' => 'transport_bus_stop2'
|
||||
);
|
||||
|
||||
$sClassPlace = $aPlace['class'].':'.$aPlace['type'];
|
||||
|
||||
return $aIcons[$sClassPlace] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an icon for the given object with its full URL.
|
||||
*/
|
||||
function getIconFile($aPlace)
|
||||
{
|
||||
if (CONST_MapIcon_URL === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sIcon = getIcon($aPlace);
|
||||
|
||||
if (!isset($sIcon)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CONST_MapIcon_URL.'/'.$sIcon.'.p.20.png';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a class importance value for the given place.
|
||||
*
|
||||
* @param array[] $aPlace Information about the place.
|
||||
*
|
||||
* @return int An importance value. The lower the value, the more
|
||||
* important the class.
|
||||
*/
|
||||
function getImportance($aPlace)
|
||||
{
|
||||
static $aWithImportance = null;
|
||||
|
||||
if ($aWithImportance === null) {
|
||||
$aWithImportance = array_flip(array(
|
||||
'boundary:administrative',
|
||||
'place:country',
|
||||
'place:state',
|
||||
'place:province',
|
||||
'place:county',
|
||||
'place:city',
|
||||
'place:region',
|
||||
'place:island',
|
||||
'place:town',
|
||||
'place:village',
|
||||
'place:hamlet',
|
||||
'place:suburb',
|
||||
'place:locality',
|
||||
'landuse:farm',
|
||||
'place:farm',
|
||||
'highway:motorway_junction',
|
||||
'highway:motorway',
|
||||
'highway:trunk',
|
||||
'highway:primary',
|
||||
'highway:secondary',
|
||||
'highway:tertiary',
|
||||
'highway:residential',
|
||||
'highway:unclassified',
|
||||
'highway:living_street',
|
||||
'highway:service',
|
||||
'highway:track',
|
||||
'highway:road',
|
||||
'highway:byway',
|
||||
'highway:bridleway',
|
||||
'highway:cycleway',
|
||||
'highway:pedestrian',
|
||||
'highway:footway',
|
||||
'highway:steps',
|
||||
'highway:motorway_link',
|
||||
'highway:trunk_link',
|
||||
'highway:primary_link',
|
||||
'landuse:industrial',
|
||||
'landuse:residential',
|
||||
'landuse:retail',
|
||||
'landuse:commercial',
|
||||
'place:airport',
|
||||
'aeroway:aerodrome',
|
||||
'railway:station',
|
||||
'amenity:place_of_worship',
|
||||
'amenity:pub',
|
||||
'amenity:bar',
|
||||
'amenity:university',
|
||||
'tourism:museum',
|
||||
'amenity:arts_centre',
|
||||
'tourism:zoo',
|
||||
'tourism:theme_park',
|
||||
'tourism:attraction',
|
||||
'leisure:golf_course',
|
||||
'historic:castle',
|
||||
'amenity:hospital',
|
||||
'amenity:school',
|
||||
'amenity:theatre',
|
||||
'amenity:public_building',
|
||||
'amenity:library',
|
||||
'amenity:townhall',
|
||||
'amenity:community_centre',
|
||||
'amenity:fire_station',
|
||||
'amenity:police',
|
||||
'amenity:bank',
|
||||
'amenity:post_office',
|
||||
'leisure:park',
|
||||
'amenity:park',
|
||||
'landuse:park',
|
||||
'landuse:recreation_ground',
|
||||
'tourism:hotel',
|
||||
'tourism:motel',
|
||||
'amenity:cinema',
|
||||
'tourism:artwork',
|
||||
'historic:archaeological_site',
|
||||
'amenity:doctors',
|
||||
'leisure:sports_centre',
|
||||
'leisure:swimming_pool',
|
||||
'shop:supermarket',
|
||||
'shop:convenience',
|
||||
'amenity:restaurant',
|
||||
'amenity:fast_food',
|
||||
'amenity:cafe',
|
||||
'tourism:guest_house',
|
||||
'amenity:pharmacy',
|
||||
'amenity:fuel',
|
||||
'natural:peak',
|
||||
'waterway:waterfall',
|
||||
'natural:wood',
|
||||
'natural:water',
|
||||
'landuse:forest',
|
||||
'landuse:cemetery',
|
||||
'landuse:allotments',
|
||||
'landuse:farmyard',
|
||||
'railway:rail',
|
||||
'waterway:canal',
|
||||
'waterway:river',
|
||||
'waterway:stream',
|
||||
'shop:bicycle',
|
||||
'shop:clothes',
|
||||
'shop:hairdresser',
|
||||
'shop:doityourself',
|
||||
'shop:estate_agent',
|
||||
'shop:car',
|
||||
'shop:garden_centre',
|
||||
'shop:car_repair',
|
||||
'shop:newsagent',
|
||||
'shop:bakery',
|
||||
'shop:furniture',
|
||||
'shop:butcher',
|
||||
'shop:apparel',
|
||||
'shop:electronics',
|
||||
'shop:department_store',
|
||||
'shop:books',
|
||||
'shop:yes',
|
||||
'shop:outdoor',
|
||||
'shop:mall',
|
||||
'shop:florist',
|
||||
'shop:charity',
|
||||
'shop:hardware',
|
||||
'shop:laundry',
|
||||
'shop:shoes',
|
||||
'shop:beverages',
|
||||
'shop:dry_cleaning',
|
||||
'shop:carpet',
|
||||
'shop:computer',
|
||||
'shop:alcohol',
|
||||
'shop:optician',
|
||||
'shop:chemist',
|
||||
'shop:gallery',
|
||||
'shop:mobile_phone',
|
||||
'shop:sports',
|
||||
'shop:jewelry',
|
||||
'shop:pet',
|
||||
'shop:beauty',
|
||||
'shop:stationery',
|
||||
'shop:shopping_centre',
|
||||
'shop:general',
|
||||
'shop:electrical',
|
||||
'shop:toys',
|
||||
'shop:jeweller',
|
||||
'shop:betting',
|
||||
'shop:household',
|
||||
'shop:travel_agency',
|
||||
'shop:hifi',
|
||||
'amenity:shop',
|
||||
'tourism:information',
|
||||
'place:house',
|
||||
'place:house_name',
|
||||
'place:house_number',
|
||||
'place:country_code',
|
||||
'leisure:pitch',
|
||||
'highway:unsurfaced',
|
||||
'historic:ruins',
|
||||
'amenity:college',
|
||||
'historic:monument',
|
||||
'railway:subway',
|
||||
'historic:memorial',
|
||||
'leisure:nature_reserve',
|
||||
'leisure:common',
|
||||
'waterway:lock_gate',
|
||||
'natural:fell',
|
||||
'amenity:nightclub',
|
||||
'highway:path',
|
||||
'leisure:garden',
|
||||
'landuse:reservoir',
|
||||
'leisure:playground',
|
||||
'leisure:stadium',
|
||||
'historic:mine',
|
||||
'natural:cliff',
|
||||
'tourism:caravan_site',
|
||||
'amenity:bus_station',
|
||||
'amenity:kindergarten',
|
||||
'highway:construction',
|
||||
'amenity:atm',
|
||||
'amenity:emergency_phone',
|
||||
'waterway:lock',
|
||||
'waterway:riverbank',
|
||||
'natural:coastline',
|
||||
'tourism:viewpoint',
|
||||
'tourism:hostel',
|
||||
'tourism:bed_and_breakfast',
|
||||
'railway:halt',
|
||||
'railway:platform',
|
||||
'railway:tram',
|
||||
'amenity:courthouse',
|
||||
'amenity:recycling',
|
||||
'amenity:dentist',
|
||||
'natural:beach',
|
||||
'place:moor',
|
||||
'amenity:grave_yard',
|
||||
'waterway:drain',
|
||||
'landuse:grass',
|
||||
'landuse:village_green',
|
||||
'natural:bay',
|
||||
'railway:tram_stop',
|
||||
'leisure:marina',
|
||||
'highway:stile',
|
||||
'natural:moor',
|
||||
'railway:light_rail',
|
||||
'railway:narrow_gauge',
|
||||
'natural:land',
|
||||
'amenity:village_hall',
|
||||
'waterway:dock',
|
||||
'amenity:veterinary',
|
||||
'landuse:brownfield',
|
||||
'leisure:track',
|
||||
'railway:historic_station',
|
||||
'landuse:construction',
|
||||
'amenity:prison',
|
||||
'landuse:quarry',
|
||||
'amenity:telephone',
|
||||
'highway:traffic_signals',
|
||||
'natural:heath',
|
||||
'historic:house',
|
||||
'amenity:social_club',
|
||||
'landuse:military',
|
||||
'amenity:health_centre',
|
||||
'historic:building',
|
||||
'amenity:clinic',
|
||||
'highway:services',
|
||||
'amenity:ferry_terminal',
|
||||
'natural:marsh',
|
||||
'natural:hill',
|
||||
'highway:raceway',
|
||||
'amenity:taxi',
|
||||
'amenity:take_away',
|
||||
'amenity:car_rental',
|
||||
'place:islet',
|
||||
'amenity:nursery',
|
||||
'amenity:nursing_home',
|
||||
'amenity:toilets',
|
||||
'amenity:hall',
|
||||
'waterway:boatyard',
|
||||
'highway:mini_roundabout',
|
||||
'historic:manor',
|
||||
'tourism:chalet',
|
||||
'amenity:bicycle_parking',
|
||||
'amenity:hotel',
|
||||
'waterway:weir',
|
||||
'natural:wetland',
|
||||
'natural:cave_entrance',
|
||||
'amenity:crematorium',
|
||||
'tourism:picnic_site',
|
||||
'landuse:wood',
|
||||
'landuse:basin',
|
||||
'natural:tree',
|
||||
'leisure:slipway',
|
||||
'landuse:meadow',
|
||||
'landuse:piste',
|
||||
'amenity:care_home',
|
||||
'amenity:club',
|
||||
'amenity:medical_centre',
|
||||
'historic:roman_road',
|
||||
'historic:fort',
|
||||
'railway:subway_entrance',
|
||||
'historic:yes',
|
||||
'highway:gate',
|
||||
'leisure:fishing',
|
||||
'historic:museum',
|
||||
'amenity:car_wash',
|
||||
'railway:level_crossing',
|
||||
'leisure:bird_hide',
|
||||
'natural:headland',
|
||||
'tourism:apartments',
|
||||
'amenity:shopping',
|
||||
'natural:scrub',
|
||||
'natural:fen',
|
||||
'building:yes',
|
||||
'mountain_pass:yes',
|
||||
'amenity:parking',
|
||||
'highway:bus_stop',
|
||||
'place:postcode',
|
||||
'amenity:post_box',
|
||||
'place:houses',
|
||||
'railway:preserved',
|
||||
'waterway:derelict_canal',
|
||||
'amenity:dead_pub',
|
||||
'railway:disused_station',
|
||||
'railway:abandoned',
|
||||
'railway:disused'
|
||||
));
|
||||
}
|
||||
|
||||
$sClassPlace = $aPlace['class'].':'.$aPlace['type'];
|
||||
|
||||
return $aWithImportance[$sClassPlace] ?? null;
|
||||
}
|
||||
|
||||
131
lib/DB.php
131
lib/DB.php
@@ -135,7 +135,7 @@ class DB
|
||||
try {
|
||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
||||
|
||||
while ($val = $stmt->fetchColumn(0)) { // returns first column or false
|
||||
while (($val = $stmt->fetchColumn(0)) !== false) { // returns first column or false
|
||||
$aVals[] = $val;
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
@@ -241,11 +241,103 @@ class DB
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the DSN includes the database name, checks if the connection works.
|
||||
* Returns a list of table names in the database
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function getListOfTables()
|
||||
{
|
||||
return $this->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a table. Returns true if deleted or didn't exist.
|
||||
*
|
||||
* @param string $sTableName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function databaseExists()
|
||||
public function deleteTable($sTableName)
|
||||
{
|
||||
return $this->exec('DROP TABLE IF EXISTS '.$sTableName.' CASCADE') == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an index exists in the database. Optional filtered by tablename
|
||||
*
|
||||
* @param string $sTableName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function indexExists($sIndexName, $sTableName = null)
|
||||
{
|
||||
return in_array($sIndexName, $this->getListOfIndices($sTableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of index names in the database, optional filtered by tablename
|
||||
*
|
||||
* @param string $sTableName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getListOfIndices($sTableName = null)
|
||||
{
|
||||
// table_name | index_name | column_name
|
||||
// -----------------------+---------------------------------+--------------
|
||||
// country_name | idx_country_name_country_code | country_code
|
||||
// country_osm_grid | idx_country_osm_grid_geometry | geometry
|
||||
// import_polygon_delete | idx_import_polygon_delete_osmid | osm_id
|
||||
// import_polygon_delete | idx_import_polygon_delete_osmid | osm_type
|
||||
// import_polygon_error | idx_import_polygon_error_osmid | osm_id
|
||||
// import_polygon_error | idx_import_polygon_error_osmid | osm_type
|
||||
$sSql = <<< END
|
||||
SELECT
|
||||
t.relname as table_name,
|
||||
i.relname as index_name,
|
||||
a.attname as column_name
|
||||
FROM
|
||||
pg_class t,
|
||||
pg_class i,
|
||||
pg_index ix,
|
||||
pg_attribute a
|
||||
WHERE
|
||||
t.oid = ix.indrelid
|
||||
and i.oid = ix.indexrelid
|
||||
and a.attrelid = t.oid
|
||||
and a.attnum = ANY(ix.indkey)
|
||||
and t.relkind = 'r'
|
||||
and i.relname NOT LIKE 'pg_%'
|
||||
FILTERS
|
||||
ORDER BY
|
||||
t.relname,
|
||||
i.relname,
|
||||
a.attname
|
||||
END;
|
||||
|
||||
$aRows = null;
|
||||
if ($sTableName) {
|
||||
$sSql = str_replace('FILTERS', 'and t.relname = :tablename', $sSql);
|
||||
$aRows = $this->getAll($sSql, array(':tablename' => $sTableName));
|
||||
} else {
|
||||
$sSql = str_replace('FILTERS', '', $sSql);
|
||||
$aRows = $this->getAll($sSql);
|
||||
}
|
||||
|
||||
$aIndexNames = array_unique(array_map(function ($aRow) {
|
||||
return $aRow['index_name'];
|
||||
}, $aRows));
|
||||
sort($aIndexNames);
|
||||
|
||||
return $aIndexNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to connect to the database but on failure doesn't throw an exception.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkConnection()
|
||||
{
|
||||
$bExists = true;
|
||||
try {
|
||||
@@ -280,11 +372,18 @@ class DB
|
||||
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associate array of postgresql database connection settings. Keys can
|
||||
* be 'database', 'hostspec', 'port', 'username', 'password'.
|
||||
* Returns empty array on failure, thus check if at least 'database' is set.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function parseDSN($sDSN)
|
||||
{
|
||||
// https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php
|
||||
$aInfo = array();
|
||||
if (preg_match('/^pgsql:(.+)/', $sDSN, $aMatches)) {
|
||||
if (preg_match('/^pgsql:(.+)$/', $sDSN, $aMatches)) {
|
||||
foreach (explode(';', $aMatches[1]) as $sKeyVal) {
|
||||
list($sKey, $sVal) = explode('=', $sKeyVal, 2);
|
||||
if ($sKey == 'host') $sKey = 'hostspec';
|
||||
@@ -295,4 +394,28 @@ class DB
|
||||
}
|
||||
return $aInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of settings and return the DNS string. Key names can be
|
||||
* 'database', 'hostspec', 'port', 'username', 'password' but aliases
|
||||
* 'dbname', 'host' and 'user' are also supported.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public static function generateDSN($aInfo)
|
||||
{
|
||||
$sDSN = sprintf(
|
||||
'pgsql:host=%s;port=%s;dbname=%s;user=%s;password=%s;',
|
||||
$aInfo['host'] ?? $aInfo['hostspec'] ?? '',
|
||||
$aInfo['port'] ?? '',
|
||||
$aInfo['dbname'] ?? $aInfo['database'] ?? '',
|
||||
$aInfo['user'] ?? '',
|
||||
$aInfo['password'] ?? ''
|
||||
);
|
||||
$sDSN = preg_replace('/\b\w+=;/', '', $sDSN);
|
||||
$sDSN = preg_replace('/;\Z/', '', $sDSN);
|
||||
|
||||
return $sDSN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,6 @@ class Geocode
|
||||
}
|
||||
|
||||
$this->oPlaceLookup->loadParamArray($oParams, $sForceGeometryType);
|
||||
$this->oPlaceLookup->setIncludePolygonAsPoints($oParams->getBool('polygon'));
|
||||
$this->oPlaceLookup->setIncludeAddressDetails($oParams->getBool('addressdetails', false));
|
||||
}
|
||||
|
||||
@@ -643,12 +642,6 @@ class Geocode
|
||||
$oValidTokens = new TokenList();
|
||||
|
||||
if (!empty($aTokens)) {
|
||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, operator, search_name_count';
|
||||
$sSQL .= ' FROM word ';
|
||||
$sSQL .= ' WHERE word_token in ('.join(',', $this->oDB->getDBQuotedList($aTokens)).')';
|
||||
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$oValidTokens->addTokensFromDB(
|
||||
$this->oDB,
|
||||
$aTokens,
|
||||
@@ -657,6 +650,8 @@ class Geocode
|
||||
$this->oNormalizer
|
||||
);
|
||||
|
||||
$oCtx->setFullNameWords($oValidTokens->getFullWordIDs());
|
||||
|
||||
// Try more interpretations for Tokens that could not be matched.
|
||||
foreach ($aTokens as $sToken) {
|
||||
if ($sToken[0] == ' ' && !$oValidTokens->contains($sToken)) {
|
||||
@@ -808,9 +803,7 @@ class Geocode
|
||||
$sSQL .= 'WHERE place_id in ('.$sPlaceIds.') ';
|
||||
$sSQL .= ' AND (';
|
||||
$sSQL .= " placex.rank_address between $this->iMinAddressRank and $this->iMaxAddressRank ";
|
||||
if (14 >= $this->iMinAddressRank && 14 <= $this->iMaxAddressRank) {
|
||||
$sSQL .= " OR (extratags->'place') = 'city'";
|
||||
}
|
||||
$sSQL .= " OR placex.rank_search between $this->iMinAddressRank and $this->iMaxAddressRank ";
|
||||
if ($this->aAddressRankList) {
|
||||
$sSQL .= ' OR placex.rank_address in ('.join(',', $this->aAddressRankList).')';
|
||||
}
|
||||
@@ -890,7 +883,6 @@ class Geocode
|
||||
|
||||
$aSearchResults = $this->oPlaceLookup->lookup($aResults);
|
||||
|
||||
$aClassType = ClassTypes\getListWithImportance();
|
||||
$aRecheckWords = preg_split('/\b[\s,\\-]*/u', $sQuery);
|
||||
foreach ($aRecheckWords as $i => $sWord) {
|
||||
if (!preg_match('/[\pL\pN]/', $sWord)) unset($aRecheckWords[$i]);
|
||||
@@ -899,33 +891,23 @@ class Geocode
|
||||
Debug::printVar('Recheck words', $aRecheckWords);
|
||||
|
||||
foreach ($aSearchResults as $iIdx => $aResult) {
|
||||
// Default
|
||||
$fDiameter = ClassTypes\getProperty($aResult, 'defdiameter', 0.0001);
|
||||
$fRadius = ClassTypes\getDefRadius($aResult);
|
||||
|
||||
$aOutlineResult = $this->oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fDiameter/2);
|
||||
$aOutlineResult = $this->oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fRadius);
|
||||
if ($aOutlineResult) {
|
||||
$aResult = array_merge($aResult, $aOutlineResult);
|
||||
}
|
||||
|
||||
if ($aResult['extra_place'] == 'city') {
|
||||
$aResult['class'] = 'place';
|
||||
$aResult['type'] = 'city';
|
||||
$aResult['rank_search'] = 16;
|
||||
}
|
||||
|
||||
// Is there an icon set for this type of result?
|
||||
$aClassInfo = ClassTypes\getInfo($aResult);
|
||||
|
||||
if ($aClassInfo) {
|
||||
if (isset($aClassInfo['icon'])) {
|
||||
$aResult['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aClassInfo['icon'].'.p.20.png';
|
||||
}
|
||||
|
||||
if (isset($aClassInfo['label'])) {
|
||||
$aResult['label'] = $aClassInfo['label'];
|
||||
}
|
||||
$sIcon = ClassTypes\getIconFile($aResult);
|
||||
if (isset($sIcon)) {
|
||||
$aResult['icon'] = $sIcon;
|
||||
}
|
||||
|
||||
$sLabel = ClassTypes\getLabel($aResult);
|
||||
if (isset($sLabel)) {
|
||||
$aResult['label'] = $sLabel;
|
||||
}
|
||||
$aResult['name'] = $aResult['langaddress'];
|
||||
|
||||
if ($oCtx->hasNearPoint()) {
|
||||
@@ -937,6 +919,26 @@ class Geocode
|
||||
$aResult['lon'],
|
||||
$aResult['lat']
|
||||
);
|
||||
|
||||
// secondary ordering (for results with same importance (the smaller the better):
|
||||
// - approximate importance of address parts
|
||||
if (isset($aResult['addressimportance']) && $aResult['addressimportance']) {
|
||||
$aResult['foundorder'] = -$aResult['addressimportance']/10;
|
||||
} else {
|
||||
$aResult['foundorder'] = -$aResult['importance'];
|
||||
}
|
||||
// - number of exact matches from the query
|
||||
$aResult['foundorder'] -= $aResults[$aResult['place_id']]->iExactMatches;
|
||||
// - importance of the class/type
|
||||
$iClassImportance = ClassTypes\getImportance($aResult);
|
||||
if (isset($iClassImportance)) {
|
||||
$aResult['foundorder'] += 0.0001 * $iClassImportance;
|
||||
} else {
|
||||
$aResult['foundorder'] += 0.01;
|
||||
}
|
||||
// - rank
|
||||
$aResult['foundorder'] -= 0.00001 * (30 - $aResult['rank_search']);
|
||||
|
||||
// Adjust importance for the number of exact string matches in the result
|
||||
$iCountWords = 0;
|
||||
$sAddress = $aResult['langaddress'];
|
||||
@@ -947,21 +949,8 @@ class Geocode
|
||||
}
|
||||
}
|
||||
|
||||
$aResult['importance'] = $aResult['importance'] + ($iCountWords*0.1); // 0.1 is a completely arbitrary number but something in the range 0.1 to 0.5 would seem right
|
||||
|
||||
// secondary ordering (for results with same importance (the smaller the better):
|
||||
// - approximate importance of address parts
|
||||
$aResult['foundorder'] = -$aResult['addressimportance']/10;
|
||||
// - number of exact matches from the query
|
||||
$aResult['foundorder'] -= $aResults[$aResult['place_id']]->iExactMatches;
|
||||
// - importance of the class/type
|
||||
if (isset($aClassType[$aResult['class'].':'.$aResult['type']]['importance'])
|
||||
&& $aClassType[$aResult['class'].':'.$aResult['type']]['importance']
|
||||
) {
|
||||
$aResult['foundorder'] += 0.0001 * $aClassType[$aResult['class'].':'.$aResult['type']]['importance'];
|
||||
} else {
|
||||
$aResult['foundorder'] += 0.01;
|
||||
}
|
||||
// 0.1 is a completely arbitrary number but something in the range 0.1 to 0.5 would seem right
|
||||
$aResult['importance'] = $aResult['importance'] + ($iCountWords*0.1);
|
||||
}
|
||||
$aSearchResults[$iIdx] = $aResult;
|
||||
}
|
||||
|
||||
@@ -104,18 +104,29 @@ class ParameterParser
|
||||
}
|
||||
|
||||
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['short_name:'.$sLanguage] = 'short_name:'.$sLanguage;
|
||||
}
|
||||
$aLangPrefOrder['official_name'] = 'official_name';
|
||||
$aLangPrefOrder['short_name'] = 'short_name';
|
||||
$aLangPrefOrder['ref'] = 'ref';
|
||||
$aLangPrefOrder['type'] = 'type';
|
||||
return $aLangPrefOrder;
|
||||
}
|
||||
|
||||
public function hasSetAny($aParamNames)
|
||||
{
|
||||
foreach ($aParamNames as $sName) {
|
||||
if ($this->getBool($sName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ class PlaceLookup
|
||||
protected $bExtraTags = false;
|
||||
protected $bNameDetails = false;
|
||||
|
||||
protected $bIncludePolygonAsPoints = false;
|
||||
protected $bIncludePolygonAsText = false;
|
||||
protected $bIncludePolygonAsGeoJSON = false;
|
||||
protected $bIncludePolygonAsKML = false;
|
||||
@@ -38,11 +37,6 @@ class PlaceLookup
|
||||
return $this->bDeDupe;
|
||||
}
|
||||
|
||||
public function setIncludePolygonAsPoints($b = true)
|
||||
{
|
||||
$this->bIncludePolygonAsPoints = $b;
|
||||
}
|
||||
|
||||
public function setIncludeAddressDetails($b)
|
||||
{
|
||||
$this->bAddressDetails = $b;
|
||||
@@ -61,7 +55,6 @@ class PlaceLookup
|
||||
|
||||
if ($sGeomType === null || $sGeomType == 'geojson') {
|
||||
$this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson');
|
||||
$this->bIncludePolygonAsPoints = false;
|
||||
}
|
||||
|
||||
if ($oParams->getString('format', '') !== 'geojson') {
|
||||
@@ -100,7 +93,6 @@ class PlaceLookup
|
||||
if ($this->bExtraTags) $aParams['extratags'] = '1';
|
||||
if ($this->bNameDetails) $aParams['namedetails'] = '1';
|
||||
|
||||
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';
|
||||
@@ -215,7 +207,7 @@ class PlaceLookup
|
||||
'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 .= " COALESCE(extratags->'place', extratags->'linked_place') AS extra_place ";
|
||||
$sSQL .= ' FROM placex';
|
||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||
$sSQL .= ' AND (';
|
||||
@@ -248,7 +240,7 @@ class PlaceLookup
|
||||
$sSQL .= ' ref, ';
|
||||
if ($this->bExtraTags) $sSQL .= 'extratags, ';
|
||||
if ($this->bNameDetails) $sSQL .= 'name, ';
|
||||
$sSQL .= " extratags->'place' ";
|
||||
$sSQL .= ' extra_place ';
|
||||
|
||||
$aSubSelects[] = $sSQL;
|
||||
}
|
||||
@@ -456,16 +448,24 @@ class PlaceLookup
|
||||
}
|
||||
}
|
||||
|
||||
$aPlace['addresstype'] = ClassTypes\getProperty(
|
||||
$aPlace['addresstype'] = ClassTypes\getLabelTag(
|
||||
$aPlace,
|
||||
'simplelabel',
|
||||
$aPlace['class']
|
||||
$aPlace['country_code']
|
||||
);
|
||||
|
||||
$aResults[$aPlace['place_id']] = $aPlace;
|
||||
}
|
||||
|
||||
Debug::printVar('Places', $aPlaces);
|
||||
$aResults = array_filter(
|
||||
$aResults,
|
||||
function ($v) {
|
||||
return !($v instanceof Result);
|
||||
}
|
||||
);
|
||||
|
||||
return $aPlaces;
|
||||
Debug::printVar('Places', $aResults);
|
||||
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
/* returns an array which will contain the keys
|
||||
@@ -500,7 +500,7 @@ class PlaceLookup
|
||||
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 ($this->bIncludePolygonAsText) $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))';
|
||||
@@ -527,8 +527,6 @@ class PlaceLookup
|
||||
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;
|
||||
@@ -551,17 +549,12 @@ class PlaceLookup
|
||||
|
||||
// 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;
|
||||
$aBounds = array(
|
||||
'minlat' => $fLat - $fRadius,
|
||||
'maxlat' => $fLat + $fRadius,
|
||||
'minlon' => $fLon - $fRadius,
|
||||
'maxlon' => $fLon + $fRadius
|
||||
);
|
||||
|
||||
$aOutlineResult['aBoundingBox'] = array(
|
||||
(string)$aBounds['minlat'],
|
||||
|
||||
@@ -54,6 +54,7 @@ class ReverseGeocode
|
||||
*/
|
||||
protected function lookupInterpolation($sPointSQL, $fSearchDiam)
|
||||
{
|
||||
Debug::newFunction('lookupInterpolation');
|
||||
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
|
||||
$sSQL .= ' ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
||||
$sSQL .= ' startnumber, endnumber, interpolationtype,';
|
||||
@@ -62,6 +63,7 @@ class ReverseGeocode
|
||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
|
||||
$sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
|
||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
return $this->oDB->getRow(
|
||||
$sSQL,
|
||||
@@ -88,16 +90,20 @@ class ReverseGeocode
|
||||
|
||||
protected function lookupInCountry($sPointSQL, $iMaxRank)
|
||||
{
|
||||
Debug::newFunction('lookupInCountry');
|
||||
// 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';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$sCountryCode = $this->oDB->getOne(
|
||||
$sSQL,
|
||||
null,
|
||||
'Could not determine country polygon containing the point.'
|
||||
);
|
||||
Debug::printVar('Country code', $sCountryCode);
|
||||
|
||||
if ($sCountryCode) {
|
||||
if ($iMaxRank > 4) {
|
||||
// look for place nodes with the given country code
|
||||
@@ -115,9 +121,11 @@ class ReverseGeocode
|
||||
$sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
|
||||
$sSQL .= ' ORDER BY rank_search DESC, distance ASC';
|
||||
$sSQL .= ' LIMIT 1';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
Debug::printVar('Country node', $aPlace);
|
||||
|
||||
if ($aPlace) {
|
||||
return new Result($aPlace['place_id']);
|
||||
}
|
||||
@@ -131,9 +139,10 @@ class ReverseGeocode
|
||||
$sSQL .= ' AND class in (\'boundary\', \'place\')';
|
||||
$sSQL .= ' AND linked_place_id is null';
|
||||
$sSQL .= ' ORDER BY distance ASC';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
Debug::printVar('Country place', $aPlace);
|
||||
if ($aPlace) {
|
||||
return new Result($aPlace['place_id']);
|
||||
}
|
||||
@@ -156,6 +165,7 @@ class ReverseGeocode
|
||||
*/
|
||||
protected function lookupPolygon($sPointSQL, $iMaxRank)
|
||||
{
|
||||
Debug::newFunction('lookupPolygon');
|
||||
// polygon search begins at suburb-level
|
||||
if ($iMaxRank > 25) $iMaxRank = 25;
|
||||
// no polygon search over country-level
|
||||
@@ -173,8 +183,10 @@ class ReverseGeocode
|
||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 50 ) as a';
|
||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$aPoly = $this->oDB->getRow($sSQL, null, 'Could not determine polygon containing the point.');
|
||||
Debug::printVar('Polygon result', $aPoly);
|
||||
|
||||
if ($aPoly) {
|
||||
// if a polygon is found, search for placenodes begins ...
|
||||
@@ -193,6 +205,7 @@ class ReverseGeocode
|
||||
// for place nodes at rank_address 16
|
||||
$sSQL .= ' AND rank_search > '.$iRankSearch;
|
||||
$sSQL .= ' AND rank_search <= '.$iMaxRank;
|
||||
$sSQL .= ' AND rank_address > 0';
|
||||
$sSQL .= ' AND class = \'place\'';
|
||||
$sSQL .= ' AND type != \'postcode\'';
|
||||
$sSQL .= ' AND name IS NOT NULL ';
|
||||
@@ -205,11 +218,12 @@ class ReverseGeocode
|
||||
$sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
|
||||
$sSQL .= ' ORDER BY distance ASC, rank_search DESC';
|
||||
$sSQL .= ' LIMIT 1';
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
if (CONST_Debug) var_dump($sSQL);
|
||||
$aPlacNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
if ($aPlacNode) {
|
||||
return $aPlacNode;
|
||||
$aPlaceNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
||||
Debug::printVar('Nearest place node', $aPlaceNode);
|
||||
if ($aPlaceNode) {
|
||||
return $aPlaceNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,6 +241,7 @@ class ReverseGeocode
|
||||
|
||||
public function lookupPoint($sPointSQL, $bDoInterpolation = true)
|
||||
{
|
||||
Debug::newFunction('lookupPoint');
|
||||
// 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,
|
||||
@@ -256,10 +271,11 @@ class ReverseGeocode
|
||||
$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);
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
||||
|
||||
if (CONST_Debug) var_dump($aPlace);
|
||||
Debug::printVar('POI/street level result', $aPlace);
|
||||
if ($aPlace) {
|
||||
$iPlaceID = $aPlace['place_id'];
|
||||
$oResult = new Result($iPlaceID);
|
||||
@@ -279,6 +295,7 @@ class ReverseGeocode
|
||||
}
|
||||
|
||||
$aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
|
||||
Debug::printVar('Interpolation result', $aPlace);
|
||||
|
||||
if ($aHouse) {
|
||||
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
|
||||
@@ -305,10 +322,12 @@ class ReverseGeocode
|
||||
$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);
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$aStreet = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
||||
Debug::printVar('Closest POI result', $aStreet);
|
||||
|
||||
if ($aStreet) {
|
||||
if (CONST_Debug) var_dump($aStreet);
|
||||
$oResult = new Result($aStreet['place_id']);
|
||||
}
|
||||
}
|
||||
@@ -326,10 +345,12 @@ class ReverseGeocode
|
||||
$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);
|
||||
Debug::printSQL($sSQL);
|
||||
|
||||
$aPlaceTiger = $this->oDB->getRow($sSQL, null, 'Could not determine closest Tiger place.');
|
||||
Debug::printVar('Tiger house number result', $aPlaceTiger);
|
||||
|
||||
if ($aPlaceTiger) {
|
||||
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
||||
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
||||
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
|
||||
}
|
||||
@@ -342,6 +363,8 @@ class ReverseGeocode
|
||||
// lower than street level ($iMaxRank < 26 )
|
||||
$oResult = $this->lookupLargeArea($sPointSQL, $iMaxRank);
|
||||
}
|
||||
|
||||
Debug::printVar('Final result', $oResult);
|
||||
return $oResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,18 @@ class SearchContext
|
||||
public $sqlCountryList = '';
|
||||
/// List of place IDs to exclude (as SQL).
|
||||
private $sqlExcludeList = '';
|
||||
/// Subset of word ids of full words in the query.
|
||||
private $aFullNameWords = array();
|
||||
|
||||
public function setFullNameWords($aWordList)
|
||||
{
|
||||
$this->aFullNameWords = $aWordList;
|
||||
}
|
||||
|
||||
public function getFullNameTerms()
|
||||
{
|
||||
return $this->aFullNameWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a reference point is defined.
|
||||
@@ -203,7 +214,7 @@ class SearchContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped for computing the distance from the reference point.
|
||||
* Get an SQL snippet for computing the distance from the reference point.
|
||||
*
|
||||
* @param string $sObj SQL variable name to compute the distance from.
|
||||
*
|
||||
@@ -215,7 +226,7 @@ class SearchContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped for checking if something is within range of the
|
||||
* Get an SQL snippet for checking if something is within range of the
|
||||
* reference point.
|
||||
*
|
||||
* @param string $sObj SQL variable name to compute if it is within range.
|
||||
@@ -228,14 +239,14 @@ class SearchContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL snipped of the importance factor of the viewbox.
|
||||
* Get an SQL snippet 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.
|
||||
* @return string SQL snippet of the factor with a leading multiply sign.
|
||||
*/
|
||||
public function viewboxImportanceSQL($sObj)
|
||||
{
|
||||
@@ -252,7 +263,7 @@ class SearchContext
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL snipped checking if a place ID should be excluded.
|
||||
* SQL snippet checking if a place ID should be excluded.
|
||||
*
|
||||
* @param string $sVariable SQL variable name of place ID to check,
|
||||
* potentially prefixed with more SQL.
|
||||
|
||||
@@ -21,8 +21,6 @@ class SearchDescription
|
||||
private $bRareName = false;
|
||||
/// List of word ids making up the address of the object.
|
||||
private $aAddress = array();
|
||||
/// Subset of word ids of full words making up the address.
|
||||
private $aFullNameAddress = array();
|
||||
/// List of word ids that appear in the name but should be ignored.
|
||||
private $aNameNonSearch = array();
|
||||
/// List of word ids that appear in the address but should be ignored.
|
||||
@@ -199,15 +197,10 @@ class SearchDescription
|
||||
} elseif (($sPhraseType == '' || $sPhraseType == 'postalcode')
|
||||
&& is_a($oSearchTerm, '\Nominatim\Token\Postcode')
|
||||
) {
|
||||
// We need to try the case where the postal code is the primary element
|
||||
// (i.e. no way to tell if it is (postalcode, city) OR (city, postalcode)
|
||||
// so try both.
|
||||
if (!$this->sPostcode) {
|
||||
// If we have structured search or this is the first term,
|
||||
// make the postcode the primary search element.
|
||||
if ($this->iOperator == Operator::NONE
|
||||
&& ($sPhraseType == 'postalcode' || $bFirstToken)
|
||||
) {
|
||||
if ($this->iOperator == Operator::NONE && $bFirstToken) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank++;
|
||||
$oSearch->iOperator = Operator::POSTCODE;
|
||||
@@ -224,6 +217,9 @@ class SearchDescription
|
||||
) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank++;
|
||||
if (strlen($oSearchTerm->sPostcode) < 4) {
|
||||
$oSearch->iSearchRank += 4 - strlen($oSearchTerm->sPostcode);
|
||||
}
|
||||
$oSearch->sPostcode = $oSearchTerm->sPostcode;
|
||||
$aNewSearches[] = $oSearch;
|
||||
}
|
||||
@@ -252,6 +248,19 @@ class SearchDescription
|
||||
$oSearch->iSearchRank++;
|
||||
}
|
||||
$aNewSearches[] = $oSearch;
|
||||
// Housenumbers may appear in the name when the place has its own
|
||||
// address terms.
|
||||
if ($oSearchTerm->iId !== null
|
||||
&& ($this->iNamePhrase >= 0 || empty($this->aName))
|
||||
&& empty($this->aAddress)
|
||||
) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank++;
|
||||
$oSearch->aAddress = $this->aName;
|
||||
$oSearch->bRareName = false;
|
||||
$oSearch->aName = array($oSearchTerm->iId => $oSearchTerm->iId);
|
||||
$aNewSearches[] = $oSearch;
|
||||
}
|
||||
}
|
||||
} elseif ($sPhraseType == ''
|
||||
&& is_a($oSearchTerm, '\Nominatim\Token\SpecialTerm')
|
||||
@@ -288,11 +297,9 @@ class SearchDescription
|
||||
if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
|
||||
if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank += 2;
|
||||
$oSearch->iSearchRank += 3 * $oSearchTerm->iTermCount;
|
||||
$oSearch->aAddress[$iWordID] = $iWordID;
|
||||
$aNewSearches[] = $oSearch;
|
||||
} else {
|
||||
$this->aFullNameAddress[$iWordID] = $iWordID;
|
||||
}
|
||||
} else {
|
||||
$oSearch = clone $this;
|
||||
@@ -338,16 +345,19 @@ class SearchDescription
|
||||
) {
|
||||
if ($oSearchTerm->iSearchNameCount < CONST_Max_Word_Frequency) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank += 2;
|
||||
$oSearch->iSearchRank += $oSearchTerm->iTermCount + 1;
|
||||
if (empty($this->aName)) {
|
||||
$oSearch->iSearchRank++;
|
||||
}
|
||||
if (preg_match('#^[0-9]+$#', $sToken)) {
|
||||
$oSearch->iSearchRank++;
|
||||
}
|
||||
$oSearch->aAddress[$iWordID] = $iWordID;
|
||||
$aNewSearches[] = $oSearch;
|
||||
} else {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank++;
|
||||
$oSearch->iSearchRank += $oSearchTerm->iTermCount + 1;
|
||||
$oSearch->aAddressNonSearch[$iWordID] = $iWordID;
|
||||
if (preg_match('#^[0-9]+$#', $sToken)) {
|
||||
$oSearch->iSearchRank += 2;
|
||||
}
|
||||
if (!empty($aFullTokens)) {
|
||||
$oSearch->iSearchRank++;
|
||||
}
|
||||
@@ -357,7 +367,7 @@ class SearchDescription
|
||||
foreach ($aFullTokens as $oSearchTermToken) {
|
||||
if (is_a($oSearchTermToken, '\Nominatim\Token\Word')) {
|
||||
$oSearch = clone $this;
|
||||
$oSearch->iSearchRank++;
|
||||
$oSearch->iSearchRank += 3;
|
||||
$oSearch->aAddress[$oSearchTermToken->iId]
|
||||
= $oSearchTermToken->iId;
|
||||
$aNewSearches[] = $oSearch;
|
||||
@@ -447,8 +457,8 @@ class SearchDescription
|
||||
$iLimit
|
||||
);
|
||||
|
||||
//now search for housenumber, if housenumber provided
|
||||
if ($this->sHouseNumber && !empty($aResults)) {
|
||||
// Now search for housenumber, if housenumber provided. Can be zero.
|
||||
if (($this->sHouseNumber || $this->sHouseNumber === '0') && !empty($aResults)) {
|
||||
// Downgrade the rank of the street results, they are missing
|
||||
// the housenumber.
|
||||
foreach ($aResults as $oRes) {
|
||||
@@ -657,13 +667,10 @@ class SearchDescription
|
||||
}
|
||||
|
||||
if ($this->sHouseNumber) {
|
||||
$aTerms[] = 'address_rank between 16 and 27';
|
||||
$aTerms[] = 'address_rank between 16 and 30';
|
||||
} elseif (!$this->sClass || $this->iOperator == Operator::NAME) {
|
||||
if ($iMinAddressRank > 0) {
|
||||
$aTerms[] = 'address_rank >= '.$iMinAddressRank;
|
||||
}
|
||||
if ($iMaxAddressRank < 30) {
|
||||
$aTerms[] = 'address_rank <= '.$iMaxAddressRank;
|
||||
$aTerms[] = "((address_rank between $iMinAddressRank and $iMaxAddressRank) or (search_rank between $iMinAddressRank and $iMaxAddressRank))";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,10 +706,11 @@ class SearchDescription
|
||||
$sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid');
|
||||
$aOrder[] = "$sImportanceSQL DESC";
|
||||
|
||||
if (!empty($this->aFullNameAddress)) {
|
||||
$aFullNameAddress = $this->oContext->getFullNameTerms();
|
||||
if (!empty($aFullNameAddress)) {
|
||||
$sExactMatchSQL = ' ( ';
|
||||
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
|
||||
$sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($this->aFullNameAddress).')';
|
||||
$sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($aFullNameAddress).')';
|
||||
$sExactMatchSQL .= ' INTERSECT ';
|
||||
$sExactMatchSQL .= ' SELECT unnest(nameaddress_vector)';
|
||||
$sExactMatchSQL .= ' ) s';
|
||||
|
||||
80
lib/Shell.php
Normal file
80
lib/Shell.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Nominatim;
|
||||
|
||||
class Shell
|
||||
{
|
||||
public function __construct($sBaseCmd, ...$aParams)
|
||||
{
|
||||
if (!$sBaseCmd) {
|
||||
throw new Exception('Command missing in new() call');
|
||||
}
|
||||
$this->baseCmd = $sBaseCmd;
|
||||
$this->aParams = array();
|
||||
$this->aEnv = null; // null = use the same environment as the current PHP process
|
||||
|
||||
$this->stdoutString = null;
|
||||
|
||||
foreach ($aParams as $sParam) {
|
||||
$this->addParams($sParam);
|
||||
}
|
||||
}
|
||||
|
||||
public function addParams(...$aParams)
|
||||
{
|
||||
foreach ($aParams as $sParam) {
|
||||
if (isset($sParam) && $sParam !== null && $sParam !== '') {
|
||||
array_push($this->aParams, $sParam);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addEnvPair($sKey, $sVal)
|
||||
{
|
||||
if (isset($sKey) && $sKey && isset($sVal)) {
|
||||
if (!isset($this->aEnv)) $this->aEnv = $_ENV;
|
||||
$this->aEnv = array_merge($this->aEnv, array($sKey => $sVal), $_ENV);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function escapedCmd()
|
||||
{
|
||||
$aEscaped = array_map(function ($sParam) {
|
||||
return $this->escapeParam($sParam);
|
||||
}, array_merge(array($this->baseCmd), $this->aParams));
|
||||
|
||||
return join(' ', $aEscaped);
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$sCmd = $this->escapedCmd();
|
||||
// $aEnv does not need escaping, proc_open seems to handle it fine
|
||||
|
||||
$aFDs = array(
|
||||
0 => array('pipe', 'r'),
|
||||
1 => STDOUT,
|
||||
2 => STDERR
|
||||
);
|
||||
$aPipes = null;
|
||||
$hProc = @proc_open($sCmd, $aFDs, $aPipes, null, $this->aEnv);
|
||||
if (!is_resource($hProc)) {
|
||||
throw new \Exception('Unable to run command: ' . $sCmd);
|
||||
}
|
||||
|
||||
fclose($aPipes[0]); // no stdin
|
||||
|
||||
$iStat = proc_close($hProc);
|
||||
return $iStat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function escapeParam($sParam)
|
||||
{
|
||||
if (preg_match('/^-*\w+$/', $sParam)) return $sParam;
|
||||
return escapeshellarg($sParam);
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,21 @@ class TokenList
|
||||
return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array();
|
||||
}
|
||||
|
||||
public function getFullWordIDs()
|
||||
{
|
||||
$ids = array();
|
||||
|
||||
foreach ($this->aTokens as $aTokenList) {
|
||||
foreach ($aTokenList as $oToken) {
|
||||
if (is_a($oToken, '\Nominatim\Token\Word') && !$oToken->bPartial) {
|
||||
$ids[$oToken->iId] = $oToken->iId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add token information from the word table in the database.
|
||||
*
|
||||
@@ -151,7 +166,8 @@ class TokenList
|
||||
$oToken = new Token\Word(
|
||||
$iId,
|
||||
$aWord['word_token'][0] != ' ',
|
||||
(int) $aWord['count']
|
||||
(int) $aWord['count'],
|
||||
substr_count($aWord['word_token'], ' ')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,15 @@ class Word
|
||||
public $bPartial;
|
||||
/// Number of appearances in the database.
|
||||
public $iSearchNameCount;
|
||||
/// Number of terms in the word.
|
||||
public $iTermCount;
|
||||
|
||||
public function __construct($iId, $bPartial, $iSearchNameCount)
|
||||
public function __construct($iId, $bPartial, $iSearchNameCount, $iTermCount)
|
||||
{
|
||||
$this->iId = $iId;
|
||||
$this->bPartial = $bPartial;
|
||||
$this->iSearchNameCount = $iSearchNameCount;
|
||||
$this->iTermCount = $iTermCount;
|
||||
}
|
||||
|
||||
public function debugInfo()
|
||||
|
||||
42
lib/cmd.php
42
lib/cmd.php
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
require_once(CONST_BasePath.'/lib/Shell.php');
|
||||
|
||||
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
|
||||
{
|
||||
@@ -148,30 +149,33 @@ function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreError
|
||||
// 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'];
|
||||
|
||||
$oCmd = new \Nominatim\Shell('psql');
|
||||
$oCmd->addParams('--port', $aDSNInfo['port']);
|
||||
$oCmd->addParams('--dbname', $aDSNInfo['database']);
|
||||
if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
|
||||
$sCMD .= ' -h ' . $aDSNInfo['hostspec'];
|
||||
$oCmd->addParams('--host', $aDSNInfo['hostspec']);
|
||||
}
|
||||
if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
|
||||
$sCMD .= ' -U ' . $aDSNInfo['username'];
|
||||
$oCmd->addParams('--username', $aDSNInfo['username']);
|
||||
}
|
||||
$aProcEnv = null;
|
||||
if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
|
||||
$aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
|
||||
if (isset($aDSNInfo['password'])) {
|
||||
$oCmd->addEnvPair('PGPASSWORD', $aDSNInfo['password']);
|
||||
}
|
||||
if (!$bVerbose) {
|
||||
$sCMD .= ' -q';
|
||||
$oCmd->addParams('--quiet');
|
||||
}
|
||||
if ($bfatal && !$bIgnoreErrors) {
|
||||
$sCMD .= ' -v ON_ERROR_STOP=1';
|
||||
$oCmd->addParams('-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);
|
||||
$hProcess = @proc_open($oCmd->escapedCmd(), $aDescriptors, $ahPipes, null, $oCmd->aEnv);
|
||||
if (!is_resource($hProcess)) {
|
||||
fail('unable to start pgsql');
|
||||
}
|
||||
@@ -191,23 +195,3 @@ function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreError
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -16,14 +16,6 @@ function userError($sMsg)
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function exception_handler_json($exception)
|
||||
{
|
||||
http_response_code($exception->getCode());
|
||||
@@ -41,14 +33,6 @@ function exception_handler_xml($exception)
|
||||
exit();
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -73,10 +57,7 @@ function set_exception_handler_by_format($sFormat = null)
|
||||
// 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');
|
||||
set_exception_handler('exception_handler_json');
|
||||
} elseif ($sFormat == 'xml') {
|
||||
set_exception_handler('exception_handler_xml');
|
||||
register_shutdown_function('shutdown_exception_handler_xml');
|
||||
|
||||
@@ -2,8 +2,3 @@
|
||||
|
||||
require_once(CONST_BasePath.'/lib/lib.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;
|
||||
}
|
||||
|
||||
63
lib/lib.php
63
lib/lib.php
@@ -55,7 +55,7 @@ function byImportance($a, $b)
|
||||
if ($a['importance'] != $b['importance'])
|
||||
return ($a['importance'] > $b['importance']?-1:1);
|
||||
|
||||
return ($a['foundorder'] < $b['foundorder']?-1:1);
|
||||
return $a['foundorder'] <=> $b['foundorder'];
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ function parseLatLon($sQuery)
|
||||
$fQueryLat = null;
|
||||
$fQueryLon = null;
|
||||
|
||||
if (preg_match('/\\s*([NS])[ ]+([0-9]+[0-9.]*)[° ]+([0-9.]+)?[′\']*[, ]+([EW])[ ]+([0-9]+)[° ]+([0-9]+[0-9.]*)[′\']*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6
|
||||
if (preg_match('/\\s*([NS])[\s]+([0-9]+[0-9.]*)[°\s]+([0-9.]+)?[′\']*[,\s]+([EW])[\s]+([0-9]+)[°\s]+([0-9]+[0-9.]*)[′\']*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6
|
||||
* degrees decimal minutes
|
||||
* N 40 26.767, W 79 58.933
|
||||
* N 40°26.767′, W 79°58.933′
|
||||
@@ -104,8 +104,8 @@ function parseLatLon($sQuery)
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60);
|
||||
$fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[5] + $aData[6]/60);
|
||||
} elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\']*[ ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\' ]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6
|
||||
} elseif (preg_match('/\\s*([0-9]+)[°\s]+([0-9]+[0-9.]*)?[′\']*[\s]+([NS])[,\s]+([0-9]+)[°\s]+([0-9]+[0-9.]*)?[′\'\s]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6
|
||||
* degrees decimal minutes
|
||||
* 40 26.767 N, 79 58.933 W
|
||||
* 40° 26.767′ N 79° 58.933′ W
|
||||
@@ -113,8 +113,8 @@ function parseLatLon($sQuery)
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[3]=='N'?1:-1) * ($aData[1] + $aData[2]/60);
|
||||
$fQueryLon = ($aData[6]=='E'?1:-1) * ($aData[4] + $aData[5]/60);
|
||||
} elseif (preg_match('/\\s*([NS])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*[, ]+([EW])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6 7 8
|
||||
} elseif (preg_match('/\\s*([NS])[\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+)[″"]*[,\s]+([EW])[\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+)[″"]*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6 7 8
|
||||
* degrees decimal seconds
|
||||
* N 40 26 46 W 79 58 56
|
||||
* N 40° 26′ 46″, W 79° 58′ 56″
|
||||
@@ -122,8 +122,8 @@ function parseLatLon($sQuery)
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600);
|
||||
$fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600);
|
||||
} elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+[0-9.]*)[″" ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+[0-9.]*)[″" ]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6 7 8
|
||||
} elseif (preg_match('/\\s*([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+[0-9.]*)[″"\s]+([NS])[,\s]+([0-9]+)[°\s]+([0-9]+)[′\'\s]+([0-9]+[0-9.]*)[″"\s]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4 5 6 7 8
|
||||
* degrees decimal seconds
|
||||
* 40 26 46 N 79 58 56 W
|
||||
* 40° 26′ 46″ N, 79° 58′ 56″ W
|
||||
@@ -132,24 +132,24 @@ function parseLatLon($sQuery)
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600);
|
||||
$fQueryLon = ($aData[8]=='E'?1:-1) * ($aData[5] + $aData[6]/60 + $aData[7]/3600);
|
||||
} elseif (preg_match('/\\s*([NS])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*[, ]+([EW])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
} elseif (preg_match('/\\s*([NS])[\s]+([0-9]+[0-9]*\\.[0-9]+)[°]*[,\s]+([EW])[\s]+([0-9]+[0-9]*\\.[0-9]+)[°]*\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
* degrees decimal
|
||||
* N 40.446° W 79.982°
|
||||
*/
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2]);
|
||||
$fQueryLon = ($aData[3]=='E'?1:-1) * ($aData[4]);
|
||||
} elseif (preg_match('/\\s*([0-9]+[0-9]*\\.[0-9]+)[° ]+([NS])[, ]+([0-9]+[0-9]*\\.[0-9]+)[° ]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
} elseif (preg_match('/\\s*([0-9]+[0-9]*\\.[0-9]+)[°\s]+([NS])[,\s]+([0-9]+[0-9]*\\.[0-9]+)[°\s]+([EW])\\s*/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
* degrees decimal
|
||||
* 40.446° N 79.982° W
|
||||
*/
|
||||
$sFound = $aData[0];
|
||||
$fQueryLat = ($aData[2]=='N'?1:-1) * ($aData[1]);
|
||||
$fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[3]);
|
||||
} elseif (preg_match('/(\\s*\\[|^\\s*|\\s*)(-?[0-9]+[0-9]*\\.[0-9]+)[, ]+(-?[0-9]+[0-9]*\\.[0-9]+)(\\]\\s*|\\s*$|\\s*)/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
} elseif (preg_match('/(\\s*\\[|^\\s*|\\s*)(-?[0-9]+[0-9]*\\.[0-9]+)[,\s]+(-?[0-9]+[0-9]*\\.[0-9]+)(\\]\\s*|\\s*$|\\s*)/', $sQuery, $aData)) {
|
||||
/* 1 2 3 4
|
||||
* degrees decimal
|
||||
* 12.34, 56.78
|
||||
* 12.34 56.78
|
||||
@@ -165,39 +165,6 @@ function parseLatLon($sQuery)
|
||||
return array($sFound, $fQueryLat, $fQueryLon);
|
||||
}
|
||||
|
||||
|
||||
function geometryText2Points($geometry_as_text, $fRadius)
|
||||
{
|
||||
$aPolyPoints = null;
|
||||
if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) {
|
||||
//
|
||||
preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER);
|
||||
//
|
||||
} elseif (preg_match('#LINESTRING\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) {
|
||||
//
|
||||
preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER);
|
||||
//
|
||||
} elseif (preg_match('#MULTIPOLYGON\\(\\(\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) {
|
||||
//
|
||||
preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER);
|
||||
//
|
||||
} elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#', $geometry_as_text, $aMatch)) {
|
||||
//
|
||||
$aPolyPoints = createPointsAroundCenter($aMatch[1], $aMatch[2], $fRadius);
|
||||
//
|
||||
}
|
||||
|
||||
if (isset($aPolyPoints)) {
|
||||
$aResultPoints = array();
|
||||
foreach ($aPolyPoints as $aPoint) {
|
||||
$aResultPoints[] = array($aPoint[1], $aPoint[2]);
|
||||
}
|
||||
return $aResultPoints;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function createPointsAroundCenter($fLon, $fLat, $fRadius)
|
||||
{
|
||||
$iSteps = max(8, min(100, ($fRadius * 40000)^2));
|
||||
|
||||
@@ -12,6 +12,8 @@ function formatOSMType($sType, $bIncludeExternal = true)
|
||||
if ($sType == 'T') return 'way';
|
||||
if ($sType == 'I') return 'way';
|
||||
|
||||
// not handled: P, L
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -33,20 +35,39 @@ function wikipediaLink($aFeature)
|
||||
return '';
|
||||
}
|
||||
|
||||
function detailsLink($aFeature, $sTitle = false)
|
||||
function detailsLink($aFeature, $sTitle = false, $sExtraProperties = false)
|
||||
{
|
||||
if (!$aFeature['place_id']) return '';
|
||||
|
||||
return '<a href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
||||
$sHtml = '<a ';
|
||||
if ($sExtraProperties) {
|
||||
$sHtml .= $sExtraProperties.' ';
|
||||
}
|
||||
|
||||
$sHtml .= 'href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
function detailsPermaLink($aFeature, $sRefText = false)
|
||||
function detailsPermaLink($aFeature, $sRefText = false, $sExtraProperties = 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>';
|
||||
$sHtml = '<a ';
|
||||
if ($sExtraProperties) {
|
||||
$sHtml .= $sExtraProperties.' ';
|
||||
}
|
||||
$sHtml .= 'href="details.php?osmtype='.$aFeature['osm_type']
|
||||
.'&osmid='.$aFeature['osm_id'].'&class='.$aFeature['class'].'">';
|
||||
|
||||
if ($sRefText) {
|
||||
$sHtml .= $sRefText.'</a>';
|
||||
} else {
|
||||
$sHtml .= $sOSMType.' '.$aFeature['osm_id'].'</a>';
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
return '';
|
||||
return detailsLink($aFeature, $sRefText, $sExtraProperties);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Nominatim\Setup;
|
||||
|
||||
require_once(CONST_BasePath.'/lib/setup/AddressLevelParser.php');
|
||||
require_once(CONST_BasePath.'/lib/Shell.php');
|
||||
|
||||
class SetupFunctions
|
||||
{
|
||||
@@ -10,11 +11,13 @@ class SetupFunctions
|
||||
protected $iInstances;
|
||||
protected $sModulePath;
|
||||
protected $aDSNInfo;
|
||||
protected $bQuiet;
|
||||
protected $bVerbose;
|
||||
protected $sIgnoreErrors;
|
||||
protected $bEnableDiffUpdates;
|
||||
protected $bEnableDebugStatements;
|
||||
protected $bNoPartitions;
|
||||
protected $bDrop;
|
||||
protected $oDB = null;
|
||||
|
||||
public function __construct(array $aCMDResult)
|
||||
@@ -29,10 +32,13 @@ class SetupFunctions
|
||||
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'];
|
||||
} elseif (!is_null(CONST_Osm2pgsql_Flatnode_File)) {
|
||||
// When flatnode files are enabled then disable cache per default.
|
||||
$this->iCacheMemory = 0;
|
||||
} else {
|
||||
// Otherwise: Assume we can steal all the cache memory in the box.
|
||||
$this->iCacheMemory = getCacheMemoryMB();
|
||||
}
|
||||
|
||||
@@ -46,6 +52,7 @@ class SetupFunctions
|
||||
}
|
||||
|
||||
// setting member variables based on command line options stored in $aCMDResult
|
||||
$this->bQuiet = isset($aCMDResult['quiet']) && $aCMDResult['quiet'];
|
||||
$this->bVerbose = $aCMDResult['verbose'];
|
||||
|
||||
//setting default values which are not set by the update.php array
|
||||
@@ -69,6 +76,8 @@ class SetupFunctions
|
||||
} else {
|
||||
$this->bEnableDiffUpdates = false;
|
||||
}
|
||||
|
||||
$this->bDrop = isset($aCMDResult['drop']) && $aCMDResult['drop'];
|
||||
}
|
||||
|
||||
public function createDB()
|
||||
@@ -76,34 +85,34 @@ class SetupFunctions
|
||||
info('Create DB');
|
||||
$oDB = new \Nominatim\DB;
|
||||
|
||||
if ($oDB->databaseExists()) {
|
||||
if ($oDB->checkConnection()) {
|
||||
fail('database already exists ('.CONST_Database_DSN.')');
|
||||
}
|
||||
|
||||
$sCreateDBCmd = 'createdb -E UTF-8 -p '.$this->aDSNInfo['port'].' '.$this->aDSNInfo['database'];
|
||||
$oCmd = (new \Nominatim\Shell('createdb'))
|
||||
->addParams('-E', 'UTF-8')
|
||||
->addParams('-p', $this->aDSNInfo['port']);
|
||||
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sCreateDBCmd .= ' -U '.$this->aDSNInfo['username'];
|
||||
$oCmd->addParams('-U', $this->aDSNInfo['username']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$oCmd->addEnvPair('PGPASSWORD', $this->aDSNInfo['password']);
|
||||
}
|
||||
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sCreateDBCmd .= ' -h '.$this->aDSNInfo['hostspec'];
|
||||
$oCmd->addParams('-h', $this->aDSNInfo['hostspec']);
|
||||
}
|
||||
$oCmd->addParams($this->aDSNInfo['database']);
|
||||
|
||||
$result = $this->runWithPgEnv($sCreateDBCmd);
|
||||
if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$this->oDB = new \Nominatim\DB();
|
||||
$this->oDB->connect();
|
||||
$result = $oCmd->run();
|
||||
if ($result != 0) fail('Error executing external command: '.$oCmd->escapedCmd());
|
||||
}
|
||||
|
||||
public function setupDB()
|
||||
{
|
||||
info('Setup DB');
|
||||
|
||||
$fPostgresVersion = $this->oDB->getPostgresVersion();
|
||||
$fPostgresVersion = $this->db()->getPostgresVersion();
|
||||
echo 'Postgres version found: '.$fPostgresVersion."\n";
|
||||
|
||||
if ($fPostgresVersion < 9.03) {
|
||||
@@ -113,7 +122,7 @@ class SetupFunctions
|
||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
|
||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
|
||||
|
||||
$fPostgisVersion = $this->oDB->getPostgisVersion();
|
||||
$fPostgisVersion = $this->db()->getPostgisVersion();
|
||||
echo 'Postgis version found: '.$fPostgisVersion."\n";
|
||||
|
||||
if ($fPostgisVersion < 2.2) {
|
||||
@@ -121,7 +130,7 @@ class SetupFunctions
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$i = $this->oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'");
|
||||
$i = $this->db()->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";
|
||||
@@ -137,7 +146,7 @@ class SetupFunctions
|
||||
exit(1);
|
||||
}
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
|
||||
$this->pgsqlRunScriptFile(CONST_ExtraDataPath.'/country_osm_grid.sql.gz');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
|
||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode_table.sql');
|
||||
|
||||
@@ -158,56 +167,66 @@ class SetupFunctions
|
||||
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)) {
|
||||
if (!file_exists(CONST_Osm2pgsql_Binary)) {
|
||||
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'");
|
||||
fail("osm2pgsql not found in '".CONST_Osm2pgsql_Binary."'");
|
||||
}
|
||||
|
||||
$osm2pgsql .= ' -S '.CONST_Import_Style;
|
||||
$oCmd = new \Nominatim\Shell(CONST_Osm2pgsql_Binary);
|
||||
$oCmd->addParams('--style', CONST_Import_Style);
|
||||
|
||||
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
|
||||
$osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
|
||||
$oCmd->addParams('--flat-nodes', CONST_Osm2pgsql_Flatnode_File);
|
||||
}
|
||||
if (CONST_Tablespace_Osm2pgsql_Data) {
|
||||
$oCmd->addParams('--tablespace-slim-data', CONST_Tablespace_Osm2pgsql_Data);
|
||||
}
|
||||
if (CONST_Tablespace_Osm2pgsql_Index) {
|
||||
$oCmd->addParams('--tablespace-slim-index', CONST_Tablespace_Osm2pgsql_Index);
|
||||
}
|
||||
if (CONST_Tablespace_Place_Data) {
|
||||
$oCmd->addParams('--tablespace-main-data', CONST_Tablespace_Place_Data);
|
||||
}
|
||||
if (CONST_Tablespace_Place_Index) {
|
||||
$oCmd->addParams('--tablespace-main-index', CONST_Tablespace_Place_Index);
|
||||
}
|
||||
$oCmd->addParams('--latlong', '--slim', '--create');
|
||||
$oCmd->addParams('--output', 'gazetteer');
|
||||
$oCmd->addParams('--hstore');
|
||||
$oCmd->addParams('--number-processes', 1);
|
||||
$oCmd->addParams('--with-forward-dependencies', 'false');
|
||||
$oCmd->addParams('--log-progress', 'true');
|
||||
$oCmd->addParams('--cache', $this->iCacheMemory);
|
||||
$oCmd->addParams('--port', $this->aDSNInfo['port']);
|
||||
|
||||
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'];
|
||||
$oCmd->addParams('--username', $this->aDSNInfo['username']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$oCmd->addEnvPair('PGPASSWORD', $this->aDSNInfo['password']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$osm2pgsql .= ' -H '.$this->aDSNInfo['hostspec'];
|
||||
$oCmd->addParams('--host', $this->aDSNInfo['hostspec']);
|
||||
}
|
||||
$osm2pgsql .= ' -d '.$this->aDSNInfo['database'].' '.$sOSMFile;
|
||||
$oCmd->addParams('--database', $this->aDSNInfo['database']);
|
||||
$oCmd->addParams($sOSMFile);
|
||||
$oCmd->run();
|
||||
|
||||
$this->runWithPgEnv($osm2pgsql);
|
||||
|
||||
if (!$this->sIgnoreErrors && !$this->oDB->getRow('select * from place limit 1')) {
|
||||
if (!$this->sIgnoreErrors && !$this->db()->getRow('select * from place limit 1')) {
|
||||
fail('No Data');
|
||||
}
|
||||
|
||||
if ($this->bDrop) {
|
||||
$this->dropTable('planet_osm_nodes');
|
||||
$this->removeFlatnodeFile();
|
||||
}
|
||||
}
|
||||
|
||||
public function createFunctions()
|
||||
@@ -225,46 +244,26 @@ class SetupFunctions
|
||||
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
|
||||
);
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
|
||||
if ($bReverseOnly) {
|
||||
$this->pgExec('DROP TABLE search_name');
|
||||
$this->dropTable('search_name');
|
||||
}
|
||||
|
||||
$oAlParser = new AddressLevelParser(CONST_Address_Level_Config);
|
||||
$oAlParser->createTable($this->oDB, 'address_levels');
|
||||
$oAlParser->createTable($this->db(), 'address_levels');
|
||||
}
|
||||
|
||||
public function createTableTriggers()
|
||||
{
|
||||
info('Create Tables');
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/table-triggers.sql');
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
}
|
||||
|
||||
public function createPartitionTables()
|
||||
@@ -272,41 +271,7 @@ class SetupFunctions
|
||||
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
|
||||
);
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunPartitionScript($sTemplate);
|
||||
}
|
||||
@@ -321,19 +286,14 @@ class SetupFunctions
|
||||
|
||||
public function importWikipediaArticles()
|
||||
{
|
||||
$sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
|
||||
$sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
|
||||
$sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikimedia-importance.sql.gz';
|
||||
if (file_exists($sWikiArticlesFile)) {
|
||||
info('Importing wikipedia articles');
|
||||
$this->pgsqlRunDropAndRestore($sWikiArticlesFile);
|
||||
info('Importing wikipedia articles and redirects');
|
||||
$this->dropTable('wikipedia_article');
|
||||
$this->dropTable('wikipedia_redirect');
|
||||
$this->pgsqlRunScriptFile($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');
|
||||
warn('wikipedia importance dump file not found - places will have default importance');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,42 +301,42 @@ class SetupFunctions
|
||||
{
|
||||
info('Drop old Data');
|
||||
|
||||
$this->pgExec('TRUNCATE word');
|
||||
$oDB = $this->db();
|
||||
|
||||
$oDB->exec('TRUNCATE word');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE placex');
|
||||
$oDB->exec('TRUNCATE placex');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE location_property_osmline');
|
||||
$oDB->exec('TRUNCATE location_property_osmline');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE place_addressline');
|
||||
$oDB->exec('TRUNCATE place_addressline');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE place_boundingbox');
|
||||
echo '.';
|
||||
$this->pgExec('TRUNCATE location_area');
|
||||
$oDB->exec('TRUNCATE location_area');
|
||||
echo '.';
|
||||
if (!$this->dbReverseOnly()) {
|
||||
$this->pgExec('TRUNCATE search_name');
|
||||
$oDB->exec('TRUNCATE search_name');
|
||||
echo '.';
|
||||
}
|
||||
$this->pgExec('TRUNCATE search_name_blank');
|
||||
$oDB->exec('TRUNCATE search_name_blank');
|
||||
echo '.';
|
||||
$this->pgExec('DROP SEQUENCE seq_place');
|
||||
$oDB->exec('DROP SEQUENCE seq_place');
|
||||
echo '.';
|
||||
$this->pgExec('CREATE SEQUENCE seq_place start 100000');
|
||||
$oDB->exec('CREATE SEQUENCE seq_place start 100000');
|
||||
echo '.';
|
||||
|
||||
$sSQL = 'select distinct partition from country_name';
|
||||
$aPartitions = $this->oDB->getCol($sSQL);
|
||||
$aPartitions = $oDB->getCol($sSQL);
|
||||
|
||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
||||
foreach ($aPartitions as $sPartition) {
|
||||
$this->pgExec('TRUNCATE location_road_'.$sPartition);
|
||||
$oDB->exec('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);
|
||||
$oDB->exec($sSQL);
|
||||
echo ".\n";
|
||||
|
||||
// pre-create the word list
|
||||
@@ -453,13 +413,13 @@ class SetupFunctions
|
||||
info('Reanalysing database');
|
||||
$this->pgsqlRunScript('ANALYSE');
|
||||
|
||||
$sDatabaseDate = getDatabaseDate($this->oDB);
|
||||
$this->oDB->exec('TRUNCATE import_status');
|
||||
$sDatabaseDate = getDatabaseDate($oDB);
|
||||
$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);
|
||||
$oDB->exec($sSQL);
|
||||
echo "Latest data imported from $sDatabaseDate.\n";
|
||||
}
|
||||
}
|
||||
@@ -468,18 +428,15 @@ class SetupFunctions
|
||||
{
|
||||
info('Import Tiger data');
|
||||
|
||||
$aFilenames = glob(CONST_Tiger_Data_Path.'/*.sql');
|
||||
info('Found '.count($aFilenames).' SQL files in path '.CONST_Tiger_Data_Path);
|
||||
if (empty($aFilenames)) {
|
||||
warn('Tiger data import selected but no files found in path '.CONST_Tiger_Data_Path);
|
||||
return;
|
||||
}
|
||||
$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
|
||||
);
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
|
||||
$aDBInstances = array();
|
||||
@@ -492,7 +449,7 @@ class SetupFunctions
|
||||
pg_ping($aDBInstances[$i]);
|
||||
}
|
||||
|
||||
foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
|
||||
foreach ($aFilenames as $sFile) {
|
||||
echo $sFile.': ';
|
||||
$hFile = fopen($sFile, 'r');
|
||||
$sSQL = fgets($hFile, 100000);
|
||||
@@ -532,24 +489,15 @@ class SetupFunctions
|
||||
|
||||
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
|
||||
);
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate, false);
|
||||
}
|
||||
|
||||
public function calculatePostcodes($bCMDResultAll)
|
||||
{
|
||||
info('Calculate Postcodes');
|
||||
$this->pgExec('TRUNCATE location_postcode');
|
||||
$this->db()->exec('TRUNCATE location_postcode');
|
||||
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
||||
@@ -560,7 +508,7 @@ class SetupFunctions
|
||||
$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);
|
||||
$this->db()->exec($sSQL);
|
||||
|
||||
// only add postcodes that are not yet available in OSM
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
@@ -570,7 +518,7 @@ class SetupFunctions
|
||||
$sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
|
||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
||||
$sSQL .= " WHERE country_code = 'us')";
|
||||
$this->pgExec($sSQL);
|
||||
$this->db()->exec($sSQL);
|
||||
|
||||
// add missing postcodes for GB (if available)
|
||||
$sSQL = 'INSERT INTO location_postcode';
|
||||
@@ -579,80 +527,104 @@ class SetupFunctions
|
||||
$sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
|
||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
||||
$sSQL .= " WHERE country_code = 'gb')";
|
||||
$this->pgExec($sSQL);
|
||||
$this->db()->exec($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);
|
||||
$this->db()->exec($sSQL);
|
||||
}
|
||||
|
||||
$sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
|
||||
$sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
|
||||
$this->pgExec($sSQL);
|
||||
$this->db()->exec($sSQL);
|
||||
}
|
||||
|
||||
public function index($bIndexNoanalyse)
|
||||
{
|
||||
$sOutputFile = '';
|
||||
$sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$this->aDSNInfo['database'].' -P '
|
||||
.$this->aDSNInfo['port'].' -t '.$this->iInstances.$sOutputFile;
|
||||
checkModulePresence(); // raises exception on failure
|
||||
|
||||
$oBaseCmd = (new \Nominatim\Shell(CONST_BasePath.'/nominatim/nominatim.py'))
|
||||
->addParams('--database', $this->aDSNInfo['database'])
|
||||
->addParams('--port', $this->aDSNInfo['port'])
|
||||
->addParams('--threads', $this->iInstances);
|
||||
|
||||
if (!$this->bQuiet) {
|
||||
$oBaseCmd->addParams('-v');
|
||||
}
|
||||
if ($this->bVerbose) {
|
||||
$oBaseCmd->addParams('-v');
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sBaseCmd .= ' -H '.$this->aDSNInfo['hostspec'];
|
||||
$oBaseCmd->addParams('--host', $this->aDSNInfo['hostspec']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sBaseCmd .= ' -U '.$this->aDSNInfo['username'];
|
||||
$oBaseCmd->addParams('--user', $this->aDSNInfo['username']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$oBaseCmd->addEnvPair('PGPASSWORD', $this->aDSNInfo['password']);
|
||||
}
|
||||
|
||||
info('Index ranks 0 - 4');
|
||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -R 4');
|
||||
$oCmd = (clone $oBaseCmd)->addParams('--maxrank', 4);
|
||||
echo $oCmd->escapedCmd();
|
||||
|
||||
$iStatus = $oCmd->run();
|
||||
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');
|
||||
info('Index administrative boundaries');
|
||||
$oCmd = (clone $oBaseCmd)->addParams('-b');
|
||||
$iStatus = $oCmd->run();
|
||||
if ($iStatus != 0) {
|
||||
fail('error status ' . $iStatus . ' running nominatim!');
|
||||
}
|
||||
|
||||
info('Index ranks 5 - 25');
|
||||
$oCmd = (clone $oBaseCmd)->addParams('--minrank', 5, '--maxrank', 25);
|
||||
$iStatus = $oCmd->run();
|
||||
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');
|
||||
$oCmd = (clone $oBaseCmd)->addParams('--minrank', 26);
|
||||
$iStatus = $oCmd->run();
|
||||
if ($iStatus != 0) {
|
||||
fail('error status ' . $iStatus . ' running nominatim!');
|
||||
}
|
||||
|
||||
info('Index postcodes');
|
||||
$sSQL = 'UPDATE location_postcode SET indexed_status = 0';
|
||||
$this->pgExec($sSQL);
|
||||
$this->db()->exec($sSQL);
|
||||
}
|
||||
|
||||
public function createSearchIndices()
|
||||
{
|
||||
info('Create Search indices');
|
||||
|
||||
$sSQL = 'SELECT relname FROM pg_class, pg_index ';
|
||||
$sSQL .= 'WHERE pg_index.indisvalid = false AND pg_index.indexrelid = pg_class.oid';
|
||||
$aInvalidIndices = $this->db()->getCol($sSQL);
|
||||
|
||||
foreach ($aInvalidIndices as $sIndexName) {
|
||||
info("Cleaning up invalid index $sIndexName");
|
||||
$this->db()->exec("DROP INDEX $sIndexName;");
|
||||
}
|
||||
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
|
||||
if (!$this->bDrop) {
|
||||
$sTemplate .= file_get_contents(CONST_BasePath.'/sql/indices_updates.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
|
||||
);
|
||||
$sTemplate = $this->replaceSqlPatterns($sTemplate);
|
||||
|
||||
$this->pgsqlRunScript($sTemplate);
|
||||
}
|
||||
|
||||
@@ -711,7 +683,7 @@ class SetupFunctions
|
||||
);
|
||||
|
||||
$aDropTables = array();
|
||||
$aHaveTables = $this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'");
|
||||
$aHaveTables = $this->db()->getListOfTables();
|
||||
|
||||
foreach ($aHaveTables as $sTable) {
|
||||
$bFound = false;
|
||||
@@ -724,10 +696,65 @@ class SetupFunctions
|
||||
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");
|
||||
$this->dropTable($sDrop);
|
||||
}
|
||||
|
||||
$this->removeFlatnodeFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup settings-frontend.php in the build/website directory
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function setupWebsite()
|
||||
{
|
||||
$rOutputFile = fopen(CONST_InstallPath.'/settings/settings-frontend.php', 'w');
|
||||
|
||||
fwrite($rOutputFile, "<?php
|
||||
@define('CONST_BasePath', '".CONST_BasePath."');
|
||||
if (file_exists(getenv('NOMINATIM_SETTINGS'))) require_once(getenv('NOMINATIM_SETTINGS'));
|
||||
|
||||
@define('CONST_Database_DSN', '".CONST_Database_DSN."');
|
||||
@define('CONST_Default_Language', ".(CONST_Default_Language ? ("'".CONST_Default_Language."'") : 'false').");
|
||||
@define('CONST_Log_DB', ".(CONST_Log_DB ? 'true' : 'false').");
|
||||
@define('CONST_Log_File', ".(CONST_Log_File ? ("'".CONST_Log_File."'") : 'false').");
|
||||
@define('CONST_Max_Word_Frequency', '".CONST_Max_Word_Frequency."');
|
||||
@define('CONST_NoAccessControl', ".CONST_NoAccessControl.");
|
||||
@define('CONST_Places_Max_ID_count', ".CONST_Places_Max_ID_count.");
|
||||
@define('CONST_PolygonOutput_MaximumTypes', ".CONST_PolygonOutput_MaximumTypes.");
|
||||
@define('CONST_Search_AreaPolygons', ".CONST_Search_AreaPolygons.");
|
||||
@define('CONST_Search_BatchMode', ".(CONST_Search_BatchMode ? 'true' : 'false').");
|
||||
@define('CONST_Search_NameOnlySearchFrequencyThreshold', ".CONST_Search_NameOnlySearchFrequencyThreshold.");
|
||||
@define('CONST_Search_ReversePlanForAll', ".CONST_Search_ReversePlanForAll.");
|
||||
@define('CONST_Term_Normalization_Rules', \"".CONST_Term_Normalization_Rules."\");
|
||||
@define('CONST_Use_Aux_Location_data', ".(CONST_Use_Aux_Location_data ? 'true' : 'false').");
|
||||
@define('CONST_Use_US_Tiger_Data', ".(CONST_Use_US_Tiger_Data ? 'true' : 'false').");
|
||||
@define('CONST_MapIcon_URL', ".(CONST_MapIcon_URL ? ("'".CONST_MapIcon_URL."'") : 'false').');
|
||||
');
|
||||
info(CONST_InstallPath.'/settings/settings-frontend.php has been set up successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection to the database.
|
||||
*
|
||||
* @return Database object.
|
||||
*
|
||||
* Creates a new connection if none exists yet. Otherwise reuses the
|
||||
* already established connection.
|
||||
*/
|
||||
private function db()
|
||||
{
|
||||
if (is_null($this->oDB)) {
|
||||
$this->oDB = new \Nominatim\DB();
|
||||
$this->oDB->connect();
|
||||
}
|
||||
|
||||
return $this->oDB;
|
||||
}
|
||||
|
||||
private function removeFlatnodeFile()
|
||||
{
|
||||
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";
|
||||
@@ -736,22 +763,6 @@ class SetupFunctions
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -764,7 +775,22 @@ class SetupFunctions
|
||||
|
||||
private function createSqlFunctions()
|
||||
{
|
||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
|
||||
$sBasePath = CONST_BasePath.'/sql/functions/';
|
||||
$sTemplate = file_get_contents($sBasePath.'utils.sql');
|
||||
$sTemplate .= file_get_contents($sBasePath.'normalization.sql');
|
||||
$sTemplate .= file_get_contents($sBasePath.'ranking.sql');
|
||||
$sTemplate .= file_get_contents($sBasePath.'importance.sql');
|
||||
$sTemplate .= file_get_contents($sBasePath.'address_lookup.sql');
|
||||
$sTemplate .= file_get_contents($sBasePath.'interpolation.sql');
|
||||
if ($this->db()->tableExists('place')) {
|
||||
$sTemplate .= file_get_contents($sBasePath.'place_triggers.sql');
|
||||
}
|
||||
if ($this->db()->tableExists('placex')) {
|
||||
$sTemplate .= file_get_contents($sBasePath.'placex_triggers.sql');
|
||||
}
|
||||
if ($this->db()->tableExists('location_postcode')) {
|
||||
$sTemplate .= file_get_contents($sBasePath.'postcode_triggers.sql');
|
||||
}
|
||||
$sTemplate = str_replace('{modulepath}', $this->sModulePath, $sTemplate);
|
||||
if ($this->bEnableDiffUpdates) {
|
||||
$sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
|
||||
@@ -791,7 +817,7 @@ class SetupFunctions
|
||||
private function pgsqlRunPartitionScript($sTemplate)
|
||||
{
|
||||
$sSQL = 'select distinct partition from country_name';
|
||||
$aPartitions = $this->oDB->getCol($sSQL);
|
||||
$aPartitions = $this->db()->getCol($sSQL);
|
||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
||||
|
||||
preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
|
||||
@@ -810,19 +836,21 @@ class SetupFunctions
|
||||
{
|
||||
if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
|
||||
|
||||
$sCMD = 'psql -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'];
|
||||
$oCmd = (new \Nominatim\Shell('psql'))
|
||||
->addParams('--port', $this->aDSNInfo['port'])
|
||||
->addParams('--dbname', $this->aDSNInfo['database']);
|
||||
|
||||
if (!$this->bVerbose) {
|
||||
$sCMD .= ' -q';
|
||||
$oCmd->addParams('--quiet');
|
||||
}
|
||||
if (isset($this->aDSNInfo['hostspec'])) {
|
||||
$sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
|
||||
$oCmd->addParams('--host', $this->aDSNInfo['hostspec']);
|
||||
}
|
||||
if (isset($this->aDSNInfo['username'])) {
|
||||
$sCMD .= ' -U '.$this->aDSNInfo['username'];
|
||||
$oCmd->addParams('--username', $this->aDSNInfo['username']);
|
||||
}
|
||||
$aProcEnv = null;
|
||||
if (isset($this->aDSNInfo['password'])) {
|
||||
$aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
|
||||
$oCmd->addEnvPair('PGPASSWORD', $this->aDSNInfo['password']);
|
||||
}
|
||||
$ahGzipPipes = null;
|
||||
if (preg_match('/\\.gz$/', $sFilename)) {
|
||||
@@ -831,12 +859,14 @@ class SetupFunctions
|
||||
1 => array('pipe', 'w'),
|
||||
2 => array('file', '/dev/null', 'a')
|
||||
);
|
||||
$hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
|
||||
$oZcatCmd = new \Nominatim\Shell('zcat', $sFilename);
|
||||
|
||||
$hGzipProcess = proc_open($oZcatCmd->escapedCmd(), $aDescriptors, $ahGzipPipes);
|
||||
if (!is_resource($hGzipProcess)) fail('unable to start zcat');
|
||||
$aReadPipe = $ahGzipPipes[1];
|
||||
fclose($ahGzipPipes[0]);
|
||||
} else {
|
||||
$sCMD .= ' -f '.$sFilename;
|
||||
$oCmd->addParams('--file', $sFilename);
|
||||
$aReadPipe = array('pipe', 'r');
|
||||
}
|
||||
$aDescriptors = array(
|
||||
@@ -845,7 +875,8 @@ class SetupFunctions
|
||||
2 => array('file', '/dev/null', 'a')
|
||||
);
|
||||
$ahPipes = null;
|
||||
$hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
||||
|
||||
$hProcess = proc_open($oCmd->escapedCmd(), $aDescriptors, $ahPipes, null, $oCmd->aEnv);
|
||||
if (!is_resource($hProcess)) fail('unable to start pgsql');
|
||||
// TODO: error checking
|
||||
while (!feof($ahPipes[1])) {
|
||||
@@ -862,43 +893,41 @@ class SetupFunctions
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceTablespace($sTemplate, $sTablespace, $sSql)
|
||||
private function replaceSqlPatterns($sSql)
|
||||
{
|
||||
if ($sTablespace) {
|
||||
$sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
|
||||
} else {
|
||||
$sSql = str_replace($sTemplate, '', $sSql);
|
||||
$sSql = str_replace('{www-user}', CONST_Database_Web_User, $sSql);
|
||||
|
||||
$aPatterns = array(
|
||||
'{ts:address-data}' => CONST_Tablespace_Address_Data,
|
||||
'{ts:address-index}' => CONST_Tablespace_Address_Index,
|
||||
'{ts:search-data}' => CONST_Tablespace_Search_Data,
|
||||
'{ts:search-index}' => CONST_Tablespace_Search_Index,
|
||||
'{ts:aux-data}' => CONST_Tablespace_Aux_Data,
|
||||
'{ts:aux-index}' => CONST_Tablespace_Aux_Index,
|
||||
);
|
||||
|
||||
foreach ($aPatterns as $sPattern => $sTablespace) {
|
||||
if ($sTablespace) {
|
||||
$sSql = str_replace($sPattern, 'TABLESPACE "'.$sTablespace.'"', $sSql);
|
||||
} else {
|
||||
$sSql = str_replace($sPattern, '', $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.
|
||||
* Drop table with the given name if it exists.
|
||||
*
|
||||
* @param string $sSQL SQL command to execute.
|
||||
* @param string $sName Name of table to remove.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
* @pre connect() must have been called.
|
||||
*/
|
||||
private function pgExec($sSQL)
|
||||
private function dropTable($sName)
|
||||
{
|
||||
$this->oDB->exec($sSQL);
|
||||
if ($this->bVerbose) echo "Dropping table $sName\n";
|
||||
$this->db()->deleteTable($sName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -908,6 +937,6 @@ class SetupFunctions
|
||||
*/
|
||||
private function dbReverseOnly()
|
||||
{
|
||||
return !($this->oDB->tableExists('search_name'));
|
||||
return !($this->db()->tableExists('search_name'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,27 +30,14 @@ if (empty($aPlace)) {
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['label'] = $aPlace['langaddress'];
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['name'] = $aPlace['placename'];
|
||||
if ($aPlace['placename'] !== null) {
|
||||
$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];
|
||||
}
|
||||
}
|
||||
$aPlace['address']->addGeocodeJsonAddressParts(
|
||||
$aFilteredPlaces['properties']['geocoding']
|
||||
);
|
||||
|
||||
$aFilteredPlaces['properties']['geocoding']['admin']
|
||||
= $aPlace['address']->getAdminLevels();
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
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" />
|
||||
</head>
|
||||
|
||||
<body id="reverse-page">
|
||||
|
||||
<?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">
|
||||
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">
|
||||
<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;
|
||||
|
||||
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>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<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 id="map-position-inner"></div>
|
||||
<div id="map-position-close"><a href="#">hide</a></div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /content -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
|
||||
$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'); ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -28,6 +28,9 @@ if (empty($aPlace)) {
|
||||
echo join(',', $aPlace['aBoundingBox']);
|
||||
echo '"';
|
||||
}
|
||||
echo " place_rank='".$aPlace['rank_search']."'";
|
||||
echo " address_rank='".$aPlace['rank_address']."'";
|
||||
|
||||
|
||||
if (isset($aPlace['asgeojson'])) {
|
||||
echo ' geojson=\'';
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
<?php
|
||||
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/details.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
function osmMapUrl($aFeature)
|
||||
{
|
||||
if (isset($sFeature['error_x']) && isset($sFeature['error_y']))
|
||||
{
|
||||
$sBaseUrl = '//www.openstreetmap.org/';
|
||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
||||
if ($sOSMType)
|
||||
{
|
||||
$sBaseUrl += $sOSMType.'/'.$aFeature['osm_id'];
|
||||
}
|
||||
|
||||
return '<a href="'.$sBaseUrl.'?mlat='.$aFeature['error_y'].'&mlon='.$aFeature['error_x'].'">view on osm.org</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function josm_edit_url($aFeature)
|
||||
{
|
||||
$fWidth = 0.0002;
|
||||
$sLon = $aFeature['error_x'];
|
||||
$sLat = $aFeature['error_y'];
|
||||
|
||||
if (isset($sLat))
|
||||
{
|
||||
return "http://localhost:8111/load_and_zoom?left=".($sLon-$fWidth)."&right=".($sLon+$fWidth)."&top=".($sLat+$fWidth)."&bottom=".($sLat-$fWidth);
|
||||
}
|
||||
|
||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
||||
if ($sOSMType)
|
||||
{
|
||||
return 'http://localhost:8111/import?url=http://www.openstreetmap.org/api/0.6/'.$sOSMType.'/'.$aFeature['osm_id'].'/full';
|
||||
// Should be better to load by object id - but this doesn't seem to zoom correctly
|
||||
// return " <a href=\"http://localhost:8111/load_object?new_layer=true&objects=".strtolower($aFeature['osm_type']).$sOSMID."\" target=\"josm\">Remote Control (JOSM / Merkaartor)</a>";
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function potlach_edit_url($aFeature)
|
||||
{
|
||||
$fWidth = 0.0002;
|
||||
$sLat = $aFeature['error_y'];
|
||||
$sLon = $aFeature['error_x'];
|
||||
|
||||
if (isset($sLat))
|
||||
{
|
||||
return "//www.openstreetmap.org/edit?editor=potlatch2&bbox=".($sLon-$fWidth).",".($sLat-$fWidth).",".($sLon+$fWidth).",".($sLat+$fWidth);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<body id="details-page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
||||
|
||||
<h1><?php echo $aPointDetails['localname'] ?></h1>
|
||||
<div class="locationdetails">
|
||||
<h2 class="bg-danger">This object has an invalid geometry.</h2>
|
||||
|
||||
<div>
|
||||
Type: <span class="type"><?php echo $aPointDetails['class'].':'.$aPointDetails['type'];?></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
OSM: <span class="label"><?php echo osmLink($aPointDetails); ?><span>
|
||||
</div>
|
||||
|
||||
|
||||
<h4>Error</h4>
|
||||
<p>
|
||||
<?php echo $aPointDetails['errormessage']?$aPointDetails['errormessage']:'unknown'; ?>
|
||||
</p>
|
||||
<?php echo osmMapUrl($aPointDetails); ?>
|
||||
|
||||
<h4>Edit</h4>
|
||||
<ul>
|
||||
<?php if (josm_edit_url($aPointDetails)) { ?>
|
||||
<li><a href="<?php echo josm_edit_url($aPointDetails); ?>" target="josm">Remote Control (JOSM / Merkaartor)</a></li>
|
||||
<?php } ?>
|
||||
<?php if (potlach_edit_url($aPointDetails)) { ?>
|
||||
<li><a href="<?php echo potlach_edit_url($aPointDetails); ?>" target="potlatch2">Potlatch 2</a></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var nominatim_result = {
|
||||
outlinestring: '<?php echo $aPointDetails['outlinestring'];?>',
|
||||
lon: <?php echo isset($aPointDetails['error_x']) ? $aPointDetails['error_x'] : 0; ?>,
|
||||
lat: <?php echo isset($aPointDetails['error_y']) ? $aPointDetails['error_y'] : 0; ?>
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,254 +0,0 @@
|
||||
<?php
|
||||
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/details.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
function headline($sTitle)
|
||||
{
|
||||
echo "<tr class='all-columns'><td colspan='6'><h2>".$sTitle."</h2></td></tr>\n";
|
||||
}
|
||||
|
||||
function headline3($sTitle)
|
||||
{
|
||||
echo "<tr class='all-columns'><td colspan='6'><h3>".$sTitle."</h3></td></tr>\n";
|
||||
}
|
||||
|
||||
|
||||
function format_distance($fDistance)
|
||||
{
|
||||
// $fDistance is in meters
|
||||
if ($fDistance < 1)
|
||||
{
|
||||
return '0';
|
||||
}
|
||||
elseif ($fDistance < 1000)
|
||||
{
|
||||
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance,0)).' m</abbr>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance/1000,1)).' km</abbr>';
|
||||
}
|
||||
}
|
||||
|
||||
function kv($sKey,$sValue)
|
||||
{
|
||||
echo ' <tr><td>' . $sKey . '</td><td>'.$sValue.'</td></tr>'. "\n";
|
||||
}
|
||||
|
||||
|
||||
function hash_to_subtable($aAssociatedList)
|
||||
{
|
||||
$sHTML = '';
|
||||
foreach ($aAssociatedList as $sKey => $sValue) {
|
||||
$sHTML = $sHTML.' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>'."\n";
|
||||
}
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
function map_icon($sIcon)
|
||||
{
|
||||
if ($sIcon){
|
||||
echo '<img id="mapicon" src="'.CONST_Website_BaseURL.'images/mapicons/'.$sIcon.'.n.32.png'.'" alt="'.$sIcon.'" />';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _one_row($aAddressLine){
|
||||
$bNotUsed = isset($aAddressLine['isaddress']) && !$aAddressLine['isaddress'];
|
||||
|
||||
echo '<tr class="' . ($bNotUsed?'notused':'') . '">'."\n";
|
||||
echo ' <td class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>')."</td>\n";
|
||||
echo ' <td>' . $aAddressLine['class'].':'.$aAddressLine['type'] . "</td>\n";
|
||||
echo ' <td>' . osmLink($aAddressLine) . "</td>\n";
|
||||
echo ' <td>' . (isset($aAddressLine['rank_address']) ? $aAddressLine['rank_address'] : '') . "</td>\n";
|
||||
echo ' <td>' . ($aAddressLine['admin_level'] < 15 ? $aAddressLine['admin_level'] : '') . "</td>\n";
|
||||
echo ' <td>' . format_distance($aAddressLine['distance'])."</td>\n";
|
||||
echo ' <td>' . detailsLink($aAddressLine,'details >') . "</td>\n";
|
||||
echo "</tr>\n";
|
||||
}
|
||||
|
||||
function _one_keyword_row($keyword_token,$word_id){
|
||||
echo "<tr>\n";
|
||||
echo '<td>';
|
||||
// mark partial tokens (those starting with a space) with a star for readability
|
||||
echo ($keyword_token[0]==' '?'*':'');
|
||||
echo $keyword_token;
|
||||
if (isset($word_id))
|
||||
{
|
||||
echo '</td><td>word id: '.$word_id;
|
||||
}
|
||||
echo "</td></tr>\n";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<body id="details-page">
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-10">
|
||||
<h1>
|
||||
<?php echo $aPointDetails['localname'] ?>
|
||||
<small><?php echo detailsPermaLink($aPointDetails, 'link to this page') ?></small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-2 text-right">
|
||||
<?php map_icon($aPointDetails['icon']) ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table id="locationdetails" class="table table-striped">
|
||||
|
||||
<?php
|
||||
|
||||
kv('Name' , hash_to_subtable($aPointDetails['aNames']) );
|
||||
kv('Type' , $aPointDetails['class'].':'.$aPointDetails['type'] );
|
||||
kv('Last Updated' , (new DateTime('@'.$aPointDetails['indexed_epoch']))->format(DateTime::RFC822) );
|
||||
kv('Admin Level' , $aPointDetails['admin_level'] );
|
||||
kv('Rank' , $aPointDetails['rank_search_label'] );
|
||||
if ($aPointDetails['calculated_importance']) {
|
||||
kv('Importance' , $aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)') );
|
||||
}
|
||||
kv('Coverage' , ($aPointDetails['isarea']?'Polygon':'Point') );
|
||||
kv('Centre Point' , $aPointDetails['lat'].','.$aPointDetails['lon'] );
|
||||
kv('OSM' , osmLink($aPointDetails) );
|
||||
if ($aPointDetails['wikipedia'])
|
||||
{
|
||||
kv('Wikipedia Calculated' , wikipediaLink($aPointDetails) );
|
||||
}
|
||||
|
||||
kv('Computed Postcode', $aPointDetails['postcode']);
|
||||
kv('Address Tags' , hash_to_subtable($aPointDetails['aAddressTags']) );
|
||||
kv('Extra Tags' , hash_to_subtable($aPointDetails['aExtraTags']) );
|
||||
|
||||
?>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<h2>Address</h2>
|
||||
|
||||
<table id="address" class="table table-striped table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Local name</td>
|
||||
<td>Type</td>
|
||||
<td>OSM</td>
|
||||
<td>Address rank</td>
|
||||
<td>Admin level</td>
|
||||
<td>Distance</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php
|
||||
foreach ($aAddressLines as $aAddressLine) {
|
||||
_one_row($aAddressLine);
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
if ($aLinkedLines)
|
||||
{
|
||||
headline('Linked Places');
|
||||
foreach ($aLinkedLines as $aAddressLine) {
|
||||
_one_row($aAddressLine);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bIncludeKeywords)
|
||||
{
|
||||
headline('Name Keywords');
|
||||
if ($aPlaceSearchNameKeywords) {
|
||||
foreach ($aPlaceSearchNameKeywords as $aRow) {
|
||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
||||
}
|
||||
}
|
||||
|
||||
headline('Address Keywords');
|
||||
if ($aPlaceSearchAddressKeywords) {
|
||||
foreach ($aPlaceSearchAddressKeywords as $aRow) {
|
||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($aHierarchyLines))
|
||||
{
|
||||
headline('Parent Of');
|
||||
|
||||
$aGroupedAddressLines = array();
|
||||
foreach ($aHierarchyLines as $aAddressLine) {
|
||||
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
||||
else $sType = $aAddressLine['type'];
|
||||
|
||||
if (!isset($aGroupedAddressLines[$sType]))
|
||||
$aGroupedAddressLines[$sType] = array();
|
||||
$aGroupedAddressLines[$sType][] = $aAddressLine;
|
||||
}
|
||||
foreach ($aGroupedAddressLines as $sGroupHeading => $aHierarchyLines) {
|
||||
$sGroupHeading = ucwords($sGroupHeading);
|
||||
headline3($sGroupHeading);
|
||||
|
||||
foreach ($aHierarchyLines as $aAddressLine) {
|
||||
_one_row($aAddressLine);
|
||||
}
|
||||
}
|
||||
if (count($aHierarchyLines) >= 500) {
|
||||
echo '<p>There are more child objects which are not shown.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
echo "</table>\n";
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
|
||||
$aNominatimMapInit = array(
|
||||
'tile_url' => $sTileURL,
|
||||
'tile_attribution' => $sTileAttribution
|
||||
);
|
||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
||||
|
||||
$aPlace = array(
|
||||
'asgeojson' => $aPointDetails['asgeojson'],
|
||||
'lon' => $aPointDetails['lon'],
|
||||
'lat' => $aPointDetails['lat'],
|
||||
);
|
||||
echo 'var nominatim_result = ' . json_encode($aPlace, JSON_PRETTY_PRINT) . ';';
|
||||
|
||||
|
||||
?>
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -26,8 +26,9 @@ $aPlaceDetails['calculated_importance'] = (float) $aPointDetails['calculated_imp
|
||||
|
||||
$aPlaceDetails['extratags'] = $aPointDetails['aExtraTags'];
|
||||
$aPlaceDetails['calculated_wikipedia'] = $aPointDetails['wikipedia'];
|
||||
if ($aPointDetails['icon']) {
|
||||
$aPlaceDetails['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aPointDetails['icon'].'.n.32.png';
|
||||
$sIcon = Nominatim\ClassTypes\getIconFile($aPointDetails);
|
||||
if (isset($sIcon)) {
|
||||
$aPlaceDetails['icon'] = $sIcon;
|
||||
}
|
||||
|
||||
$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
|
||||
@@ -47,11 +48,13 @@ $funcMapAddressLine = function ($aFull) {
|
||||
'place_id' => isset($aFull['place_id']) ? (int) $aFull['place_id'] : null,
|
||||
'osm_id' => isset($aFull['osm_id']) ? (int) $aFull['osm_id'] : null,
|
||||
'osm_type' => isset($aFull['osm_type']) ? $aFull['osm_type'] : null,
|
||||
'place_type' => isset($aFull['place_type']) ? $aFull['place_type'] : null,
|
||||
'class' => $aFull['class'],
|
||||
'type' => $aFull['type'],
|
||||
'admin_level' => isset($aFull['admin_level']) ? (int) $aFull['admin_level'] : null,
|
||||
'rank_address' => $aFull['rank_address'] ? (int) $aFull['rank_address'] : null,
|
||||
'distance' => (float) $aFull['distance']
|
||||
'distance' => (float) $aFull['distance'],
|
||||
'isaddress' => isset($aFull['isaddress']) ? (bool) $aFull['isaddress'] : null
|
||||
);
|
||||
|
||||
return $aMapped;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
$title = 'Internal Server Error';
|
||||
if ( $exception->getCode() == 400 ) {
|
||||
$title = 'Bad Request';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
em { font-weight: bold; font-family: monospace; color: #e00404; background-color: #ffeaea; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><?php echo $title ?></h1>
|
||||
|
||||
<?php if (get_class($exception) == 'Nominatim\DatabaseError') { ?>
|
||||
|
||||
<p>Nominatim has encountered an internal error while accessing the database.
|
||||
This may happen because the database is broken or because of a bug in
|
||||
the software.</p>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<p>Nominatim has encountered an error with your request.</p>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
<h3>Details</h3>
|
||||
|
||||
<?php echo $exception->getMessage() ?>
|
||||
|
||||
<?php if (CONST_Debug) { ?>
|
||||
<p>
|
||||
Exception <em><?php echo get_class($exception) ?></em> thrown in <em><?php echo $exception->getFile() . '('. $exception->getLine() . ')' ?></em>.
|
||||
|
||||
<?php if (get_class($exception) == 'Nominatim\DatabaseError') { ?>
|
||||
|
||||
<h3>SQL Error</h3>
|
||||
<em><?php echo $exception->getSqlError() ?></em>
|
||||
|
||||
<pre><?php echo $exception->getSqlDebugDump() ?></pre>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<h3>Stack trace</h3>
|
||||
<pre><?php echo $exception->getTraceAsString() ?></pre>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<p>
|
||||
If you feel this error is incorrect feel file an issue on
|
||||
<a href="https://github.com/openstreetmap/Nominatim/issues">Github</a>.
|
||||
|
||||
Please include the error message above and the URL you used.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
<footer>
|
||||
<p class="disclaimer">
|
||||
Addresses and postcodes are approximate
|
||||
</p>
|
||||
<p class="copyright">
|
||||
© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/leaflet.min.js"></script>
|
||||
<script src="js/Control.Minimap.min.js"></script>
|
||||
<script src="js/url-search-params.js"></script>
|
||||
<script src="js/nominatim-ui.js"></script>
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>OpenStreetMap Nominatim: Search</title>
|
||||
<meta content="IE=edge" http-equiv="x-ua-compatible" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<base href="<?php echo CONST_Website_BaseURL;?>" />
|
||||
<link href="nominatim.xml" rel="search" title="Nominatim Search" type="application/opensearchdescription+xml" />
|
||||
<link href="css/leaflet.css" rel="stylesheet" />
|
||||
<link href="css/Control.Minimap.min.css" rel="stylesheet" />
|
||||
<link href="css/bootstrap-theme.min.css" rel="stylesheet" />
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet" />
|
||||
@@ -1,49 +0,0 @@
|
||||
<header class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<div class="brand">
|
||||
<a href="<?php echo CONST_Website_BaseURL;?>">
|
||||
<img alt="logo" src="images/osm_logo.120px.png" width="30" height="30"/>
|
||||
<h1>Nominatim</h1>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="last-updated" class="col-xs-4 text-center">
|
||||
<?php if (isset($sDataDate)){ ?>
|
||||
Data last updated:
|
||||
<br>
|
||||
<?php echo $sDataDate; ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="col-xs-4 text-right">
|
||||
<div class="btn-group">
|
||||
<button class="dropdown-toggle btn btn-sm btn-default" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
About & Help <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a href="https://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Documentation</a></li>
|
||||
<li><a href="https://wiki.openstreetmap.org/wiki/Nominatim/FAQ" target="_blank">FAQ</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="#" class="" data-toggle="modal" data-target="#report-modal">Report problem with results</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="modal fade" id="report-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Report a problem</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/report-errors.php'); ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
<h2>Welcome to Nominatim</h2>
|
||||
|
||||
<p>Nominatim is a search engine for <a href="https://www.openstreetmap.org">OpenStreetMap</a>
|
||||
data. This is the debugging interface. You may search for a name or address (forward search) or
|
||||
look up data by its geographic coordinate (reverse search). Each result comes with a
|
||||
link to a details page where you can inspect what data about the object is saved in
|
||||
the database and investigate how the address of the object has been computed.</p>
|
||||
|
||||
For more information visit the <a href="https://wiki.openstreetmap.org/wiki/Nominatim">Nominatim wiki page</a>.
|
||||
@@ -1,24 +0,0 @@
|
||||
<p>
|
||||
Before reporting problems please read the <a target="_blank" href="https://wiki.openstreetmap.org/wiki/Nominatim">user documentation</a>
|
||||
and
|
||||
<a target="_blank" href="https://wiki.openstreetmap.org/wiki/Nominatim/FAQ">FAQ</a>.
|
||||
|
||||
If your problem relates to the address of a particular search result please use the 'details' link
|
||||
to check how the address was generated before reporting a problem.
|
||||
</p>
|
||||
<p>
|
||||
Use <a target="_blank" href="https://github.com/openstreetmap/nominatim/issues">Nominatim issues on github</a>
|
||||
to report problems.
|
||||
<!-- You can search for existing bug reports
|
||||
<a href="https://trac.openstreetmap.org/query?status=new&status=assigned&status=reopened&component=nominatim&order=priority">here</a>.</p>
|
||||
-->
|
||||
</p>
|
||||
<p>
|
||||
Please ensure that you include a full description of the problem, including the search
|
||||
query that you used, the problem with the result and, if the problem relates to missing data,
|
||||
the osm type (node, way, relation) and id of the item that is missing.
|
||||
</p>
|
||||
<p>
|
||||
Problems that contain enough detail are likely to get looked at before ones that require
|
||||
significant research.
|
||||
</p>
|
||||
@@ -25,10 +25,6 @@ foreach ($aBatchResults as $aSearchResults) {
|
||||
$aPointDetails['aBoundingBox'][2],
|
||||
$aPointDetails['aBoundingBox'][3]
|
||||
);
|
||||
|
||||
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons) {
|
||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aPointDetails['zoom'])) {
|
||||
|
||||
@@ -20,27 +20,14 @@ foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||
|
||||
$aPlace['properties']['geocoding']['label'] = $aPointDetails['langaddress'];
|
||||
|
||||
$aPlace['properties']['geocoding']['name'] = $aPointDetails['placename'];
|
||||
if ($aPointDetails['placename'] !== null) {
|
||||
$aPlace['properties']['geocoding']['name'] = $aPointDetails['placename'];
|
||||
}
|
||||
|
||||
if (isset($aPointDetails['address'])) {
|
||||
$aFieldMappings = array(
|
||||
'house_number' => 'housenumber',
|
||||
'road' => 'street',
|
||||
'locality' => 'locality',
|
||||
'postcode' => 'postcode',
|
||||
'city' => 'city',
|
||||
'district' => 'district',
|
||||
'county' => 'county',
|
||||
'state' => 'state',
|
||||
'country' => 'country'
|
||||
);
|
||||
|
||||
$aAddrNames = $aPointDetails['address']->getAddressNames();
|
||||
foreach ($aFieldMappings as $sFrom => $sTo) {
|
||||
if (isset($aAddrNames[$sFrom])) {
|
||||
$aPlace['properties']['geocoding'][$sTo] = $aAddrNames[$sFrom];
|
||||
}
|
||||
}
|
||||
$aPointDetails['address']->addGeocodeJsonAddressParts(
|
||||
$aPlace['properties']['geocoding']
|
||||
);
|
||||
|
||||
$aPlace['properties']['geocoding']['admin']
|
||||
= $aPointDetails['address']->getAdminLevels();
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
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" />
|
||||
</head>
|
||||
|
||||
<body id="search-page">
|
||||
|
||||
<?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; ?>search.php">
|
||||
<div class="form-group">
|
||||
<input id="q" name="q" type="text" class="form-control input-sm" placeholder="Search" value="<?php echo htmlspecialchars($sQuery); ?>" >
|
||||
</div>
|
||||
<div class="form-group search-button-group">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Search</button>
|
||||
<?php if (CONST_Search_AreaPolygons) { ?>
|
||||
<input type="hidden" value="1" name="polygon_geojson" />
|
||||
<?php } ?>
|
||||
<input type="hidden" name="viewbox" value="<?php if (isset($aMoreParams['viewbox'])) echo ($aMoreParams['viewbox']); ?>" />
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox" id="use_viewbox" <?php if (isset($aMoreParams['viewbox'])) echo "checked='checked'"; ?>>
|
||||
<label for="use_viewbox">apply viewbox</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-type-link">
|
||||
<a id="switch-to-reverse" href="<?php echo CONST_Website_BaseURL; ?>reverse.php?format=html">reverse search</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div id="content">
|
||||
|
||||
<?php if ($sQuery) { ?>
|
||||
|
||||
<div id="searchresults" class="sidebar">
|
||||
<?php
|
||||
$i = 0;
|
||||
foreach($aSearchResults as $iResNum => $aResult)
|
||||
{
|
||||
|
||||
echo '<div class="result" data-position=' . $i . '>';
|
||||
|
||||
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
||||
echo ' <span class="name">'.htmlspecialchars($aResult['name']).'</span>';
|
||||
// echo ' <span class="latlon">'.round($aResult['lat'],3).','.round($aResult['lon'],3).'</span>';
|
||||
// echo ' <span class="place_id">'.$aResult['place_id'].'</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 ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
|
||||
echo '</div>';
|
||||
$i = $i+1;
|
||||
}
|
||||
if (!empty($aSearchResults) && $sMoreURL)
|
||||
{
|
||||
echo '<div class="more"><a class="btn btn-primary" href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<div class="noresults">No search results found</div>';
|
||||
}
|
||||
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<div id="intro" class="sidebar">
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/introduction.php'); ?>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<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 -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
|
||||
$aNominatimMapInit = array(
|
||||
'zoom' => CONST_Default_Zoom,
|
||||
'lat' => CONST_Default_Lat,
|
||||
'lon' => CONST_Default_Lon,
|
||||
'tile_url' => CONST_Map_Tile_URL,
|
||||
'tile_attribution' => CONST_Map_Tile_Attribution
|
||||
);
|
||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
||||
|
||||
echo 'var nominatim_results = ' . json_encode($aSearchResults, JSON_PRETTY_PRINT) . ';';
|
||||
?>
|
||||
</script>
|
||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,10 +15,6 @@ foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||
|
||||
if (isset($aPointDetails['aBoundingBox'])) {
|
||||
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
|
||||
|
||||
if (isset($aPointDetails['aPolyPoints'])) {
|
||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aPointDetails['zoom'])) {
|
||||
|
||||
@@ -11,7 +11,6 @@ echo " timestamp='".date(DATE_RFC822)."'";
|
||||
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
||||
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
|
||||
if (isset($aMoreParams['viewbox'])) echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'";
|
||||
echo " polygon='".(isset($aMoreParams['polygon'])?'true':'false')."'";
|
||||
if (isset($aMoreParams['exclude_place_ids'])) {
|
||||
echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'";
|
||||
}
|
||||
@@ -26,17 +25,12 @@ foreach ($aSearchResults as $iResNum => $aResult) {
|
||||
echo " osm_id='".$aResult['osm_id']."'";
|
||||
}
|
||||
echo " place_rank='".$aResult['rank_search']."'";
|
||||
echo " address_rank='".$aResult['rank_address']."'";
|
||||
|
||||
if (isset($aResult['aBoundingBox'])) {
|
||||
echo ' boundingbox="';
|
||||
echo join(',', $aResult['aBoundingBox']);
|
||||
echo '"';
|
||||
|
||||
if (isset($aResult['aPolyPoints'])) {
|
||||
echo ' polygonpoints=\'';
|
||||
echo json_encode($aResult['aPolyPoints']);
|
||||
echo '\'';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aResult['asgeojson'])) {
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
# just use the pgxs makefile
|
||||
|
||||
foreach(suffix ${PostgreSQL_ADDITIONAL_VERSIONS} "13" "12" "11" "10" "9.6" "9.5" "9.4" "9.3")
|
||||
list(APPEND PG_CONFIG_HINTS
|
||||
"/usr/pgsql-${suffix}/bin")
|
||||
endforeach()
|
||||
|
||||
find_program(PG_CONFIG pg_config HINTS ${PG_CONFIG_HINTS})
|
||||
|
||||
|
||||
|
||||
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()
|
||||
|
||||
ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/dummy
|
||||
COMMAND PGXS=${PGXS} PG_CONFIG=${PG_CONFIG} MODSRCDIR=${CMAKE_CURRENT_SOURCE_DIR} $(MAKE) -f ${CMAKE_CURRENT_SOURCE_DIR}/Makefile
|
||||
COMMENT "Running external makefile ${PGXS}"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
add_executable(nominatim export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c)
|
||||
|
||||
CHECK_SYMBOL_EXISTS(bswap_32 "byteswap.h" HAVE_BYTESWAP)
|
||||
CHECK_SYMBOL_EXISTS(bswap32 "sys/endian.h" HAVE_SYS_ENDIAN)
|
||||
|
||||
target_compile_definitions(nominatim
|
||||
PRIVATE HAVE_BYTESWAP=$<BOOL:${HAVE_BYTESWAP}>
|
||||
PRIVATE HAVE_SYS_ENDIAN=$<BOOL:${HAVE_SYS_ENDIAN}>
|
||||
)
|
||||
|
||||
target_link_libraries(nominatim ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${PostgreSQL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#include "nominatim.h"
|
||||
#include "export.h"
|
||||
#include "postgresql.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
int mode = 0;
|
||||
|
||||
void nominatim_export(int rank_min, int rank_max, const char *conninfo, const char *structuredoutputfile)
|
||||
{
|
||||
xmlTextWriterPtr writer;
|
||||
|
||||
int rankTotalDone;
|
||||
|
||||
PGconn *conn;
|
||||
PGresult * res;
|
||||
PGresult * resSectors;
|
||||
PGresult * resPlaces;
|
||||
|
||||
int rank;
|
||||
int i;
|
||||
int iSector;
|
||||
int tuples;
|
||||
|
||||
const char *paramValues[2];
|
||||
int paramLengths[2];
|
||||
int paramFormats[2];
|
||||
uint32_t paramRank;
|
||||
uint32_t paramSector;
|
||||
uint32_t sector;
|
||||
|
||||
Oid pg_prepare_params[2];
|
||||
|
||||
conn = PQconnectdb(conninfo);
|
||||
if (PQstatus(conn) != CONNECTION_OK)
|
||||
{
|
||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT4;
|
||||
res = PQprepare(conn, "index_sectors",
|
||||
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status = 0 group by geometry_sector order by geometry_sector",
|
||||
1, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) exit(EXIT_FAILURE);
|
||||
PQclear(res);
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT4;
|
||||
pg_prepare_params[1] = PG_OID_INT4;
|
||||
res = PQprepare(conn, "index_sector_places",
|
||||
"select place_id from placex where rank_search = $1 and geometry_sector = $2",
|
||||
2, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) exit(EXIT_FAILURE);
|
||||
PQclear(res);
|
||||
|
||||
nominatim_exportCreatePreparedQueries(conn);
|
||||
|
||||
// Create the output file
|
||||
writer = nominatim_exportXMLStart(structuredoutputfile);
|
||||
|
||||
for (rank = rank_min; rank <= rank_max; rank++)
|
||||
{
|
||||
printf("Starting rank %d\n", rank);
|
||||
|
||||
paramRank = PGint32(rank);
|
||||
paramValues[0] = (char *)¶mRank;
|
||||
paramLengths[0] = sizeof(paramRank);
|
||||
paramFormats[0] = 1;
|
||||
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
|
||||
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(resSectors);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (PQftype(resSectors, 0) != PG_OID_INT4)
|
||||
{
|
||||
fprintf(stderr, "Sector value has unexpected type\n");
|
||||
PQclear(resSectors);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (PQftype(resSectors, 1) != PG_OID_INT8)
|
||||
{
|
||||
fprintf(stderr, "Sector value has unexpected type\n");
|
||||
PQclear(resSectors);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rankTotalDone = 0;
|
||||
for (iSector = 0; iSector < PQntuples(resSectors); iSector++)
|
||||
{
|
||||
sector = PGint32(*((uint32_t *)PQgetvalue(resSectors, iSector, 0)));
|
||||
|
||||
// Get all the place_id's for this sector
|
||||
paramRank = PGint32(rank);
|
||||
paramValues[0] = (char *)¶mRank;
|
||||
paramLengths[0] = sizeof(paramRank);
|
||||
paramFormats[0] = 1;
|
||||
paramSector = PGint32(sector);
|
||||
paramValues[1] = (char *)¶mSector;
|
||||
paramLengths[1] = sizeof(paramSector);
|
||||
paramFormats[1] = 1;
|
||||
resPlaces = PQexecPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
|
||||
if (PQresultStatus(resPlaces) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(resPlaces);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (PQftype(resPlaces, 0) != PG_OID_INT8)
|
||||
{
|
||||
fprintf(stderr, "Place_id value has unexpected type\n");
|
||||
PQclear(resPlaces);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
tuples = PQntuples(resPlaces);
|
||||
for (i = 0; i < tuples; i++)
|
||||
{
|
||||
nominatim_exportPlace(PGint64(*((uint64_t *)PQgetvalue(resPlaces, i, 0))), conn, writer, NULL, NULL);
|
||||
rankTotalDone++;
|
||||
if (rankTotalDone%1000 == 0) printf("Done %i (k)\n", rankTotalDone/1000);
|
||||
}
|
||||
PQclear(resPlaces);
|
||||
}
|
||||
PQclear(resSectors);
|
||||
}
|
||||
|
||||
nominatim_exportXMLEnd(writer);
|
||||
|
||||
PQfinish(conn);
|
||||
}
|
||||
|
||||
void nominatim_exportCreatePreparedQueries(PGconn * conn)
|
||||
{
|
||||
Oid pg_prepare_params[2];
|
||||
PGresult * res;
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT8;
|
||||
res = PQprepare(conn, "placex_details",
|
||||
"select placex.osm_type, placex.osm_id, placex.class, placex.type, placex.name, placex.housenumber, placex.country_code, ST_AsText(placex.geometry), placex.admin_level, placex.rank_address, placex.rank_search, placex.parent_place_id, parent.osm_type, parent.osm_id, placex.indexed_status, placex.linked_place_id from placex left outer join placex as parent on (placex.parent_place_id = parent.place_id) where placex.place_id = $1",
|
||||
1, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
fprintf(stderr, "Error preparing placex_details: %s", PQerrorMessage(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT8;
|
||||
res = PQprepare(conn, "placex_address",
|
||||
"select osm_type,osm_id,class,type,distance,cached_rank_address,isaddress from place_addressline join placex on (address_place_id = placex.place_id) where place_addressline.place_id = $1 and address_place_id != place_addressline.place_id order by cached_rank_address asc,osm_type,osm_id",
|
||||
1, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
fprintf(stderr, "Error preparing placex_address: %s", PQerrorMessage(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT8;
|
||||
res = PQprepare(conn, "placex_names",
|
||||
"select (each(name)).key,(each(name)).value from (select name from placex where place_id = $1) as x order by (each(name)).key",
|
||||
1, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
fprintf(stderr, "Error preparing placex_names: %s", PQerrorMessage(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
pg_prepare_params[0] = PG_OID_INT8;
|
||||
res = PQprepare(conn, "placex_extratags",
|
||||
"select (each(extratags)).key,(each(extratags)).value from (select extratags from placex where place_id = $1) as x order by (each(extratags)).key",
|
||||
1, pg_prepare_params);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
fprintf(stderr, "Error preparing placex_extratags: %s", PQerrorMessage(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
xmlTextWriterPtr nominatim_exportXMLStart(const char *structuredoutputfile)
|
||||
{
|
||||
xmlTextWriterPtr writer;
|
||||
|
||||
writer = xmlNewTextWriterFilename(structuredoutputfile, 0);
|
||||
if (writer==NULL)
|
||||
{
|
||||
fprintf(stderr, "Unable to open %s\n", structuredoutputfile);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
xmlTextWriterSetIndent(writer, 1);
|
||||
if (xmlTextWriterStartDocument(writer, NULL, "UTF8", NULL) < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterStartDocument failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (xmlTextWriterStartElement(writer, BAD_CAST "osmStructured") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST "0.1") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterWriteAttribute failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (xmlTextWriterWriteAttribute(writer, BAD_CAST "generator", BAD_CAST "Nominatim") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterWriteAttribute failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
mode = 0;
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
void nominatim_exportXMLEnd(xmlTextWriterPtr writer)
|
||||
{
|
||||
nominatim_exportEndMode(writer);
|
||||
|
||||
// End <osmStructured>
|
||||
if (xmlTextWriterEndElement(writer) < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterEndElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (xmlTextWriterEndDocument(writer) < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterEndDocument failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
xmlFreeTextWriter(writer);
|
||||
}
|
||||
|
||||
void nominatim_exportStartMode(xmlTextWriterPtr writer, int newMode)
|
||||
{
|
||||
if (mode == newMode) return;
|
||||
|
||||
nominatim_exportEndMode(writer);
|
||||
|
||||
switch(newMode)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (xmlTextWriterStartElement(writer, BAD_CAST "add") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (xmlTextWriterStartElement(writer, BAD_CAST "update") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (xmlTextWriterStartElement(writer, BAD_CAST "delete") < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterStartElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
void nominatim_exportEndMode(xmlTextWriterPtr writer)
|
||||
{
|
||||
if (!mode) return;
|
||||
|
||||
if (xmlTextWriterEndElement(writer) < 0)
|
||||
{
|
||||
fprintf(stderr, "xmlTextWriterEndElement failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void nominatim_exportPlaceQueries(uint64_t place_id, PGconn * conn, struct export_data * querySet)
|
||||
{
|
||||
const char * paramValues[1];
|
||||
int paramLengths[1];
|
||||
int paramFormats[1];
|
||||
uint64_t paramPlaceID;
|
||||
|
||||
paramPlaceID = PGint64(place_id);
|
||||
paramValues[0] = (char *)¶mPlaceID;
|
||||
paramLengths[0] = sizeof(paramPlaceID);
|
||||
paramFormats[0] = 1;
|
||||
|
||||
querySet->res = PQexecPrepared(conn, "placex_details", 1, paramValues, paramLengths, paramFormats, 0);
|
||||
if (PQresultStatus(querySet->res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "placex_details: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(querySet->res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
querySet->resNames = PQexecPrepared(conn, "placex_names", 1, paramValues, paramLengths, paramFormats, 0);
|
||||
if (PQresultStatus(querySet->resNames) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "placex_names: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(querySet->resNames);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
querySet->resAddress = PQexecPrepared(conn, "placex_address", 1, paramValues, paramLengths, paramFormats, 0);
|
||||
if (PQresultStatus(querySet->resAddress) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "placex_address: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(querySet->resAddress);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
querySet->resExtraTags = PQexecPrepared(conn, "placex_extratags", 1, paramValues, paramLengths, paramFormats, 0);
|
||||
if (PQresultStatus(querySet->resExtraTags) != PGRES_TUPLES_OK)
|
||||
{
|
||||
fprintf(stderr, "placex_extratags: SELECT failed: %s", PQerrorMessage(conn));
|
||||
PQclear(querySet->resExtraTags);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void nominatim_exportFreeQueries(struct export_data * querySet)
|
||||
{
|
||||
PQclear(querySet->res);
|
||||
PQclear(querySet->resNames);
|
||||
PQclear(querySet->resAddress);
|
||||
PQclear(querySet->resExtraTags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Requirements: the prepared queries must exist
|
||||
*/
|
||||
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
|
||||
xmlTextWriterPtr writer, pthread_mutex_t * writer_mutex, struct export_data * prevQuerySet)
|
||||
{
|
||||
struct export_data querySet;
|
||||
|
||||
int i;
|
||||
|
||||
nominatim_exportPlaceQueries(place_id, conn, &querySet);
|
||||
|
||||
// Add, modify or delete?
|
||||
if (prevQuerySet)
|
||||
{
|
||||
if ((PQgetvalue(prevQuerySet->res, 0, 14) && strcmp(PQgetvalue(prevQuerySet->res, 0, 14), "100") == 0) || PQntuples(querySet.res) == 0)
|
||||
{
|
||||
// Delete
|
||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
||||
nominatim_exportStartMode(writer, 3);
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "feature");
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "place_id", "%li", place_id);
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 0));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 1));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 2));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(prevQuerySet->res, 0, 3));
|
||||
xmlTextWriterEndElement(writer);
|
||||
if (writer_mutex) pthread_mutex_unlock( writer_mutex );
|
||||
nominatim_exportFreeQueries(&querySet);
|
||||
return;
|
||||
}
|
||||
if (PQgetvalue(prevQuerySet->res, 0, 14) && strcmp(PQgetvalue(prevQuerySet->res, 0, 14), "1") == 0)
|
||||
{
|
||||
// Add
|
||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
||||
nominatim_exportStartMode(writer, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update, but only if something has changed
|
||||
|
||||
// TODO: detect changes
|
||||
|
||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
||||
nominatim_exportStartMode(writer, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add
|
||||
if (writer_mutex) pthread_mutex_lock( writer_mutex );
|
||||
nominatim_exportStartMode(writer, 1);
|
||||
}
|
||||
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "feature");
|
||||
xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "place_id", "%li", place_id);
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.res, 0, 0));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(querySet.res, 0, 1));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(querySet.res, 0, 2));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(querySet.res, 0, 3));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "rank", BAD_CAST PQgetvalue(querySet.res, 0, 9));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "importance", BAD_CAST PQgetvalue(querySet.res, 0, 10));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 11));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_type", BAD_CAST PQgetvalue(querySet.res, 0, 12));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_id", BAD_CAST PQgetvalue(querySet.res, 0, 13));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "linked_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 15));
|
||||
|
||||
if (PQntuples(querySet.resNames))
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "names");
|
||||
|
||||
for (i = 0; i < PQntuples(querySet.resNames); i++)
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "name");
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resNames, i, 0));
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.resNames, i, 1));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
if (PQgetvalue(querySet.res, 0, 5) && strlen(PQgetvalue(querySet.res, 0, 5)))
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "houseNumber");
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 5));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
if (PQgetvalue(querySet.res, 0, 8) && strlen(PQgetvalue(querySet.res, 0, 8)))
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "adminLevel");
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 8));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
if (PQgetvalue(querySet.res, 0, 6) && strlen(PQgetvalue(querySet.res, 0, 6)))
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "countryCode");
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 6));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
if (PQntuples(querySet.resAddress) > 0)
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "address");
|
||||
for (i = 0; i < PQntuples(querySet.resAddress); i++)
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST getRankLabel(atoi(PQgetvalue(querySet.resAddress, i, 5))));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "rank", BAD_CAST PQgetvalue(querySet.resAddress, i, 5));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resAddress, i, 0));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "id", BAD_CAST PQgetvalue(querySet.resAddress, i, 1));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "key", BAD_CAST PQgetvalue(querySet.resAddress, i, 2));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "value", BAD_CAST PQgetvalue(querySet.resAddress, i, 3));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "distance", BAD_CAST PQgetvalue(querySet.resAddress, i, 4));
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "isaddress", BAD_CAST PQgetvalue(querySet.resAddress, i, 6));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
if (PQntuples(querySet.resExtraTags))
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "tags");
|
||||
|
||||
for (i = 0; i < PQntuples(querySet.resExtraTags); i++)
|
||||
{
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "tag");
|
||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST PQgetvalue(querySet.resExtraTags, i, 0));
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.resExtraTags, i, 1));
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
xmlTextWriterEndElement(writer);
|
||||
}
|
||||
|
||||
|
||||
xmlTextWriterStartElement(writer, BAD_CAST "osmGeometry");
|
||||
xmlTextWriterWriteString(writer, BAD_CAST PQgetvalue(querySet.res, 0, 7));
|
||||
xmlTextWriterEndElement(writer);
|
||||
|
||||
xmlTextWriterEndElement(writer); // </feature>
|
||||
|
||||
if (writer_mutex) pthread_mutex_unlock( writer_mutex );
|
||||
|
||||
nominatim_exportFreeQueries(&querySet);
|
||||
}
|
||||
|
||||
const char * getRankLabel(int rank)
|
||||
{
|
||||
switch (rank)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
return "continent";
|
||||
case 2:
|
||||
case 3:
|
||||
return "sea";
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return "country";
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
return "state";
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
return "county";
|
||||
case 16:
|
||||
return "city";
|
||||
case 17:
|
||||
return "town";
|
||||
case 18:
|
||||
return "village";
|
||||
case 19:
|
||||
return "unknown";
|
||||
case 20:
|
||||
return "suburb";
|
||||
case 21:
|
||||
return "postcode";
|
||||
case 22:
|
||||
return "neighborhood";
|
||||
case 23:
|
||||
return "postcode";
|
||||
case 24:
|
||||
return "unknown";
|
||||
case 25:
|
||||
return "postcode";
|
||||
case 26:
|
||||
return "street";
|
||||
case 27:
|
||||
return "access";
|
||||
case 28:
|
||||
return "building";
|
||||
case 29:
|
||||
default:
|
||||
return "other";
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef EXPORT_H
|
||||
#define EXPORT_H
|
||||
|
||||
#include <libxml/encoding.h>
|
||||
#include <libxml/xmlwriter.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct export_data
|
||||
{
|
||||
PGresult * res;
|
||||
PGresult * resNames;
|
||||
PGresult * resAddress;
|
||||
PGresult * resExtraTags;
|
||||
};
|
||||
|
||||
void nominatim_export(int rank_min, int rank_max, const char *conninfo, const char *structuredoutputfile);
|
||||
void nominatim_exportCreatePreparedQueries(PGconn * conn);
|
||||
|
||||
xmlTextWriterPtr nominatim_exportXMLStart(const char *structuredoutputfile);
|
||||
void nominatim_exportXMLEnd(xmlTextWriterPtr writer);
|
||||
|
||||
void nominatim_exportEndMode(xmlTextWriterPtr writer);
|
||||
|
||||
void nominatim_exportPlaceQueries(uint64_t place_id, PGconn * conn, struct export_data * querySet);
|
||||
void nominatim_exportFreeQueries(struct export_data * querySet);
|
||||
|
||||
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
|
||||
xmlTextWriterPtr writer, pthread_mutex_t * writer_mutex, struct export_data * prevQuerySet);
|
||||
const char * getRankLabel(int rank);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user